2013-06-14 2 views
0

Я пытаюсь передать сигналы из моего pyobjc gui (меню в строке состояния osx) в основной процесс моего приложения. В частности, я запускаю gui, завернутый в класс, и это внутри процесса, и я пытаюсь отправить сообщения из gui в основной процесс через канал.pyobjc как подпроцесс, связанный с основным процессом через трубу, не работает

Когда я использую простой метод для ввода данных в трубу, мой код работает. Сообщение переходит к основному процессу, давая main process... recv(): foo Когда я запускаю gui в подпроцессе и пытаюсь поместить данные в трубу, скажите, когда я нажимаю на пункт меню «start», ничего не происходит. Основная технологическая линия никогда не печатается, и насколько я могу судить, основной процесс заблокирован.

Я предполагаю, что это имеет какое-то отношение к циклу событий в 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()) 

userinterface.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" 

ответ

1

Ваш код создает объекты GUI в главном процессе (main.py), а затем использует этот объект в подпроцесса, созданного используя fork. Это не поддерживается большинством инфраструктур Apple.

Кроме того, вызов OSXstatusbaritem.start создает и запускает eventloop в основном процессе.

Возможно, у вас может быть больше успеха, создав объект GUI в дочернем процессе, но даже это не гарантирует работу (если вам не повезло, инфраструктура GUI уже инициализирована и вызывает сбой при ее использовании в дочернем процессе):

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

Самый безопасный способ начать процесс элемента строки состояния его использовать подпроцесс, чтобы избежать создания нового процесса без вызова execv (2). There was some talk on Python's tracker, чтобы добавить модуль мультипроцессорности для запуска новых процессов с использованием fork + exec, но это еще не привело к фиксации.

+0

Чтобы уточнить, что безопасный способ запуска элемента статуса в качестве подпроцесса означает использование модуля 'subprocess' или использование' os.fork + os.exec'? Каков типичный способ сделать IPC между процессами в этом случае? Просто стандартная труба Unix? – nflacco

+0

Будет работать либо os.fork + os.exec, либо модуль подпроцесса. Я бы использовал модуль подпроцесса, потому что h был более приятным интерфейсом. Затем вы должны использовать каналы для связи (аргумент subprocess.PIPE для STDOUT или os.pipe, если вы не используете модуль подпроцесса). –