2017-04-10 38 views
0

我有一個PySide應用程序。在這個應用程序中,主函數運行在一個線程中(AnalysisThread)。在這個線程中,我用Python記錄器記錄了一些東西。然後我添加一個自定義記錄器,它實際上是用一個字符串觸發一個信號。這個信號可以在主線程(GUI線程)中處理,但顯然插槽永遠不會觸發,但我確信信號self.messageWritten.emit函數被調用(調試器確認這一點)。我究竟做錯了什麼 ?PySide中沒有處理的信號

class LogStream(QtCore.QObject): 
    messageWritten = QtCore.Signal(str) 
    signal_test = QtCore.Signal() 

    def write(self, msg): 
     if not self.signalsBlocked(): 
      self.messageWritten.emit(msg) 


class QtHandler(logging.Handler): 
    def __init__(self, stream): 
     logging.Handler.__init__(self) 
     self.stream = stream 

    def emit(self, record): 
     record = self.format(record) 
     if record: 
      self.stream.write('%s\n' % record) 

class AnalysisThread(QtCore.QThread): 
    processing_ended = QtCore.Signal() 
    processing_failed = QtCore.Signal(Exception, list) 

    def __init__(self, analysis): 
     super(AnalysisThread, self).__init__() 
     self.analysis = analysis 

    def run(self): 
     try: 
      process = Process(target=self.analysis.analyze) 
      process.start() 
      process.join() 
     except Exception as err: 
      exec_info = sys.exc_info() 
      self.processing_failed.emit(err, exec_info) 
     finally: 
      self.processing_ended.emit() 

class ProcessView(QtGui.QMainWindow): 

    def __init__(self): 
     super(ProcessView, self).__init__() 
     # Log Stream 
     self.stream = LogStream() 
     self.stream.messageWritten.connect(self.on_log_written) 

    def go(self): 
     analysis = MyAnalysis() 
     # Handler 
     handler = QtHandler(self.stream) 
     handler.setFormatter(logging.Formatter('(%(levelname)s-%(name)s) %(message)s')) 
     analysis.log.addHandler(handler) 
     self.processing = AnalysisThread(analysis) 
     self.processing.processing_ended.connect(self.on_processing_ended) 

self.processing.processing_failed.connect(self.on_processing_failed) 
     self.processing.start() 

    def on_log_written(self, msg): 
     print('Message: {}'.format(msg)) # never called 

編輯

爲了澄清,這是一個多線程的應用程序,而且還多進程一個...

回答

0

既然你在多線程環境中工作,嘗試指定的Qt 。當你連接你的信號和插槽時,連接

例如:

self.stream.messageWritten.connect(self.on_log_written, QtCore.Qt.QueuedConnection) 
+0

根據文檔,QueuedConnection是多線程的默認行爲。但是,它不起作用,無論是DirectConnection。即使在執行結束時,也不會顯示日誌。 – GuillaumeA

0

ANSWER

我終於找到了解決方案。我的應用程序如上所述使用多處理,並且日誌發生在子進程中,所以父進程永遠不會警告信號。解決方案是在子代和父進程之間創建一個「鏈接」,其中包含multiprocessing.Pipe。實現可能是

class Streamer(QtCore.QThread): 
    messageWritten = QtCore.Signal(str) 

    def __init__(self, pipe): 
     super(Streamer, self).__init__() 
     self.pipe = pipe 

    def run(self): 
     while True: 
      try: 
       msg = self.pipe.recv() 
      except EOFError: 
       break 
      else: 
       self.messageWritten.emit(msg) 

class QtHandler(logging.Handler): 
    def __init__(self, stream): 
     """Instantiate handler 

     :param stream: multiprocessing.Pipe 
     """ 
     logging.Handler.__init__(self) 
     self.stream = stream 

    def emit(self, record): 
     record = self.format(record) 
     if record: 
      self.stream.send('%s\n' % record) 

class ProcessView(QtGui.QMainWindow): 

    def __init__(self): 
     super(ProcessView, self).__init__() 
     # Log Stream 
     mother_pipe, child_pipe = Pipe() 
     self.stream = child_pipe 
     self.streamer = Streamer(mother_pipe) 
     self.streamer.daemon = True 
     self.streamer.start() 
     self.streamer.messageWritten.connect(self.on_log_written) 

    def on_log_written(self, msg): 
     self.ui.textBrowser_log.insertPlainText(msg) 
     txt_max = self.ui.textBrowser_log.verticalScrollBar().maximum() 
     self.ui.textBrowser_log.verticalScrollBar().setValue(txt_max) 

說明:

主線程(GUI)開始。在初始化時,它會創建一個multiprocesing.Pipe來與子進程通信,還有一個監聽管道結束的守護進程。所以在子進程啓動後,它最終會記錄一些內容。 QtHandler截取消息並通過管道發送。守護程序在管道的另一端收到消息,並通過信號messageWritten將其轉發到GUI線程。最後,該信號由一個插槽處理,該插槽將其寫入QTextBrowser,該滾動條被刷新爲