我有一个PySide应用程序。在此应用程序中,main函数在一个线程(AnalysisThread
)中运行。在这个帖子中,我用python logger记录了一些东西。然后我添加一个自定义记录器,它实际上触发了一个带字符串的信号。这个信号可以在主线程(GUI线程)中处理,但显然从不触发插槽,但我确信调用了信号self.messageWritten.emit
函数(调试器确认了这一点)。我做错了什么?
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 :(得分:0)
由于您在多线程环境中工作,请在连接信号和插槽时尝试指定 Qt.QueuedConnection 。
例如:
self.stream.messageWritten.connect(self.on_log_written, QtCore.Qt.QueuedConnection)
答案 1 :(得分: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
,滚动条刷新到