6

Using Python3 and PyQt4 I have a function (run) that takes as an input a callable to provide status updates.

class Windows(QtGui.QWidget):
    # Creates a widget containing:
    # - a QLineEdit (status_widget)
    # - a button, connected to on_run_clicked

    def on_run_clicked(self):
        def update(text):
            self.widget.setText(text)

        threading.Thread(target=run, args=(update, )).start()

This works ok (i.e. the text updates are displayed properly in the widget). However, when I replace QLineEdit by QTextEdit and use the append method to add text, I get:

QObject::connect: Cannot queue arguments of type 'QTextCursor'

(Make sure 'QTextCursor' is registered using qRegisterMetaType().)

It still works but point to the fact that I am doing something wrong, and I am not sure that I will keep working when more threads are active. Usually, I do this type of updates using signals and slots, but the run function is not PyQt specific. The questions are:

  1. Why does it work without a warning for QLineEdit and not for QTextEdit?
  2. What is the right way to deal with a situation like this?
Filippo Mazza
  • 4,339
  • 4
  • 22
  • 25
Hernan
  • 5,811
  • 10
  • 51
  • 86

1 Answers1

11

I don't know the specific reason why one class works and the other doesn't - nor do I really know the difference between using Python threading vs. Qt's threading...however, I can tell you that it is very tempremental if you don't set it up properly. Namely, you cannot (or at the very least, should not) modify GUI objects from a thread. Again, not sure the difference of a python vs. a Qt thread on that. But, the safe way to modify your interface from a GUI is by sending signals to your window...easiest way I know to do this is via the Qt threading.

class MyThread(QtCore.QThread):
    updated = QtCore.pyqtSignal(str)

    def run( self ):
        # do some functionality
        for i in range(10000):
            self.updated.emit(str(i))

class Windows(QtGui.QWidget):
    def __init__( self, parent = None ):
        super(Windows, self).__init__(parent)

        self._thread = MyThread(self)
        self._thread.updated.connect(self.updateText)

        # create a line edit and a button

        self._button.clicked.connect(self._thread.start)

    def updateText( self, text ):
        self.widget.setText(text)
Eric Hulser
  • 3,912
  • 21
  • 20
  • 3
    [This question](http://stackoverflow.com/q/1595649/984421) is a good starting point for the Python vs Qt threads issue. Executive summary: always use Qt threads when interacting with Qt; otherwise, use Python threads. – ekhumoro Aug 23 '12 at 02:19
  • Which is the right way when you do not control the run function but you can give a callback to it? The easiest thing seems to be doing the emit on the callback function, right? Additionally, this post http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/ says you should not subclass QThread, does this apply also to PyQt? – Hernan Aug 23 '12 at 23:36
  • Huh, that is interesting - I have never seen that before. I'll have to read that in more detail. I mirror the comments on that thread, if you are not supposed to subclass QThread, then their documentation is grossly out of date... – Eric Hulser Aug 24 '12 at 04:24
  • @EricHulser Yep. That seems to be the case. There is an interesting C++ post here http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ – Hernan Aug 24 '12 at 13:46