关于 pyqt5 跨进程操作 ui 的问题。

2017-03-12 18:26:48 +08:00
 falseen

是这样的,我用 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的多线程,都存在无法强制结束子线程的问题。所以只能用多进程了。

8743 次点击
所在节点    Python
20 条回复
XYxe
2017-03-12 20:22:38 +08:00
用信号不行吗?
signal = pyqtSignal(str)
signal.connect(log_func) # 主线程
signal.emit(log_content) # 子线程
nyanyh
2017-03-12 20:23:32 +08:00
1L 正解
wwqgtxx
2017-03-12 20:28:06 +08:00
根本就不是这个问题,在 multiprocessing 开启的子进程和父进程之间是隔离的,你所说的“由于 logging 是模块级别的,因此主线程中的 MyLogHandler 可以捕获到子进程的 log 输出。”这句话本身就不成立,你用 print 能看到输出是因为这个是子进程直接输出到 stdout 了,根本就不是你父进程捕获到的
你要想解决这个问题,需要通过进程间通讯来传递你的日志信息
falseen
2017-03-12 21:07:16 +08:00
@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()


```
falseen
2017-03-12 21:10:10 +08:00
手误,打错了。
handler = MyLogHandler(mySW.loggingBrowser) 这一行应该是 handler = MyLogHandler(mySW)
wwqgtxx
2017-03-12 22:04:39 +08:00
@XYxe pyqt 的 Signal 只是能跨线程,不能跨进程传输吧
XYxe
2017-03-12 22:39:03 +08:00
@wwqgtxx 啊,应该是的。

@falseen 在主进程里面创建一个线程用来和其他进程通信,用 Queue 。然后主进程内部用 pyqtSignal 。

weyou
2017-03-12 22:44:23 +08:00
@wwqgtxx 是正解
falseen
2017-03-12 23:01:44 +08:00
@wwqgtxx 是的,如你所说。是我理解错了。看来只能用进程间通信了。。
wwqgtxx
2017-03-12 23:07:25 +08:00
@falseen 最简单的方法就是用 @XYxe 的方法,而跨进程 Queue 可以使用 multiprocessing.Queue 来实现
wwqgtxx
2017-03-12 23:11:59 +08:00
不过要注意 multiprocessing 类库中提供的跨进程通讯代理类的使用有一个小问题,就是不能反复创建新的线程来调用这些代理方法,否则会导致他内部创建过多的 socket 连接,最后被操作系统 kill 掉
falseen
2017-03-12 23:15:36 +08:00
@wwqgtxx @XYxe 如果用进程间通信的话,感觉还不如用多进程。想请教下两位大神,如何强制关闭 Qthread 或 threading 生成的线程? 我试过 Qthread 的 terminate() wait() , wait() 之后就会无限等待,根本不会结束。因为我要启动的子线程是不可控的,因此不可能通过修改子线程的源码来自动退出。只能是由主线程结束。
wwqgtxx
2017-03-12 23:18:54 +08:00
@falseen 我记得是可以通过 cytpes 调用 cpython 的 c 函数使得在另外一个进程中强行抛出一个 BaseExecption ,具体的办法你可以自己查查
wwqgtxx
2017-03-12 23:22:11 +08:00
https://www.oschina.net/question/172446_2159505
这篇文章你可以参考参考
toono
2017-03-13 08:30:03 +08:00
差点大意了 ,果然是进程间通信
falseen
2017-03-13 10:15:10 +08:00
@wwqgtxx 试了没效果。我感觉如果用进程间通信的话还不如把 log 写入文件,然后主进程去读取。反正都要保存 log 的。
wwqgtxx
2017-03-13 10:50:13 +08:00
@falseen 如果多进程去读写文件容易出更多的奇奇怪怪的坑,具体的你自己衡量吧
wwqgtxx
2017-03-13 10:51:25 +08:00
对了,我前面说的那个强行抛异常只适用于多线程环境,不能跨进程抛异常
falseen
2017-03-14 15:58:41 +08:00
@wwqgtxx
用了进程间通信之后最终还是决定使用写入文件的方式,主进程和子进程程都把 log 写入文件,需要查看的 log 的时候开一个子线程读取然后显示。我觉得这样开销会小一点。

另外想问下大神,如何优化 pyqt5 的内存占用呢 ?具体见这个帖子 https://www.v2ex.com/t/347235
wwqgtxx
2017-03-14 16:30:00 +08:00
@falseen 那你最好注意不要在主进程和子进程中同时写文件,否则…
pyqt5 我没仔细研究过,所以帮不上你

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/346882

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX