Why does the main event loop freeze when a private slot is called from a background thread?

I am developing a GUI application using python and PySide. I need to run a long backgroung task in a separate thread. For streaming, I decided to use QThread following the "correct" approach without subclassing from if (see http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the -full-explanation / ). So, to run my long task in the background, I simply connect the "started" thread signal to a method to execute the long task.

For some reason, my program is not working as expected. I spent several hours before I discovered that if I rename the slot name to "public" (remove the underscores), everything starts working as expected. Wondering what is wrong with the private slot? It looks like for some reason the main event loop is not running while a separate slot is running, although I can see that it runs on a thread other than the main thread (see Program Output below).

Here's an example that reproduces the problem:

from PySide.QtCore import QThread, QObject, QCoreApplication, QTimer
import time
import sys

class BackgroundWorker(QObject):
    def __init__(self):
        QObject.__init__(self)

    def __process(self):
        self.process()

    def process(self):
        print 'Background thread:', QThread.currentThread()
        for i in xrange(0, 5):
            print 'In background thread', i
            time.sleep(1)
        self.thread.quit()

    def start(self):
        self.thread = QThread()        
        self.moveToThread(self.thread)
        #self.thread.started.connect(self.process)      # <---- Case 1: This works as expected
        self.thread.started.connect(self.__process)    # <---- Case 2: Main event loop freezes. 
        self.thread.finished.connect(app.quit)
        self.thread.start()

def main_thread():
    print 'Main thread:', QThread.currentThread()
    for i in xrange(0, 5):
        print 'In main thread', i
        time.sleep(1)        

if __name__ == '__main__':
    app = QCoreApplication(sys.argv)
    bw = BackgroundWorker()
    QTimer.singleShot(0, bw.start)
    QTimer.singleShot(100, main_thread)
    app.exec_()

      

In the above code, there are two lines in the "BackgroundWorker.start" method to connect the "start" signal stream to the slots. The "Case 1" line works as expected (the shared slot is connected), but the "case 2" line does not (the private slot is connected).

Program output for case 1:

Background thread: <PySide.QtCore.QThread object at 0x02161738>
In background thread 0
Main thread: <PySide.QtCore.QThread object at 0x021616E8>
In main thread 0
In background thread 1
In main thread 1
In background thread 2
In main thread 2
In background thread 3
In main thread 3
In background thread 4
In main thread 4

      

Program output for case 2:

Background thread: <PySide.QtCore.QThread object at 0x021916E8>
In background thread 0
In background thread 1
In background thread 2
In background thread 3
In background thread 4
Main thread: <PySide.QtCore.QThread object at 0x02191788>
In main thread 0
In main thread 1
In main thread 2
In main thread 3
In main thread 4

      

I suspect it has something to do with the signal / slot implementation in PySide, but I was unable to find a bug report or anything related to the behavior I'm seeing.

PySide version is 1.2.2, python version is 2.6.6

+3


source to share


1 answer


When you hook up a QObject method in python, it is automatically registered with its meta object as a slot. However, this does not happen with methods that start with a double underscore.

This can be seen in a simple example:

from PySide.QtCore import *

class Test(QObject):

    sig = Signal()

    def __test(self):
        pass

    def test(self):
        pass

t = Test()
print(t.metaObject().methodCount())  # prints 5
t.sig.connect(t.test)
print(t.metaObject().methodCount())  # prints 6
t.sig.connect(t._Test__test)         # name mangling is applied to the __test method
print(t.metaObject().methodCount())  # still prints 6!

      



This means that such a method is treated as a normal function that wenn connected to a signal will always run on the main event thread. If you really want to use a method name starting with __

, you need to explicitly decorate it as a slot, then it should work:

from PySide.QtCore import QThread, QObject, QCoreApplication, QTimer, Slot
import time
import sys

class BackgroundWorker(QObject):
    def __init__(self):
        QObject.__init__(self)

    @Slot()
    def __process(self):
        self.process()

...

      

0


source







All Articles