QObject (QPlainTextEdit)与多线程问题
我现在正在学习用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 个回答
看起来你正在尝试从主线程以外的线程访问QtGui类。就像一些其他的图形界面工具包(比如Java Swing)一样,这是不被允许的。根据Threads and QObjects网页上的内容:
虽然QObject是可重入的,但图形界面类,特别是QWidget及其所有子类,是不可重入的。它们只能在主线程中使用。
解决这个问题的方法是使用信号和槽来进行主线程(图形界面对象所在的地方)和你的其他线程之间的通信。简单来说,就是在一个线程中发出信号,然后通过另一个线程把信号传递给QObjects。上面我提到的网页对此有很好的讨论。实际上,关于Qt中的线程支持的整个部分都值得一读。
你可能会遇到的一个问题是,通常要在不同线程之间完全支持信号和槽,你需要在子线程中启动一个事件循环,使用QThread::exec()
(或者PyQt的等效方法),这样信号才能传递到那个线程中的槽。不过,在你的情况下,听起来你正在调用asyncore.loop()
,这会阻止你这样做。但是,如果你只需要在一个方向上发出信号(从子线程到主线程中的小部件),我觉得你应该不会有问题。