Python如何在macosx上用GUI应用程序生成守护进程?

2024-09-25 02:34:23 发布

您现在位置:Python中文网/ 问答频道 /正文

在Windows上很容易。只要用pythonw而不是python运行程序,代码就会在后台执行。在

所以,我想实现的事情很容易安排好。在

我有一个应用程序,这是一个真正的服务做地下工作。但这项服务需要一个控制面板。在

所以,在Windows上,我使用wxPython来创建一个GUI,甚至一些wx工具来提供所需的服务,当用户完成调整后,她/他单击Hide and Show(False)将在主窗口上被调用。在

因此,GUI消失了,服务在后台继续工作。用户可以随时使用热键返回。在

问题在于,在macosx上,这种策略只能在一定程度上起作用。在

什么时候wx.Frame.Show公司调用(False)时,窗口将随菜单栏一起消失,服务工作正常,但应用程序仍然可见。在

你可以切换到它,不管你不能用它做任何事情。它仍然存在于码头等

当程序使用python或pythonw或它与Py2App绑定时,就会发生这种情况。在

不管我做什么,图标都会留在那里。在

必须有一些技巧,让程序员删除这个顽皮的图标,从而停止打扰可怜的小用户时,她/他不想被打扰。在

隐藏窗口显然是不够的。有人知道窍门吗?在

注:我真的很想按照我上面描述的方式来做,而不是弄乱两个独立的进程和IPC。在

编辑:

我仔细挖掘后发现:

How to hide application icon from Mac OS X dock

http://codesorcery.net/2008/02/06/feature-requests-versus-the-right-way-to-do-it

How to hide the Dock icon

根据最后一个链接,正确的方法是使用:

[NSApp setActivationPolicy: NSApplicationActivationPolicyAccessory];

或者

^{pr2}$

所以我想要的(运行时从后台切换到前台和后台)是可能的。在

但是如何从Python中实现呢???在

常量:NSApplicationActivationPolicyProhibited和NSApplicationActivationPolicyAccessory存在于AppKit中,但我在任何地方都找不到setApplicationActivationPolicy函数。在

NSApp()没有。在

我知道有一种方法可以用ctypes加载objc dylib,委托给NSApp并发送“setApplicationActivationPolicy:<;constant\u value>;”,但我不知道这会给您带来多大的麻烦wx.应用程序(). 对于已经可以使用的东西来说,这是一个有点费劲的工作。在

以我的经验,NSApp()和wx.应用程序()同时活跃,彼此不喜欢。在

也许我们可以得到wx正在使用的NSApp()实例并使用wx的委托???在

请记住,已经建议的解决方案,从代理开始,切换到前台,或者运行多个进程,并执行IPC,这在我的情况下是非常不可取的。在

所以,理想情况下,使用setApplicationActivationPolicy是我的目标,但是如何使用呢?(简单易用,不乱七八糟wx.应用程序()请。)

有什么想法吗???在


Tags: to用户程序false应用程序windowsshow情况
2条回答

好吧,各位,有一个好的,好的和正确的解决方案,没有任何混乱。在

首先,我想解释为什么Windows GUI进程在wx.Frame.Show公司(MyFrame,False)被调用。在

非常简短的解释和略过细节的是,Windows认为窗口和应用程序是一样的。在

也就是说,MS Windows应用程序的主要元素是主GUI窗口。在

所以,当这个窗口被隐藏时,应用程序就不再有GUI,而是继续在后台运行。在

macosx将应用程序视为您的应用程序,可以说,您选择放入其中的任何窗口都是它的子级。在

这允许您在运行应用程序时不显示窗口,而只显示一个菜单栏,您可以从中选择一个操作,然后生成所需的窗口。在

非常方便的编辑器,你可能有多个文件打开一次,每一个在自己的窗口,当你关闭最后一个,你仍然可以打开一个新的或创建一个空白的,等等

因此,macosx应用程序的一个主要元素就是应用程序本身,这就是为什么它在最后一个窗口被隐藏之后仍然保持打开状态。破坏菜单栏也无济于事。应用程序的名称将保留在Dock和应用程序切换器中,并有效退出。你可以切换到它而什么也不做。:天 但是,幸运的是,Mac提供了一个将其放到后台的功能。这个函数已经在NSApp对象中提到过setApplicationActivationPolicy()。在

问题在于它在Python的AppKit中的命名NSApp.setActivationPolicy_(). 更复杂的是,它不能直接从Python的交互式shell获得,但至少必须从导入的模块调用它。在

为什么?我不知道。不管怎样,这里有一个完整的例子来说明如何把一个可以在Mac和Windows上运行的应用程序放到后台。在

我没有在Linux上尝试过,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上)启动它,或者从戴蒙特斯皮尤文件

在Mac上,您应该使用:

^{pr2}$

或者将其与py2app捆绑在一起,或者使用附带的Python启动器python.org网站要启动的Python版本daemontest.py公司没有终端。在

注意:这个例子在macosx上有同样的缺陷,我在问题中提供的链接中也提到了这个缺陷。我指的是当应用程序来自后台时,错误的对焦和菜单栏不立即出现的问题。用户必须切换并返回到新返回的应用程序才能正常工作。我希望有人也能解决这个问题。很快。这很烦人。在

还有一个注意事项:如果您的程序中有线程在运行,请在守护进程和排除线程时暂停它们。尤其是如果他们正在使用苹果事件与另一个应用程序通信。坦白说,关于wx计时器也应该这样做。如果您不小心,可能会在程序终止时在不存在的NSAutoreleasePool和/或SegmentationFault周围出现泄漏问题。在

好吧。下面是执行您想执行的操作的代码:

import AppKit
info = AppKit.NSBundle.mainBundle().infoDictionary()
info["LSUIElement"] = "1"

这是你不想做的更混乱的答案,但我还是会列出它。在info.plist文件中,添加以下密钥:

^{pr2}$

另一个更像守护程序的解决方案,但意味着它不能有GUI,您可以将此密钥添加到info.plist文件:

<key>LSBackgroundOnly</key>
<string>1</string>

Source

相关问题 更多 >