Python-如何在Mac OS X上的GUI应用程序中创建守护程序?

秋季

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

因此,我希望实现的目标很容易安排。

我有一个应用程序,它实际上是一项在做地下工作的服务。但是此服务需要一个控制面板。

因此,在Windows上,我使用wxPython创建GUI,甚至使用一些wx东西来提供所需的服务,并且当用户完成调整后,她/他单击主窗口上的Hide and Show(False)。

因此,GUI消失了,该服务在后台继续其工作。用户始终可以使用热键将其恢复。

问题在于,在Mac OS X上,这种策略只能在某种程度上起作用。

调用wx.Frame.Show(False)时,该窗口及其菜单栏消失,并且服务正常,但是该应用程序仍在其中可见。

无论您无法对其执行任何操作,都可以切换到它。它仍然存在于Dock等中。

当程序使用python或pythonw或与Py2App捆绑在一起时,会发生这种情况。

无论我做什么,图标都停留在该位置。

必须有一些技巧可以使程序员删除该顽皮的图标,从而在不想打扰可怜的小用户时不再打扰他/他。

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

NB:我真的很想像上面描述的那样去做,不要把两个独立的过程和IPC搞混了。

编辑:

经过大量的挖掘,我发现了以下这些:

如何从Mac OS X扩展坞隐藏应用程序图标

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

如何隐藏Dock图标

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

[NSApp setActivationPolicy: NSApplicationActivationPolicyAccessory];

要么

[NSApp setActivationPolicy: NSApplicationActivationPolicyProhibited];

因此,我想要什么(运行时从后台切换到前台,然后再切换回)是可能的。

但是如何从Python做到这一点?

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

NSApp()没有。

我知道有一种方法可以通过使用ctypes加载objc dylib,委托给NSApp并发送“ setApplicationActivationPolicy:<constant_value>”来实现,但是我不知道这与wx.App()有多大关系。对于应该已经可用的东西来说,这要花很多工夫。

以我的经验,同时激活的NSApp()和wx.App()彼此非常讨厌。

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

请记住,在我看来,已经建议的解决方案(以代理开始,切换到前台或运行多个进程并执行IPC)是非常不可取的。

因此,理想情况下,使用setApplicationActivationPolicy是我的目标,但是如何?(简单易用,请不要混淆wx.App()。)

有任何想法吗???

秋季

好的,您可以找到一个很好的,不错的,正确的解决方案,而不会产生任何混乱。

首先,我想解释一下为什么调用wx.Frame.Show(MyFrame,False)时Windows GUI进程会进入后台。

非常简短的解释和跳过的细节是Windows将Window和应用程序视为同一事物。

即,MS Windows应用程序的主要元素是您的主GUI窗口。

因此,当该窗口隐藏时,应用程序将不再具有GUI并继续在后台运行。

Mac OS X将该应用程序视为您的应用程序,可以这么说,您选择放入其中的任何窗口都是其子级。

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

对于编辑者非常方便,您可以一次在一个窗口中打开多个文件,每个文件都在其自己的窗口中,当您关闭最后一个文件时,您仍然可以打开一个新文件或创建一个空白文件,依此类推。

因此,Mac OS X应用程序的主要元素是应用程序本身,这就是为什么在逻辑上隐藏了最后一个窗口之后,它仍保持打开状态的原因。销毁其菜单栏也无济于事。该应用程序的名称将始终存在于Dock,应用程序切换器和“强制退出”中。您将能够切换到该功能,而无需执行任何操作。:D但是,幸运的是,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上)启动它,或从daemontest.pyw文件启动它,

在Mac上,您应该使用:

% nohup python daemontest.py &

或将其与py2app捆绑在一起,或使用python.org随附的Python启动器Python版本启动daemontest.py而无需终端。

注意:该示例在Mac OS X上也存在与我在问题中提供的链接中提到的相同的缺陷。我指的是聚焦不正确的问题,并且当应用来自背景时菜单栏不会立即出现。用户必须切换并返回新返回的应用程序才能正常工作。我希望有人也能解决这个问题。等等。这很烦人。

还有一点要注意:如果您的程序中正在运行线程,请在守护进程和驱魔时暂停它们。尤其是当他们使用Apple事件与另一个应用通信时。坦率地说,关于wx.Timers的事情也应该做。如果不小心,程序终止时可能会遇到不存在的NSAutoreleasePool和/或SegmentationFault泄漏的问题。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在Mac OS X上的Pure C中创建窗口应用程序

如何在不打开Mac OS X中任何终端窗口的情况下运行Python 3 tkinter应用程序?

如何在Mac OS X Sierra上删除多余的python

如何在Mac OS X 10.6.4上卸载Python 2.7?

如何在Mac OS X上正确更新Python

如何在Mac OS X中修复System Python的权限?

如何在Mac OS X中最大化整个屏幕上的应用程序

如何在Mac OS X上找到正在运行的应用程序的路径

如何在Mac OS X标题栏中更改java swing应用程序标题?

如何在Linux中对Mac OS X应用程序进行签名?

如何在MAC OS X应用程序中更改NSTableView标头背景颜色?

如何在Mac OS X中的Eclipse下停止Android应用程序?

如何在Mac OS X中更改应用程序图标?

如何在wxPython应用程序中更改Mac OS X停靠图标

如何在 Mac OS 上运行 python-flask 容器?

如何在Mac OS上安装python-evdev?

如何在Python中创建守护程序?

如何在Mac OS应用程序中使用canOpenURL?

如何在OS X中关闭盖上的应用程序

如何使用具有PySide2 GUI的Python脚本构建Mac OS应用程序?

如何在Mac App Store上的os x应用程序中捆绑命令行实用程序(使用沙盒授权)

如何在Windows上使用Python创建OS X应用

如何在Python中创建基于HTML的布局GUI应用程序?

在Mac OS X上管理应用程序

摆脱Mac OS X上的全屏应用程序

在Linux上运行Mac OS X应用程序

如何在Mac OS上执行.net核心控制台应用程序

.net 核心 Web 应用程序 docker 映像如何在 Mac OS 上运行?

如何在Mac OS X中将Java应用程序与本地数据库接口