QObject (QPlainTextEdit)与多线程问题

13 投票
1 回答
19049 浏览
提问于 2025-04-15 18:16

我现在正在学习用Python的asyncore和pyqt4进行网络编程。

我写了一个小服务器,基本上就是监听某个端口,然后把收到的所有消息转发给发送者。

因为qt的 QApplication.exec_()asyncore.loop() 这两个函数都是不会返回的,所以我不能在一个线程里同时启动它们,于是我在一个单独的守护线程里启动了 asyncore.loop()

每当我的服务器类(继承自 asyncore.dispatcher)建立或断开连接,或者发送/接收消息时,它会调用我的窗口类(继承自 QtGui.QMainWindow)的方法,这些方法会把信息显示在一个 QPlainTextEdit 中。

但是,文本不显示,除非你用鼠标选中它。

Python控制台显示了以下错误信息:

QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)

我在某个论坛上看到,这可能是因为从另一个线程调用qt函数造成的,使用信号和槽而不是直接调用函数可能会解决这个问题,但我也尝试了信号,还是出现了这个错误。

所以,如果这真的是我问题的原因,那么从另一个线程调用qt对象的方法的正确方法是什么呢?

编辑:更多信息: asyncore.loop() 的调用是在子线程中,虽然它并不是完全阻塞的,但在 asyncore.loop() 运行期间,我的服务器类(asyncore.dispatcher)才能进行网络操作。所以,在 asyncore.loop() 运行期间,我的服务器类的方法是由 asyncore.loop()(即子线程)调用的,在这些方法中我尝试向主线程中运行的窗口类发出信号。

编辑:看起来我现在搞定了,我的代码里有一些错误,现在用信号一切都按预期工作了。

编辑:小例子: http://paste2.org/p/635612(链接已失效)

1 个回答

16

看起来你正在尝试从主线程以外的线程访问QtGui类。就像一些其他的图形界面工具包(比如Java Swing)一样,这是不被允许的。根据Threads and QObjects网页上的内容:

虽然QObject是可重入的,但图形界面类,特别是QWidget及其所有子类,是不可重入的。它们只能在主线程中使用。

解决这个问题的方法是使用信号和槽来进行主线程(图形界面对象所在的地方)和你的其他线程之间的通信。简单来说,就是在一个线程中发出信号,然后通过另一个线程把信号传递给QObjects。上面我提到的网页对此有很好的讨论。实际上,关于Qt中的线程支持的整个部分都值得一读。

你可能会遇到的一个问题是,通常要在不同线程之间完全支持信号和槽,你需要在子线程中启动一个事件循环,使用QThread::exec()(或者PyQt的等效方法),这样信号才能传递到那个线程中的槽。不过,在你的情况下,听起来你正在调用asyncore.loop(),这会阻止你这样做。但是,如果你只需要在一个方向上发出信号(从子线程到主线程中的小部件),我觉得你应该不会有问题。

撰写回答