
是这样的,我用 pyqt5 写了一个 gui 程序,在主线程中用 multiprocessing 启动新的进程执行一些任务。现在我想把这个子进程的 log 输出到 ui 上,采用了添加 logging.Handler 的方式获取子进程的 log ,然后写入到 ui 中。但是问题来了,由于 qt 的进程安全机制,明明已经获取到 log 了,但是它就是不让我输出到 ui 中,通过 print 的方式是可以显示的。
所以想问下各位大神,我需要怎么做才能让它把 log 写到 ui 中?
代码大概是这样的:
class MyLogHandler(logging.Handler): def __init__(self, obj): logging.Handler.__init__(self) self.Object = obj def emit(self, record): if record.levelno<self.level: return tstr = time.strftime('%Y-%m-%d %H:%M:%S.%U') self.Object.append("[%s][%s] %s"%(tstr, record.levelname, record.getMessage())) self.Object.moveCursor(QtGui.QTextCursor.End) mySW = MainWindow() handler = MyLogHandler(mySW.loggingBrowser) logging.getLogger().addHandler(handler) multiprocessing.Process(pass).start() 其中的 loggingBrowser就是 log 显示组件。由于 logging 是模块级别的,因此主线程中的MyLogHandler 可以捕获到子进程的 log 输出。
补充:
当然,如果用多线程的话是可以的,但是不管是python的多线程还是qt的多线程,都存在无法强制结束子线程的问题。所以只能用多进程了。
1 XYxe 2017-03-12 20:22:38 +08:00 用信号不行吗? signal = pyqtSignal(str) signal.connect(log_func) # 主线程 signal.emit(log_content) # 子线程 |
2 nyanyh 2017-03-12 20:23:32 +08:00 1L 正解 |
3 wwqgtxx 2017-03-12 20:28:06 +08:00 根本就不是这个问题,在 multiprocessing 开启的子进程和父进程之间是隔离的,你所说的“由于 logging 是模块级别的,因此主线程中的 MyLogHandler 可以捕获到子进程的 log 输出。”这句话本身就不成立,你用 print 能看到输出是因为这个是子进程直接输出到 stdout 了,根本就不是你父进程捕获到的 你要想解决这个问题,需要通过进程间通讯来传递你的日志信息 |
4 falseen OP @XYxe 试了不行,难道是我姿势不对 ? 修改之后的代码大概是这个样子的: ```python class MyLogHandler(logging.Handler): def __init__(self, obj): logging.Handler.__init__(self) self.Object = obj def emit(self, record): if record.levelno<self.level: return tstr = time.strftime('%Y-%m-%d %H:%M:%S.%U') self.Object.sin.emit("[%s][%s] %s" %(tstr, record.levelname, record.getMessage())) self.Object.loggingBrowser.moveCursor(QtGui.QTextCursor.End) class MainWindow(QMainWindow, Ui_MainWindow): sin = pyqtSignal(str) def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.sin.connect(self.loggingBrowser.append) handler = MyLogHandler(mySW.loggingBrowser) logging.getLogger().addHandler(handler) ........ multiprocessing.Process(pass).start() mySW = MainWindow() ``` |
5 falseen OP 手误,打错了。 handler = MyLogHandler(mySW.loggingBrowser) 这一行应该是 handler = MyLogHandler(mySW) |
7 XYxe 2017-03-12 22:39:03 +08:00 |
10 wwqgtxx 2017-03-12 23:07:25 +08:00 |
11 wwqgtxx 2017-03-12 23:11:59 +08:00 不过要注意 multiprocessing 类库中提供的跨进程通讯代理类的使用有一个小问题,就是不能反复创建新的线程来调用这些代理方法,否则会导致他内部创建过多的 socket 连接,最后被操作系统 kill 掉 |
12 falseen OP |
13 wwqgtxx 2017-03-12 23:18:54 +08:00 @falseen 我记得是可以通过 cytpes 调用 cpython 的 c 函数使得在另外一个进程中强行抛出一个 BaseExecption ,具体的办法你可以自己查查 |
14 wwqgtxx 2017-03-12 23:22:11 +08:00 |
15 toono 2017-03-13 08:30:03 +08:00 差点大意了 ,果然是进程间通信 |
16 falseen OP @wwqgtxx 试了没效果。我感觉如果用进程间通信的话还不如把 log 写入文件,然后主进程去读取。反正都要保存 log 的。 |
18 wwqgtxx 2017-03-13 10:51:25 +08:00 via iPhone 对了,我前面说的那个强行抛异常只适用于多线程环境,不能跨进程抛异常 |
19 falseen OP |