如何强制仅运行一个应用程序实例?

如何强制仅运行一个应用程序实例?

我正在使用 quickstart 开发一个程序。我正在使用一个可以隐藏/显示主窗口的指示器。

但是当我从启动器启动应用程序时,它会启动该应用程序的一个新实例(因此将显示一个附加指示器)。而不是仅仅显示上一个正在运行的应用程序的主窗口。

强制仅使用一个指标的通常方法是什么?

答案1

首先,确保你有一个好的理由来做这件事。人们希望应用程序能够像你在帖子中描述的那样工作。即使你个人可能不喜欢它。

但是,要回答你的问题,最简单的方法之一是在启动时创建一个文件(例如在配置目录中),然后在应用程序退出时删除该文件。在启动应用程序之前,检查代码中是否有另一个实例处于活动状态,如果是,则直接退出。如果你还想关注活动应用程序,则需要一种进行进程间通信的方法。例如通过套接字。请参阅http://docs.python.org/library/ipc.html了解更多信息。

答案2

我找到了一个更好的解决方案(虽然稍微复杂一些)。您不仅可以验证您的进程是否正在运行,还可以在进程已启动时为其提供一些输入。请查看此解决方案卢卡布鲁诺

我正在尝试 自由言论最近我需要在每个会话中运行一次该应用程序,因为它有一个托盘图标和一个笔记本(也许带有小程序的窗口更好?)我决定使用数据总线使应用程序仅运行单个实例;当您尝试再次打开它时,它不会启动另一个进程,而是使用已经运行的进程。

以下是我使用 dbus-python 创建通用单个应用程序实例的方法:

import dbus
import dbus.bus
import dbus.service
import dbus.mainloop.glib
import gobject

class Application (dbus.service.Object):
  def __init__ (self, bus, path, name):
    dbus.service.Object.__init__ (self, bus, path, name)
    self.loop = gobject.MainLoop ()

 @dbus.service.method ("org.domain.YourApplication",
                       in_signature='a{sv}', out_signature='')
 def start (self, options={}):
   if self.loop.is_running ():
     print 'instance already running'
   else:
     self.loop.run ()

dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
bus = dbus.SessionBus ()
request = bus.request_name ("org.domain.YourApplication", dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
 app = Application (bus, '/', "org.domain.YourApplication")
else:
 object = bus.get_object ("org.domain.YourApplication", "/")
 app = dbus.Interface (object, "org.domain.YourApplication")

# Get your options from the command line, e.g. with OptionParser
options = {'option1': 'value1'}
app.start (options)

怎么运行的?

  1. 设置 dbus 的主循环
  2. 请求会话总线名称,以便其他应用程序(在我们的例子中是同一应用程序的另一个实例)可以连接到它
  3. 如果总线名称不存在(因此我们是主要所有者),则在路径 / 处创建一个新实例。如果存在,则从 dbus 获取对象并使用已知接口调用远程应用程序对象上的方法。
  4. Application.start 方法检查它是否已经在运行,然后决定在两种情况下要做什么。

用这种方式创建 GTK+ 应用程序非常简单。它只需要使用 GTK+ 主循环。假设我们想在用户尝试打开应用程序的另一个实例时 present() GtkWindow:

import dbus
import dbus.bus
import dbus.service
import dbus.mainloop.glib
import gobject
import gtk
import gtk.gdk
import time

class Application (dbus.service.Object):
 def __init__ (self, bus, path, name):
   dbus.service.Object.__init__ (self, bus, path, name)
   self.running = False
   self.main_window = gtk.Window ()
   self.main_window.show_all ()

 @dbus.service.method ("org.domain.YourApplication",
                       in_signature='', out_signature='b')
 def is_running (self):
   return self.running

 @dbus.service.method ("org.domain.YourApplication",
                       in_signature='a{sv}i', out_signature='')
 def start (self, options, timestamp):
   if self.is_running ():
     self.main_window.present_with_time (timestamp)
   else:
     self.running = True
     gtk.main ()
     self.running = False

dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
bus = dbus.SessionBus ()
request = bus.request_name ("org.domain.YourApplication", dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
 app = Application (bus, '/', "org.domain.YourApplication")
else:
 object = bus.get_object ("org.domain.YourApplication", "/")
 app = dbus.Interface (object, "org.domain.YourApplication")

# Get your options from the command line, e.g. with OptionParser
options = {'option1': 'value1'}
app.start (options, int (time.time ()))
if app.is_running ():
 gtk.gdk.notify_startup_complete ()

怎么运行的?

假设我们第一次运行该应用程序,循环开始,当它结束时跑步设置为 False,因此不会调用 gtk.gdk.notify_startup_complete()。相反,如果应用程序已在运行,则会在运行循环的远程对象上调用 start()。然后该方法立即返回,因此将调用 gtk.gdk.notify_startup_complete()。如果您不通知启动器启动已完成...猜猜您的鼠标和任务栏面板会发生什么...

如果循环正在运行,窗口会稍微延迟显示给用户。如果不使用任何时间戳,UI 交互将不会让窗口有时间显示。

当然,您可以将 DBus 用于更多用途,例如从命令行设置选项(如上面的代码中所述),并让其他应用程序与您的应用程序通信,反之亦然。如今几乎所有系统都使用 DBus,因此添加这种依赖关系并不麻烦。在我看来,使用锁定文件、FIFO、unix 套接字或其他任何东西会更加麻烦。FreeSpeak 使用了这些旧技术,它对 DBus 已经提供的功能的模拟非常差。

相关内容