gpt4 book ai didi

Python - 如何在 Mac OS X 上使用 GUI 应用程序制作守护程序?

转载 作者:行者123 更新时间:2023-12-03 17:05:54 50 4
gpt4 key购买 nike

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

因此,我希望实现的事情很容易安排。

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

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

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

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

当 wx.Frame.Show(False) 被调用时,窗口连同它的菜单栏一起消失并且服务工作正常,但应用程序仍然在那里可见。

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

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

无论我做什么,图标都会留在那里。

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

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

N.B.:我真的很想按照我上面描述的方式来做,而不是混淆两个独立的进程和 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];

或者
[NSApp setActivationPolicy: NSApplicationActivationPolicyProhibited];

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

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

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

NSApp() 没有它。

我知道有一种方法可以通过使用 ctypes 加载 objc dylib,委托(delegate)给 NSApp 并发送“setApplicationActivationPolicy: ”,但我不知道这会与 wx.App() 混淆多少。对于应该已经可用的东西来说,这需要做很多工作。

以我的经验,NSApp() 和 wx.App() 同时活跃,非常讨厌对方。

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

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

所以,理想情况下,使用 setApplicationActivationPolicy 是我的目标,但如何? (请简单易行,不要弄乱 wx.App()。)

有任何想法吗???

最佳答案

好的人们,有一个好的、好的和正确的解决方案,没有任何麻烦。

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

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

IE。 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 的泄漏问题。

关于Python - 如何在 Mac OS X 上使用 GUI 应用程序制作守护程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34246801/

50 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com