GUI编程--Qt--多线程


多线程,并发处理事务,提高效率;
多线程处理耗费时间的任务;

在Qt GUI 界面开发中使用多线程,这里总结两种方式:

  • 基于threading模块

    • 线程逻辑内部获取当前线程 threading.currentThread()
    • 获取物理地址 threading.get_ident()
    • 获取线程号 threading.get_native_id()
    • os.kill(进程id, signal.SIGTERM) 杀死进程
    • signal.pthread_kill(线程id, signal.SIGTERM) 杀死线程
  • 基于QThread/QThreadPool
    分别实现如下效果:
    在这里插入图片描述

原理解析

子线程更新数据,主线程更新界面,即进度条状态。

使用python threading模块


from threading import Thread, Lock, RLock, Semaphore, BoundedSemaphore, Event
from PySide2.QtCore import QTimer, QDate, QTime, QDateTime, QThread, QThreadPool, Qt, QObject, Signal
from PySide2.QtWidgets import *
from PySide2.QtGui import QIcon, QFont
import PySide2
import logging
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s-%(message)s")


#
# 自定义信号
class Laufing(QObject):
    notify = Signal(object)


# 自定义线程
class MyThread(Thread):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        Thread.__init__(self)

    def run(self):
        minField, maxField, sig = self.args
        minVal = int(minField.text()) if minField.text() else None
        maxVal = int(maxField.text()) if maxField.text() else None

        # 通知 必须输入最小值、最大值
        if minVal is None or maxVal is None:
            sig.notify.emit(None)

        else:
            index = minVal
            while index < maxVal:
                index += 1
                # QProgressBar 只能设置整数,自动计算百分比	
                # 这里待更新
                pert = (index - minVal)/(maxVal - minVal) * 100
                print("当前百分比:", pert)
                sig.notify.emit(pert)
                # 如下最少必须为1, 若为0.5 则无效,可采用msleep(500)
                QThread.sleep(1)


class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        # 设置窗口标题s
        self.setWindowTitle("laufing")
        self.setWindowIcon(QIcon("./imgs/dog.jpg"))

        # 窗口居中
        desk = QDesktopWidget()
        width, height = desk.width(), desk.height()
        self.resize(800, 600)
        self.move(width//2 - self.width()//2, height//2 - self.height()//2)

        self.laufing = Laufing()
        self.laufing.notify.connect(self.updateUi)

        #
        self.setUi()

    def setUi(self):
        gridLayout = QGridLayout(self)
        gridLayout.setContentsMargins(10, 10, 10, 10)

        minLabel = QLabel("最小值")
        minLabel.setStyleSheet("border: 1px solid red; border-radius: 10px; align:center;")
        maxLabel = QLabel("最大值")
        self.minField = QLineEdit()
        self.minField.setPlaceholderText("输入最小值")
        self.maxField = QLineEdit()
        self.maxField.setPlaceholderText("输入最大值")
        self.progress = QProgressBar()
        btn = QPushButton("点击下载")
        btn.clicked.connect(self.download)

        gridLayout.addWidget(minLabel, 0, 0)
        gridLayout.addWidget(self.minField, 0, 1)
        gridLayout.addWidget(maxLabel, 1, 0)
        gridLayout.addWidget(self.maxField, 1, 1)
        gridLayout.addWidget(self.progress, 2, 0, columnSpan=2, alignment=Qt.AlignCenter)
        gridLayout.addWidget(btn, 3, 0, columnSpan=2, alignment=Qt.AlignCenter)
        gridLayout.setColumnStretch(2,10)

    def download(self):
    	# 注意避免线程对象被垃圾回收
        self.th = MyThread(self.minField, self.maxField, self.laufing)
        # 设置守护线程,主线程退出,则不管子线程有没有执行完,子线程都退出
        self.th.setDaemon(True)
        self.th.start()

    def updateUi(self, arg):
        if arg is None:
            mb = QMessageBox(self)
            mb.setModal(True)
            mb.setWindowTitle("提示")
            mb.setText("<span style='color: red;'>必须输入最小值和最大值</span>")
            mb.setTextFormat(Qt.TextFormat.RichText)
            mb.setInformativeText("子标题")
            confirm = mb.addButton("确定", QMessageBox.ButtonRole.YesRole)
            cancle = mb.addButton("取消", QMessageBox.ButtonRole.NoRole)
            # 按钮被点击
            mb.buttonClicked.connect(lambda x : mb.hide() if x == confirm else None)

            mb.show()
        else:
            self.progress.setValue(arg)

	# 拖动窗口空白区域可移动窗口
    def mousePressEvent(self, event:PySide2.QtGui.QMouseEvent):
        self.originX = event.pos().x()
        self.originY = event.pos().y()

    def mouseMoveEvent(self, event:PySide2.QtGui.QMouseEvent):
        self.move(event.screenPos().x() - self.originX, event.screenPos().y() - self.originY - 35)


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)

    win = MyWidget()
    win.show()

    sys.exit(app.exec_())

 

基于QThread实现

  • 重写run方法;
    简单修改上述的线程类定义即可。
  • 注意,QThread 线程运行期间不能被垃圾回收,否则报错;
# 防止线程对象在执行时,被垃圾回收或者强制kill,增加self引用
self.th = MyThread(self.minField, self.maxField, self.laufing)

# QThread 线程默认是守护线程
# run方法是在子线程中执行
# run执行后,再次调用start将不会进行任何处理
# run运行时,self.th.quit/exit 无法中断线程,只是等线程执行结束,再退出
# run 运行时, self.th.terminate() 可以立即中断子线程,但不安全
# run运行时,可以在主线程设置一个变量标识,子线程中运行时检测该变量,从而控制子线程的中断(注意加锁)。
# mutex = QMutex()
# mutex.lock()/unlock()

# QThread子线程的信号
self.th.started.connect(func)   # 子线程正常开始后
self.th.finished.connect(self.th.deleteLater())   # 子线程正常结束后
# 正常结束后,一般使用deleteLater释放内存,析构对象

 

使用QThreadPool实现

QThreadPool 官方文档
结合QRunnable使用

from PySide2.QtCore import Qt, QThreadPool, QRunnable, QThread


class Task(QRunnable):
    def run(self):
        i = 0
        while i < 10:
            print(i)
            QThread.sleep(1)
            i += 1

self.th_pool = QThreadPool(self)
self.th_pool.setMaxThreadCount(10)
self.task = Task()
self.th_pool.start(self.task)

# 取消队列中尚未执行的任务
# th_pool.cancle(task)  已弃用
th_pool.tryTake(task)

# 正在执行的任务,不能强制terminate 或者kill
# 只能在task->run的逻辑内部判断flag 来主动return
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laufing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值