Python pyobjc作为一个子流程,通过管道与主流程进行通信;行不通
我正试图从我的pyobjc gui(osx状态栏中的一个菜单)向我的应用程序的主进程发送信号。具体地说,我正在运行包装在类中的gui,它在一个进程中运行,我正在尝试通过管道将消息从gui发送到主进程 当我使用一种简单的方法将数据放入管道中时,我的代码可以工作。消息被传递到主进程,产生Python pyobjc作为一个子流程,通过管道与主流程进行通信;行不通,python,multiprocessing,pyobjc,Python,Multiprocessing,Pyobjc,我正试图从我的pyobjc gui(osx状态栏中的一个菜单)向我的应用程序的主进程发送信号。具体地说,我正在运行包装在类中的gui,它在一个进程中运行,我正在尝试通过管道将消息从gui发送到主进程 当我使用一种简单的方法将数据放入管道中时,我的代码可以工作。消息被传递到主进程,产生main进程。。。recv():foo当我在子流程中启动gui并尝试将数据放入管道时,比如当我单击菜单选项“开始”时,什么都不会发生。主流程线从未打印,据我所知,主流程被阻塞 我假设这与pyobjc中的事件循环有关。
main进程。。。recv():foo
当我在子流程中启动gui并尝试将数据放入管道时,比如当我单击菜单选项“开始”时,什么都不会发生。主流程线从未打印,据我所知,主流程被阻塞
我假设这与pyobjc中的事件循环有关。我能做些什么来让它工作?如何将pyobjc代码作为子进程运行
main.py
import sys
from multiprocessing import Process, Pipe
from userinterface import OSXstatusbaritem
def f2(pipe):
print "starting subprocess f2"
print pipe.send("foo")
pipe.close()
def main():
pipeUI, pipeServer = Pipe()
# p = Process(target=f2, args=(pipeUI,)) # <---------------------- This works
p = Process(target=OSXstatusbaritem.start(pipeUI), args=()) # <----This doesn't
p.start()
print "main process... recv():", pipeServer.recv()
p.join()
if __name__ == "__main__": sys.exit(main())
代码在主进程(main.py)中创建GUI对象,然后在使用fork创建的子进程中使用该对象。大多数苹果的框架都不支持这一点 此外,对OSXstatusbaritem.start的调用在主进程中创建并运行eventloop 通过在子进程中创建GUI对象,您可能会获得更大的成功,但即使这样也不能保证工作(如果您运气不好,GUI框架已经初始化,并且在子进程中使用时导致崩溃):
启动状态栏项process的最安全方法是使用subprocess,以避免在不调用execv(2)的情况下创建新流程。向多处理模块添加一个选项,以使用fork+exec启动新进程,但尚未导致提交。要澄清,为了安全地将状态项作为子进程启动,是否意味着使用
子进程
模块或使用os.fork+os.exec
?在这种情况下,在进程之间执行IPC的典型方式是什么?只有一个标准的unix管道?os.fork+os.exec或子流程模块都可以工作。我会使用子流程模块,因为h是一个更好的接口。然后使用管道进行通信(STDOUT的subprocess.PIPE参数,或者在不使用子流程模块时使用os.PIPE)。
import objc, re, os
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
from multiprocessing import Pipe
status_images = {'idle':'./ghost.png'}
class OSXstatusbaritem(NSObject):
images = {}
statusbar = None
state = 'idle'
@classmethod
def start(self, pipe):
self.pipe = pipe
self.start_time = NSDate.date()
app = NSApplication.sharedApplication()
delegate = self.alloc().init()
app.setDelegate_(delegate)
AppHelper.runEventLoop()
def applicationDidFinishLaunching_(self, notification):
statusbar = NSStatusBar.systemStatusBar()
# Create the statusbar item
self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
# Load all images
for i in status_images.keys():
self.images[i] = NSImage.alloc().initByReferencingFile_(status_images[i])
# Set initial image
self.statusitem.setImage_(self.images['idle'])
# self.statusitem.setAlternateImage_(self.images['highlight'])
# Let it highlight upon clicking
self.statusitem.setHighlightMode_(1)
# Set a tooltip
self.statusitem.setToolTip_('Sample app')
# Build a very simple menu
self.menu = NSMenu.alloc().init()
# Start and stop service
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Start Service', 'startService:', '')
self.menu.addItem_(menuitem)
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Stop Service', 'stopService:', '')
self.menu.addItem_(menuitem)
# Add a separator
menuitem = NSMenuItem.separatorItem()
self.menu.addItem_(menuitem)
# Terminate event
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit', 'terminate:', '')
self.menu.addItem_(menuitem)
# Bind it to the status item
self.statusitem.setMenu_(self.menu)
# Get the timer going
self.timer = NSTimer.alloc().initWithFireDate_interval_target_selector_userInfo_repeats_(self.start_time, 5.0, self, 'tick:', None, True)
NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSDefaultRunLoopMode)
self.timer.fire()
def tick_(self, notification):
print self.state
def startService_(self, notification):
self.pipe.send(["foobar", None])
print "starting service"
def stopService_(self, notification):
print "stopping service"
p = Process(target=OSXstatusbaritem.start, args=(pipeUI,))