好的人们,有一个好的、好的和正确的解决方案,没有任何麻烦。
首先,我想解释一下为什么在调用 wx.Frame.Show(MyFrame, False) 时,Windows GUI 进程会进入后台。
非常简短的解释和略过细节是 Windows 将 Window 和应用程序视为同一事物。
即MS Windows 应用程序的主要元素是您的主 GUI 窗口。
因此,当隐藏此窗口时,应用程序将不再有 GUI,并继续在后台运行。
Mac OS X 将应用程序视为您的应用程序,您选择放入其中的任何窗口都是它的子级。
这允许您在运行应用程序时不显示窗口,只显示一个菜单栏,您可以从中选择一个操作,然后生成所需的窗口。
对于编辑器来说非常方便,您可能一次打开多个文件,每个文件都在自己的窗口中,当您关闭最后一个文件时,您仍然可以打开一个新文件或创建一个空白文件等。
因此,Mac OS X 应用程序的主要元素是应用程序本身,这就是逻辑上在最后一个窗口隐藏后它保持打开状态的原因。破坏它的菜单栏也无济于事。应用程序的名称将保留在 Dock 和应用程序切换器以及 Force Quit 中。您将能够切换到它并且什么都不做。 :D
但是,幸运的是,Mac 为我们提供了将其置于后台的功能。而这个函数在 NSApp 对象中已经提到了 setApplicationActivationPolicy()。
问题在于它在 Python 的 AppKit 中的命名,即 NSApp.setActivationPolicy_()。更复杂的是,它不能直接从 Python 的交互式 shell 中使用,但必须至少从导入的模块中调用。
为什么?我不知道。无论如何,这里是一个完整的例子,用于将一个应用程序放到后台,可以在 Mac 和 Windows 上运行。
我没有在 Linux 上尝试过,它结合了 Mac 和 Windows 在呈现应用方面的行为,因此,仅隐藏一个窗口是否足够还有待观察。
请随时尝试提交修改以使示例更具跨平台性。
例子:
"""
This app will show you small window with the randomly generated code that will confirm that reopened window is still the same app returned from background,
and the button allowing you to send it to background.
After you send it to background, wait 8 seconds and application will return to foreground again.
Too prove that the application is continuing its work in the background, the app will call wx.Bell() every second.
You should hear the sound while app is in the foreground and when it is in background too.
Merry Christmas and a happy New Year!
"""
import wx
import random, sys
if sys.platform=="darwin":
from AppKit import NSBundle, NSApp, NSAutoreleasePool, NSApplicationActivationPolicyRegular, NSApplicationActivationPolicyProhibited
# Use Info.plist values to know whether our process started as daemon
# Also, change this dict in case anyone is later checking it (e.g. some module)
# Note: Changing this dict doesn't change Info.plist file
info = NSBundle.mainBundle().infoDictionary()
def SendToBackground ():
# Change info, just in case someone checks it later
info["LSUIElement"] = "1"
NSApp.setActivationPolicy_(NSApplicationActivationPolicyProhibited)
def ReturnToForeground ():
# Change info, just in case someone checks it later
info["LSUIElement"] = "0"
NSApp.setActivationPolicy_(NSApplicationActivationPolicyRegular)
else:
# Simulate Mac OS X App - Info.plist
info = {"LSUIElement": "0"} # Assume non background at startup
# If programmer chose not to display GUI at startup then she/he should change this before calling ReturnToForeground()
# To preserve consistency and allow correct IsDaemon() answer
def SendToBackground ():
info["LSUIElement"] = "1"
def ReturnToForeground ():
info["LSUIElement"] = "0"
def IsDaemon ():
return info["LSUIElement"]=="1"
class Interface (wx.Frame):
def __init__ (self):
wx.Frame.__init__(self, None, -1, "Test", pos=(100, 100), size=(100, 100))
wx.StaticText(self, -1, "Test code: "+str(random.randint(1000, 10000)), pos=(10, 10), size=(80, 20))
b = wx.Button(self, -1, "DAEMONIZE ME", size=(80, 20), pos=(10, 50))
wx.EVT_BUTTON(self, b.GetId(), self.OnDaemonize)
self.belltimer = wx.Timer(self)
wx.EVT_TIMER(self, self.belltimer.GetId(), self.OnBellTimer)
self.belltimer.Start(1000)
# On Mac OS X, you wouldn't be able to quit the app without the menu bar:
if sys.platform=="darwin":
self.SetMenuBar(wx.MenuBar())
self.Show()
def OnBellTimer (self, e):
wx.Bell()
def OnDaemonize (self, e):
self.Show(False)
SendToBackground()
self.timer = wx.Timer(self)
wx.EVT_TIMER(self, self.timer.GetId(), self.OnExorcize)
self.timer.Start(8000)
def OnExorcize (self, e):
self.timer.Stop()
ReturnToForeground()
self.Show()
self.Raise()
app = wx.App()
i = Interface()
app.MainLoop()
当然,这个例子可以从终端启动,也可以用 CLI 窗口启动。在这种情况下,您的程序的终端控件将保持打开状态,而仅应用程序会出现和消失。
要完成您的 GUI 守护程序,您应该使用 pythonw 启动它(在 Windows 上)或从 daemontest.pyw 文件启动它,
在 Mac 上你应该使用:
% nohup python daemontest.py &
或与py2app捆绑或使用python.org Python版本附带的Python启动器在没有终端的情况下启动daemontest.py。
注意:此示例在 Mac OS X 上存在与我在问题中提供的链接中提到的相同的缺陷。我指的是当应用程序来自后台时,焦点错误和菜单栏没有立即出现的问题。用户必须切换并返回到新返回的应用程序才能正常工作。我也希望有人能解决这个问题。等等。很烦人。
另一个注意事项:如果您的程序中有线程正在运行,请在守护进程和驱除线程时暂停它们。尤其是当他们使用 Apple 事件与另一个应用程序通信时。坦率地说,关于 wx.Timers 的一些事情也应该做。如果您不小心,您可能会在程序终止时遇到不存在的 NSAutoreleasePool 和/或 SegmentationFault 的泄漏问题。