PyQt中的多线程
- 一、PyQt中的多线程
- 二、创建线程
- 2.1 设计ui界面
- 2.2 设计工作线程
- 2.3 主程序设计
- 三、运行结果示例
一、PyQt中的多线程
传统的图形用户界面应用程序都只有一个执行线程,并且一次只执行一个操作。如果用户从用户界面中调用一个比较耗时的操作,那么当执行这个程序时,虽然实际上该操作正在进行,但用户界面通常会冻结而不再响应。
为了解决这个问题,通常会让用户的UI界面运行在它自己的线程中,而另外的事件处理过程则发生在一个或多个其他线程中。这样做之后,即使在处理那些数据密集的事件时,应用程序也能对GUI用户界面保持响应。当在一个单处理器上运行时,多线程应用程序可能会比实现同样功能的单线程应用程序运行得更慢一些,无法体现出其优势。但在目前多处理器系统越来越普及的情况下,多线程应用程序可以在不同的处理器中同时执行多个线程,从而获得更好的总体性能。
二、创建线程
2.1 设计ui界面
我们通过下面这个小例子来展示一下多线程的处理。用户界面如下。
使用Qt designer新建一个widget窗口,然后在其中添加2个button,分别是theradA和threadB,点击btn_theradA时,会不断地在控制台打印字母A,点击btn_theradB打印字母B。保存该ui文件为Form.ui
界面设计完毕后,使用以下命令,将Form.ui转换为form.py
pyuic5 -o form.py Form.ui
2.2 设计工作线程
添加一个工作线程类,用于完成按钮按下后需要执行的任务。
MyThread.py
from PyQt5.QtCore import QThread, qDebug
import time
class MyThread(QThread):
def __init__(self, thread_name):
super().__init__()
self.__stop_running = False # 此变量用于控制线程的运行
self.__thread_name = thread_name # 线程名,用于区分是哪个线程
def run(self):
while not self.__stop_running:
qDebug(f'{self.__thread_name} is running') # 不停地打印,模拟执行任务
time.sleep(1)
def stop(self):
self.__stop_running = True
在QT应用程序中提供多线程是非常简单的,只需要子类化QThread并且重新实现它的run()函数即可。
如上所述,我们定义了一个自己的Thread类,名为MyThread,它派生自QThread类,并且重新实现了run()函数。
当调用线程的start()函数时,就会自动调用run()函数。只要stop_running标志为false,run()函数就会不断地进行打印,模拟任务的执行。当控制离开run()函数时,线程就会终止。
2.3 主程序设计
程序入口文件main.py
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtCore import qDebug
import sys
from MyThread import MyThread
from form import Ui_Form
class MyWidget(QWidget):
def __init__(self) -> None:
super().__init__()
self.__init_ui()
# 定义2个工作线程
self.__threadA = MyThread('threadA')
self.__threadB = MyThread('threadB')
# 连接button的clicked信号到对应的槽函数
self.__my_form.btn_threadA.clicked.connect(self.start_or_stop_threadA)
self.__my_form.btn_threadB.clicked.connect(self.start_or_stop_threadB)
def __init_ui(self):
self.__my_form = Ui_Form()
self.__my_form.setupUi(self)
def start_or_stop_threadA(self):
if self.__threadA.isRunning():
self.__threadA.stop()
else:
self.__threadA.start()
def start_or_stop_threadB(self):
if self.__threadB.isRunning():
self.__threadB.stop()
else:
self.__threadB.start()
def closeEvent(self, e:QCloseEvent):
qDebug('stop all threads...')
self.__threadA.stop()
self.__threadB.stop()
self.__threadA.wait()
self.__threadB.wait()
e.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
my_widget = MyWidget()
my_widget.show()
sys.exit(app.exec_())
主程序界面通过Ui_Form类的setupUi建立,随后我们将两个button的clicked信号绑定到我们定义的槽函数start_or_stop_threadA和start_or_stop_threadB上,这样,点击按钮之后,就可以执行我们自定义的线程动作。
在关闭窗口时,会产生QCloseEvent事件,我们重写了closeEvent方法来对线程执行了清理工作,确保应用程序是以一种原始清空的状态进行退出。当然了,在这个例子中,是否这样做其实并没有什么影响,这里只是举个例子示范一下。
三、运行结果示例
执行main.py, 单击按钮A时,控制台就会输出A,再次单击则会停止输出。按钮B同理。当按钮A和按钮B被按下同时工作时,A和B会交替输出。
运行效果如下: