是这样的,我用 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 @wwqgtxx
用了进程间通信之后最终还是决定使用写入文件的方式,主进程和子进程程都把 log 写入文件,需要查看的 log 的时候开一个子线程读取然后显示。我觉得这样开销会小一点。 另外想问下大神,如何优化 pyqt5 的内存占用呢 ?具体见这个帖子 https://www.v2ex.com/t/347235 |