In PyQt it is not recommended to use the main UI thread to handle time-consuming operations, which can cause the window component to block. Time-consuming operations are usually placed in a sub-thread. After the sub-thread is finished, you may need to update the window component, but PyQt does not recommend using the sub-thread to update the main thread (it is not impossible to update), which uses the signal slot mechanism to update the main thread.
- Create a signal in a subclass of QObject (
) Properties
- Bind this signal property to a function in another class, and the bound function is called the slot function for the entire signal. A signal can be bound to more than one slot function.
- When this signal is emitted, the corresponding slot function is called
It may be questioned whether the thread in which the slot function is being executed is the same as the thread in which the signal is being sent.
Note that signal one is defined in QObject or its subclasses. When the signal is emitted by calling the emit method of the attribute, all slot functions bound to the signal will be called, but the calling thread is not necessarily the thread that sent the signal, which is similar to the PyQtThread Affinity (Thread Affinity)
Related.
Thread Affinity (Thread Affinity)
In PyQt, an object can be moved to different threads, but an object can belong to only one thread at a time. This is because Qt uses the concept of Thread Affinity to manage which threads an object belongs to.
Each Qt object is associated with a specific thread, i.e. it has thread affinity.The thread affinity of an object determines in which thread the object's slot function is executedThe object will be associated with the thread in which it was created by default. By default, an object is associated with the thread in which it was created when it is created, but you can use the moveToThread method to move the object to another thread.
Example of an error:
from import QApplication, QMainWindow, QPushButton,QFileDialog from import QThread,pyqtSignal,QObject import sys, threading class MyWindow(QMainWindow): def __init__(self, parent=None): super(MyWindow, self).__init__(parent) = QPushButton('Hi') (self.on_click) () def on_click(self): print("on_click",threading.current_thread().name) = MyThread(self) () def set_text(self,file_name): print("setText",threading.current_thread().name) (file_name) class MyThread(QThread): def __init__(self,mv:QMainWindow) -> None: super().__init__(None) = mv def run(self): print('run',threading.current_thread().name) (5) .set_text("Hello World") if __name__ == '__main__': app = QApplication([]) window = MyWindow() () (app.exec_())
Output results:
on_click MainThread
run Dummy-1
setText Dummy-1 //Subthread to update UI, not recommended.
Using the Signal Slot Mechanism
from import QApplication, QMainWindow, QPushButton,QFileDialog from import QThread,pyqtSignal,QObject import sys, threading class MyWindow(QMainWindow): def __init__(self, parent=None): super(MyWindow, self).__init__(parent) = QPushButton('Hi') (self.on_click) () def on_click(self): print("on_click",threading.current_thread().name) = MyThread(self) (self.set_text) () def set_text(self,file_name): print("setText",threading.current_thread().name) (file_name) class MyThread(QThread): pyqtSignal = pyqtSignal(str) def __init__(self,mv:QMainWindow) -> None: super().__init__(None) = mv def run(self): print('run',threading.current_thread().name) (5) ("Hello World") if __name__ == '__main__': app = QApplication([]) window = MyWindow() () (app.exec_())
Output results:
on_click MainThread
run Dummy-1
setText MainThread //The thread that executes when updating the UI is the main thread.
The reason why the setText slot function will be executed by the main function is because of thread affinity. The object where the slot function is located is bound to MainThread, so of course it will be executed by the main thread.
However, PyQt5 does not recommend writing the transaction directly in run, the correct way to write it is as follows
Create a class integrated QObject to do the business. And bind this object to the newly created thread via moveToThread as the affinity thread for this object. Bind the QThread's started signal to this business event. The thread starts, sends the started signal, the business object starts processing the business, and sends a signal to the main thread slot function when it's done.
from import QApplication, QMainWindow, QPushButton,QFileDialog from import QThread,pyqtSignal,QObject import sys, threading class MyWindow(QMainWindow): def __init__(self, parent=None): super(MyWindow, self).__init__(parent) = QPushButton('Hi') (self.on_click) () def on_click(self): print("on_click",threading.current_thread().name) = QThread() = MyHandler() () (self.set_text) () () def set_text(self,file_name): print("setText",threading.current_thread().name) (file_name) class MyHandler(QObject): pyqtSignal = pyqtSignal(str) def handle(self): print('handle',threading.current_thread().name) ("Hello World") if __name__ == '__main__': app = QApplication([]) window = MyWindow() () (app.exec_())
Calling QFileDialog in a child thread
If the QFileDialog window is called in a subthread to select a file, the program crashes a few seconds after the QFileDialog window appears with the following code
from import QApplication, QMainWindow, QPushButton,QFileDialog from import QThread,pyqtSignal,QObject import sys, threading class MyWindow(QMainWindow): def __init__(self, parent=None): super(MyWindow, self).__init__(parent) = QPushButton('Hi') (self.on_click) () def on_click(self): print("on_click",threading.current_thread().name) = MyThread(self) (self.set_text) () def set_text(self,file_name): print("setText",threading.current_thread().name) (file_name) class MyThread(QThread): pyqtSignal = pyqtSignal(str) def __init__(self,mv:QMainWindow) -> None: super().__init__(None) = mv def run(self): print('run',threading.current_thread().name) file_name = (, 'Select file', './', 'Excel files(*.xlsx , *.xls)') print(file_name) ("Hello World") if __name__ == '__main__': app = QApplication([]) window = MyWindow() () (app.exec_())
Output results:
on_click MainThread
run Dummy-1
QObject::setParent: Cannot set parent, new parent is in a different thread
CoCreateInstance failed (Operation completed successfully.)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x21fb451d190), parent's thread is QThread(0x21fb443b430), current thread is MyThread(0x21fb8788df0)
CoCreateInstance failed (Operation completed successfully.)
QObject::startTimer: Timers cannot be started from another thread
Cause of the problem
In PyQt, you must be in the main thread to create child objects.
to this article on the PyQt sub-thread processing business events to solve the problem of the article is introduced to this, more related PyQt sub-thread content, please search for my previous posts or continue to browse the following related articles I hope you will support me in the future !