Search code examples
pythonmultithreadingqtsignals-slotspyside

PySide: Easier way of updating GUI from another thread


I have a PySide (Qt) GUI which spawns multiple threads. The threads sometimes need to update the GUI. I have solved this in the following way:

class Signaller(QtCore.QObject) :
    my_signal = QtCore.Signal(QListWidgetItem, QIcon)
signaller = Signaller()

class MyThread(threading.Thread):
    def __init__(self):
        super(IconThread, self).__init__()
        # ...

    def run(self) :
        # ...

        # Need to update the GUI
        signaller.my_signal.emit(self.item, icon)

#
# MAIN WINDOW        
# 
class Main(QtGui.QMainWindow):

    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        # ...

        # Connect signals
        signaller.my_signal.connect(self.my_handler)

    @QtCore.Slot(QListWidgetItem, QIcon)
    def my_handler(self, item, icon):
        item.setIcon(icon)

    def do_something(self, address):
        # ...

        # Start new thread 
        my_thread = MyThread(newItem)
        my_thread.start()

    # ...

Is there an easier way? Creating the signals, handlers and connect them requires a few lines of code.


Solution

  • I started coding with PySide recently and I needed a equivalent of PyGObject's GLib.idle_add behaviour. I based the code off of your answer ( https://stackoverflow.com/a/11005204/1524507 ) but this one uses events instead of using a queue ourselves.

    from PySide import QtCore
    
    
    class InvokeEvent(QtCore.QEvent):
        EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
    
        def __init__(self, fn, *args, **kwargs):
            QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
            self.fn = fn
            self.args = args
            self.kwargs = kwargs
    
    
    class Invoker(QtCore.QObject):
        def event(self, event):
            event.fn(*event.args, **event.kwargs)
    
            return True
    
    _invoker = Invoker()
    
    
    def invoke_in_main_thread(fn, *args, **kwargs):
        QtCore.QCoreApplication.postEvent(_invoker,
            InvokeEvent(fn, *args, **kwargs))
    

    Which is used the same way in the above answer link.