0

我正在尝试将信号从我的 pyobjc gui(osx 状态栏中的菜单)发送到我的应用程序的主进程。具体来说,我正在运行包装在一个类中的 gui,并在一个进程中运行,并且我试图通过管道将消息从 gui 发送到主进程。

当我使用一种简单的方法将数据放入管道时,我的代码就可以工作了。消息被传递给主进程,main process... recv(): foo当我在子进程中启动 gui 并尝试将数据放入管道时,比如当我单击菜单选项“开始”时,什么也没有发生。主进程线永远不会被打印出来,据我所知,主进程被阻塞了。

我假设这与 pyobjc 中的事件循环有关。我能做些什么来完成这项工作?如何将 pyobjc 代码作为子进程运行?


主文件

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())

用户界面.py

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"
4

1 回答 1

1

您的代码在主进程 (main.py) 中创建 GUI 对象,然后在使用 fork 创建的子进程中使用该对象。大多数 Apple 的框架都不支持这一点。

此外,对 OSXstatusbaritem.start 的调用会在主进程中创建并运行事件循环。

通过在子进程中创建 GUI 对象,您可能会取得更大的成功,但即使这样也不能保证有效(如果您不走运,GUI 框架已经初始化并在子进程中使用它时会导致崩溃):

p = Process(target=OSXstatusbaritem.start, args=(pipeUI,))

启动状态栏项进程的最安全方法是使用子进程,以避免在不调用 execv(2) 的情况下创建新进程。有一些关于 Python 的跟踪器的讨论,以向多处理模块添加一个选项,以使用 fork+exec 启动新进程,但这还没有导致提交。

于 2013-06-17T11:36:54.973 回答