我正在使用 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)
怎么运行的?
- 设置 dbus 的主循环
- 请求会话总线名称,以便其他应用程序(在我们的例子中是同一应用程序的另一个实例)可以连接到它
- 如果总线名称不存在(因此我们是主要所有者),则在路径 / 处创建一个新实例。如果存在,则从 dbus 获取对象并使用已知接口调用远程应用程序对象上的方法。
- 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 已经提供的功能的模拟非常差。