Back to top

Calling a function in the main thread in PyQt

As UI related function in Qt are limited to the main thread, now and again it's useful to 'switch' from the current active thread to the main thread. One option to achieve this is to use the qtutils package. However, pulling in a third party dependency for something that seems trivial is not very satisfactory. The easiest way to execute a function on the main thread is by initializing a QObject while in the main thread (at the start of the application). A signal part of this object is automatically bound to the main thread. This means that slots connected to the signal are executed in the main thread. The Qt signal-slot connection type determines the blocking behaviour when emitting the signal.
# Save this file as InMain.py and import it 
# while in the main thread (at the start of the application)

import queue as q
from PyQt5.QtCore import *

class InMain(QObject):

    sig = pyqtSignal()
    
    def __init__(self):
        super().__init__()
        self.queue = q.Queue()
	# Notice we use a Qt.QueuedConnection here, change to your liking.
        self.sig.connect(self.execute_funs, Qt.QueuedConnection)
     
    def execute_funs(self):
        while not self.queue.empty():
            (fn, args, kwargs) = self.queue.get()
            fn(*args, **kwargs)
    
    def call(self, fn, *args, **kwargs):
        """Schedule a function to be called on the main thread."""
        self.queue.put( (fn, args, kwargs) )
        self.sig.emit()

inMain = InMain()
It's important to import the Python file containing this code while in the main thread, otherwise (the QObject) inMain won't be part of the main thread. To call a function in the main thread you can use:
from InMain import inMain

# ... somewhere in the code
inMain.call(fun_to_call, some_arg, keyword_arg=10)