PyQt5 利用QThread多线程弹出自动更新的进度条QProgressDialog提示框(避坑指南)

大致思路是除了主线程外制造三个子线程,一个子线程负责QProgressDialog,一个子线程处理特定工作,还有一个子线程负责查询工作进度并向主线程发送信号。主线程中设置信号槽连接到更新进度的函数。另外定义一个全局变量作为进度信号,由完成特定工作的线程负责更新,查询进度的线程负责侦听。查询进度的子线程中需要自定义一个信号向主线程通讯。

 

详细过程如下:

首先制作一个带pushButton的窗口,点击pushButton,程序将处理某一个事务,并弹出进度条提示框,在事务处理完毕后自行关闭提示框。

代码如下:

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton,QMainWindow,QProgressDialog
import time
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(722, 362)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 722, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolBar)
        self.toolBar_2 = QtWidgets.QToolBar(MainWindow)
        self.toolBar_2.setObjectName("toolBar_2")
        MainWindow.addToolBar(QtCore.Qt.RightToolBarArea, self.toolBar_2)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.toolBar_2.setWindowTitle(_translate("MainWindow", "toolBar_2"))
        self.pushButton.clicked.connect(self.progress)

运行后界面如下(以下是mac电脑运行结果,Windows不同):

定义一个完成特定工作的线程(在这里工作就是什么都不做,每0.2秒更新信号)

class working(QThread):
    def __init__(self):
        super(working,self).__init__()
    def run(self):
        for i in range(1,100+1):
            time.sleep(0.2)
            global global_jindu
            global_jindu=I #进度信号

定义一个侦听进度的线程,并创建一个自定义信号,周期性向主线程发送

class find_jindu(QThread):
    sinOut=pyqtSignal(str)#定义自定义信号
    def __init__(self):
        super(find_jindu,self).__init__()
    def run(self):
        while True:
            time.sleep(0.1)
            global global_jindu
            self.sinOut.emit(str(global_jindu))#向主线程发送信号
            if global_jindu==100:
                print("break")
                break
        return

在主线程定义特定动作(定义在class Ui_MainWindow下)

def progress(self):
        global global_jindu
        global_jindu=0
        try:
            self.progress_bar=progre()#该progre线程开启后不再关闭,后续只对其初始化
        except:
            pass
        self.progress_bar.start()#初始化progre线程,令进度为0
        self.work=working()
        self.work.start()
        self.find_jindu=find_jindu()
        self.find_jindu.start()
        self.find_jindu.sinOut.connect(self.update)#接收到信号并执行update
    def update(self,s):
        s=int(eval(s))
        print(s)
        self.progress_bar.progress.setValue(s)

全部代码

# -*- coding: utf-8 -*-


from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton,QMainWindow,QProgressDialog
import time
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(722, 362)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 722, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolBar)
        self.toolBar_2 = QtWidgets.QToolBar(MainWindow)
        self.toolBar_2.setObjectName("toolBar_2")
        MainWindow.addToolBar(QtCore.Qt.RightToolBarArea, self.toolBar_2)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.toolBar_2.setWindowTitle(_translate("MainWindow", "toolBar_2"))
        self.pushButton.clicked.connect(self.progress)
    def progress(self):
        global global_jindu
        global_jindu=0
        try:
            self.progress_bar=progre()#该progre线程开启后不再关闭,后续只对其初始化
        except:
            pass
        self.progress_bar.start()#初始化progre线程,令进度为0
        self.work=working()
        self.work.start()
        self.find_jindu=find_jindu()
        self.find_jindu.start()
        self.find_jindu.sinOut.connect(self.update)#接收到信号并执行update
    def update(self,s):
        s=int(eval(s))
        print(s)
        self.progress_bar.progress.setValue(s)



class progre(QThread): # 创建线程类
    def __init__(self):
        super(progre,self).__init__()
        self.progress=QProgressDialog('', '', 0,0,MainWindow)
        self.progress.setFixedSize(400,200)
        self.progress.setWindowTitle('处理中')
        self.progress.setLabelText('当前进度值')
        self.progress.setCancelButtonText('取消')
        self.progress.setRange(0, 100)
        self.progress.canceled.connect(lambda:print('进度对话框被取消'))
        self.progress.setAutoClose(True)#value为最大值时自动关闭
    def run(self):#重写run,为了第二次启动时初始化做准备
        self.progress.setValue(0)

class working(QThread):
    def __init__(self):
        super(working,self).__init__()
    def run(self):
        for i in range(1,100+1):
            time.sleep(0.2)
            global global_jindu
            global_jindu=i

class find_jindu(QThread):
    sinOut=pyqtSignal(str)#定义自定义信号
    def __init__(self):
        super(find_jindu,self).__init__()
    def run(self):
        while True:
            time.sleep(0.1)
            global global_jindu
            self.sinOut.emit(str(global_jindu))#向主线程发送信号
            if global_jindu==100:
                print("break")
                break
        return


global_jindu=0
app=QtWidgets.QApplication(sys.argv)
MainWindow=QtWidgets.QMainWindow()
ui=Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()

sys.exit(app.exec())

执行结果:

一切正常!

总结踩坑点:

1、进度条提示框QProgressDialog的运行后就不会结束,如果放在主线程会卡死,所以一定要使用多线程

2、搭载进度条提示框QProgressDialog的线程一但开启变不会关闭,因此对progre实例化时会提示:QThread:Destroyed while thread is still running。从第二次执行开始,一定要绕开对其实例化的步骤。

3、各子线程只能访问自己内部空间,如果去访问主线程内部变量,如:子线程去给ui下的进度条set_value更新,会造成串线,程序会崩掉。

有问题请在评论区提出。

  • 6
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: PyQt5中的QThread类可以用于创建多线程应用程序。在多线程应用程序中,同步是非常重要的,因为多个线程可能会同时访问同一个资源。为了确保线程安全,可以使用互斥锁或信号量等同步机制来保护共享资源。在PyQt5中,可以使用QMutex和QSemaphore类来实现同步。QMutex类提供了一个互斥锁,可以确保同一时间只有一个线程可以访问共享资源。QSemaphore类提供了一个信号量,可以控制同时访问共享资源的线程数量。在使用QThread类创建多线程应用程序时,需要注意同步问题,以确保应用程序的正确性和稳定性。 ### 回答2: PyQt5多线程方面提供了很好的支持,通过QThread类,我们可以创建多个线程,从而实现并发操作。QThreadQt的线程类,它封装了线程的所有操作,如启动、停止、暂停、恢复等。下面我们来探讨一下如何在PyQt5中实现多线程同步。 1.线程间通信 由于多个线程是并发执行的,如果在某个线程中修改了共享变量,可能会对其他线程产生影响,因此为了保证程序的正确性,在多线程编程中必须保证线程间同步,确保线程之间通信的正确性、完整性和准确性。为了实现线程间通信,在PyQt5中提供了多种方法: a.使用信号和槽方法:信号和槽方法是Qt中的一种消息传递机制,由于多个线程之间不能访问同一个变量,只能通过信号和槽方法来交换信息。 b.使用共享变量:通过将变量声明为共享变量,多个线程可以访问这个变量,通过加锁和解锁保证同步。 c.使用队列:在一个线程中,将共享变量或计算结果放入队列中,然后在另一个线程中取出这些结果。 2.加锁和解锁 为了保证多线程的同步,PyQt5提供了多种锁的类型,如互斥锁、读写锁、信号量、条件变量等。互斥锁是最简单的一种锁,通常用于多个线程之间对同一共享资源的互斥访问。在一个线程中执行临界区代码时,需要加锁,以避免其他线程同时访问这个共享资源。在PyQt5中,可以使用QMutex类或QReadWriteLock类实现互斥锁。 3.线程池 线程池是一种常见的多线程应用模型,它由一个线程池管理器、工作线程和任务队列组成。线程池管理器负责创建、销毁和管理工作线程,工作线程执行实际的任务,任务队列用于存储等待执行的任务。线程池的优点是可以在一定程度上控制线程的数量,防止因线程过多导致程序崩溃,提高了程序的性能。 总之,在PyQt5中实现多线程同步需要注意线程间通信、加锁和解锁以及使用线程池等方法来提高程序的效率和可靠性。除此之外,还需要注意程序的实际应用场景和数据结构的合理设计,以达到更好的多线程同步效果。 ### 回答3: 在PyQt5中,QThread用于在单独的线程中执行耗时或长时间运行的任务。它是基于QObject的子类,可以通过信号和槽与其他线程和主线程进行通信。但是,在使用QThread时,需要注意多线程同步的问题,以避免出现不稳定和不可预测的行为。 为了避免多线程同步问题,可以采用以下几种方法: 1. 使用锁(QMutex)或信号量(QSemaphore): 使用锁或信号量可以确保多个线程不会同时访问共享资源。在一个线程中,可以使用QMutex.lock()函数来获得锁,使用QMutex.unlock()函数来释放锁。当一个线程正在使用共享资源时,其他线程将被阻塞,直到锁被释放为止。同样地,可以使用QSemaphore.acquire()和QSemaphore.release()函数实现信号量。 2. 使用互斥量(QReadWriteLock): 互斥量是一种特殊的锁,用于控制多个线程对同一资源的访问。QReadWriteLock类提供了读写锁,其中读锁可被多个线程同时保持,但写锁只能由一个线程保持,并且在此期间其他线程将被阻塞。 3. 使用信号和槽: 在使用QThread时,可以通过信号和槽来实现多线程同步。可以在主线程中定义一个信号,并将其连接到QThread中的槽。当QThread中的任务完成时,可以通过信号来通知主线程更新UI。 总之,多线程同步是编写可靠和高质量的多线程代码的关键部分。需要根据应用程序的要求选择合适的同步机制,并在代码中正确地实现它们。通过正确使用多线程同步机制,可以确保应用程序的可靠性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值