Search code examples
pythonpysidepython-multithreadingqlistwidget

Altering PySide.QtGui.QListWidget with an emitted signal from a multiprocessing.Pool async call results in Runtime Error?


I have:

from PySide.QtCore import Signal, QObject
from multiprocessing import Pool

def some_computation():
    pass
    # ..some computations
    return 'result'

class MyClass(QObject):

    my_signal = Signal()

    def __init__(self):
        self.mylistwidget = # ... QListWidget from somewhere

        # bind the signal to my slot
        self.my_signal.connect(self.on_my_signal)

    # this is called after computation thread is finished
    def my_callback_fct(result):
        # ..bla bla
        self.my_signal.emit()

    # this is the function I call
    def do_some_async_computation(self)
        pool = Pool(processes=2)
        pool.apply_async(target=some_computation, callback=my_callback_fct)

    # this is the slot
    def on_my_signal(self):
        self.mylistwidget.clear()

I read around stackoverflow that in order to change the gui from a secondary execution thread one must use slot-signal mechanism, which is what I did in MyClass, although when I call do_some_async_computation I would expect the pool to initiate a secondary thread for some_computation function ..which happens, after the computation is finished the my_callback_fct is executed accordingly, which emits a my_signal signal that is connected to the on_my_signal slot, which is executed as expected, but when altering the self.mylistwidget it gives a Runtime Error / QWidget runtime error redundant repaint detected


Solution

  • I solved this by using a QtCore.QThread instead of multiprocessing.Pool. I was thinking about the mechanism you talked about @deets and I said to myself that it should be in in the same context in order for Qt.QueuedConnection to work, so that's why I wanted to go with QThread.

    class MyComputationalThread(PySide.QtCore.QThread):
    
        data_available = PySide.QtCore.Signal(str)
        def run(self):
            result = # ... do computations
            serialized_result = json.dumps(result) #  actually I use JSONEncoder here
            self.data_available.emit(serialized_result)
    

    ... and in my MyClass:

    class MyClass(QObject):
        # ...
        mythread = MyComputationalThread()
        # ...
    
        def __init__(self):
            # ...
            self.mythread.connect(do_something_with_serialized_data, PySide.QtCore.Qt.QueuedConnection)
            # ...
    

    It works like a charm.