QObject (QPlainTextEdit) & Multithreading issues

smerlin picture smerlin · Jan 20, 2010 · Viewed 16.8k times · Source

Im currently trying to learn Networking with Python asyncore and pyqt4.

I coded a small server, which basically listens on some port, and resends all messages it recieves to the sender.

Since both qts QApplication.exec_() and asyncore.loop() are functions which never return i could not start them both in one thread, so i stared asyncore.loop() in a seperate daemon thread.

Whenever my server class (derived from asyncore.dispatcher) establishes or drops a connection, or sends/recieves a message, it calls methods of my window class (derived from QtGui.QMainWindow), which displays the information in a QPlainTextEdit.

But the text is not visible, unless you mark the text with the mouse.

Python console displays following error msg:

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().)

I read on some forum, that this may be caused by calling qt-functions from another Thread, and that using signals & slots instead of plain function calling may fix the issue, but i have tried signals aswell, and i still get this error.

So, (if that is really the cause of my problems) whats the correct way to call methods of an qt object from another thread ?

EDIT More Info: the asyncore.loop() call is located in the child thread, well its not really blocking, but only during the runtime of asyncore.loop() my Server class (asyncore.dispatcher) can do networking. So, during the runtime of asyncore.loop() the methods of my Server class ARE called by asyncore.loop() (=child thread), and in these i tried to emit signals to the window class running in the main thread

EDIT: Seems like i got it working now, i had some errors in my code, everything works as intended with signals now.

EDIT: small example: http://paste2.org/p/635612 (dead link)

Answer

Rob H picture Rob H · Jan 20, 2010

It appears you're trying to access QtGui classes from a thread other than the main thread. Like in some other GUI toolkits (e.g. Java Swing), that's not allowed. From the Threads and QObjects web page:

Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.

A solution is to use signals and slots for communication between the main thread (where the GUI objects live) and your secondary thread(s). Basically, you emit signals in one thread that get delivered to the QObjects via the other thread. The page I linked to above has a good discussion of this. Actually, the whole section on Thread Support in Qt is a good read.

One potential issue you could run into is that, normally, to get full signals and slots support working across threads, you need to start an event loop in the child thread using QThread::exec() (or the PyQt equivalent) so that signals can be delivered to slots in the QObjects that live there. In your case, it sounds like you're making a blocking call to asyncore.loop(), which will prevent you from doing this. But, if you only need to emit signals in one direction (from the child thread to widgets in the main thread), I don't think you'll have a problem.