Python Pyqt5多线程更新UI代码实例(防止界面卡死)

yizhihongxing

下面是Python Pyqt5多线程更新UI代码实例(防止界面卡死)的完整攻略。

1. 背景

在使用PyQt5进行GUI开发时,假如某个计算机密集型的操作耗时较长,那么就可能会导致界面卡死,影响用户体验。为了避免这种情况,可以利用多线程更新UI的方式来解决。

2. 实现过程

2.1 创建线程

在PyQt5中利用QThread创建线程,在其run方法中执行需要进行的计算密集型操作,例如:

class Worker(QThread):

    finishSignal = pyqtSignal(int)

    def run(self):
        # 计算密集型操作
        # ...

    def __del__(self):
        self.wait()

2.2 进行信号槽连接

在主程序窗口中连接信号槽,将相关信号链接到主程序窗口的槽函数中。在槽函数中使用QMetaObject.invokeMethod() 方法来实现跨线程UI更新,例如:

class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.worker = Worker(self)
        self.worker.finishSignal.connect(self.update_ui)

    def on_start_button_clicked(self):
        self.worker.start()

    @pyqtSlot(int)
    def update_ui(self, progress):
        QMetaObject.invokeMethod(self.progress_bar, "setValue", Qt.QueuedConnection, Q_ARG(int, progress))

以上代码中,在worker的finishSignal信号被触发时,将会执行主程序窗口的update_ui槽函数,该函数中通过QMetaObject.invokeMethod()来更新UI。

2.3 代码完整示例

import sys
import time
from PyQt5.QtCore import pyqtSignal, QThread, QMetaObject, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow


class Worker(QThread):

    finishSignal = pyqtSignal(int)

    def run(self):
        for i in range(101):
            time.sleep(0.1)
            self.finishSignal.emit(i)


class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.worker = Worker(self)
        self.worker.finishSignal.connect(self.update_ui)

        self.start_button.clicked.connect(self.on_start_button_clicked)

    def on_start_button_clicked(self):
        self.worker.start()

    @pyqtSlot(int)
    def update_ui(self, progress):
        QMetaObject.invokeMethod(self.progress_bar, "setValue", Qt.QueuedConnection, Q_ARG(int, progress))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

以上代码中,我们通过Worker线程来模拟计算密集型操作,每隔0.1秒发射一次finishSignal信号,在主界面窗口的progress_bar进度条中进行更新。

3. 示例说明

3.1 示例1 - 多线程更新UI

我们在代码中添加一个QLabel控件,用于展示worker线程中的计数器变量。完整代码如下:

import sys
import time
from PyQt5.QtCore import pyqtSignal, QThread, QMetaObject, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow


class Worker(QThread):

    finishSignal = pyqtSignal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.count = 0

    def run(self):
        for i in range(101):
            time.sleep(0.1)
            self.count = i
            self.finishSignal.emit(i)


class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.worker = Worker(self)
        self.worker.finishSignal.connect(self.update_ui)

        self.start_button.clicked.connect(self.on_start_button_clicked)

    def on_start_button_clicked(self):
        self.worker.start()

    @pyqtSlot(int)
    def update_ui(self, progress):
        QMetaObject.invokeMethod(self.progress_bar, "setValue", Qt.QueuedConnection, Q_ARG(int, progress))
        QMetaObject.invokeMethod(self.label, "setText", Qt.QueuedConnection, Q_ARG(str, f"Count: {self.worker.count}"))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

在点击start_button按钮事件后,我们会启动Worker线程执行计算密集型操作,此时在主窗口的之前创建好的progress_bar进度条上将会由10%到100%逐渐切换。而在主程序窗口的label标签中将会展示一个不断递增的,从0开始的计数器变量,该变量与worker线程的运行是在同步的。

3.2 示例2 - 多线程同时操作UI组件

在本示例中我们主要演示多个线程如何同时更新同一个UI组件。我们假设我们有两个人在分别进行下棋操作,他们会在不同的线程内进行操作和计算,而在UI显示这些下棋操作的时候,他们都需要更新同一个UI棋盘组件。我们将棋盘组件设计成一个QLabel,使用字母“A”来代表落子,而使用字母“B”来代表可以下子的空位。

import sys
import time
from PyQt5.QtCore import pyqtSignal, QThread, QMetaObject, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from ui_mainwindow import Ui_MainWindow


class PlayWorker(QThread):

    finishSignal = pyqtSignal(list)  # 信号带参数

    def __init__(self, parent=None):
        super().__init__(parent)
        self.player = None  # 已放置棋子
        self.step = None  # 待放置位置

    def run(self):
        if self.step and self.player:
            self.finishSignal.emit([self.player, self.step])


class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.last_player = ""  # 上一个下棋的人
        self.chessboard = [["B" for i in range(3)] for j in range(3)]  # 用列表记录棋盘状态
        self.chessboard_labels = [[QLabel() for i in range(3)] for j in range(3)]  # 用于显示棋盘的标签

        for row in range(3):
            for col in range(3):
                self.grid.addWidget(self.chessboard_labels[row][col], row, col)

        self.play_worker_player_1 = PlayWorker(self)
        self.play_worker_player_1.finishSignal.connect(self.update_chessboard)

        self.play_worker_player_2 = PlayWorker(self)
        self.play_worker_player_2.finishSignal.connect(self.update_chessboard)

        self.player_1_button.clicked.connect(self.on_player_1_button_clicked)
        self.player_2_button.clicked.connect(self.on_player_2_button_clicked)

    def on_player_1_button_clicked(self):
        self.last_player = "A"
        for row in range(3):
            for col in range(3):
                if self.chessboard[row][col] == "B":
                    self.chessboard[row][col] = self.last_player
                    self.play_worker_player_1.player = self.last_player
                    self.play_worker_player_1.step = (row, col)
                    self.play_worker_player_1.start()

    def on_player_2_button_clicked(self):
        self.last_player = "B"
        for row in range(3):
            for col in range(3):
                if self.chessboard[row][col] == "B":
                    self.chessboard[row][col] = self.last_player
                    self.play_worker_player_2.player = self.last_player
                    self.play_worker_player_2.step = (row, col)
                    self.play_worker_player_2.start()

    @pyqtSlot(list)
    def update_chessboard(self, move):
        player, step = move[0], move[1]
        row, col = step[0], step[1]
        self.chessboard_labels[row][col].setText(player)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

在上述代码中,我们创建了两个PlayWorker线程,它们分别代表不同的人。当任意一个人点击了自己的按钮,我们将遍历整个棋盘,找到下棋的空白位置。根据选手不同选举合适的worker线程来执行信号槽通信操作,在update_chessboard槽函数中进行棋盘UI组件的更新。

4. 总结

在本篇文章中我们讲解了如何利用PyQt5创建QThread线程并与主线程进行通信,实现多线程更新UI的效果。并展示了两个完整的程序案例来进一步演示该技术。虽然该技术可以解决界面卡死的问题,但使用多线程也会带来一定的额外开销,并且如果线程之间没有合理的同步机制,那么会存在操作顺序异常的风险,请开发人员谨慎使用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python Pyqt5多线程更新UI代码实例(防止界面卡死) - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Python实现屏幕截图的两种方式

    Python实现屏幕截图有两种方式,分别是: 1.使用Pillow库的ImageGrab模块 2.使用PyQt5库的QScreen类 以下分别讲解这两种方式的具体实现步骤。 1.使用Pillow库的ImageGrab模块 步骤如下: 1.导入Pillow库和ImageGrab模块 from PIL import ImageGrab 2.使用ImageGrab…

    python 2023年5月19日
    00
  • Python 对积进行归约

    Python对积进行归约一般可以通过使用reduce()函数来实现。reduce()函数接受一个可迭代序列,将序列中的元素进行归约(reduce)操作,最终返回一个归约结果。在归约时,reduce()函数会先取出前两个元素,并将它们传给指定的二元函数进行处理,然后将函数结果与下一个元素再传入函数,依次执行,不断地缩小序列的范围,最终得出一个单一结果。redu…

    python-answer 2023年3月25日
    00
  • 基python实现多线程网页爬虫

    以下是基于 Python 实现多线程网页爬虫的攻略,包含以下步骤: 确定爬取目标; 分析目标页面的网页结构,获取需要的数据; 使用多线程并发爬取数据; 存储数据。 下面详细介绍每个步骤的实现。 1. 确定爬取目标 首先需要确定要爬取的目标,这个目标可以是一个网站的全部页面,也可以是某个特定的页面或数据。 2. 分析目标页面的网页结构,获取需要的数据 网页结构…

    python 2023年5月18日
    00
  • Python实现的栈、队列、文件目录遍历操作示例

    下面是Python实现栈、队列、文件目录遍历的攻略,分别讲解栈、队列、文件目录遍历的基础知识和示例代码: 栈 栈是一种数据结构,遵循“后进先出”的原则。栈的操作只能从栈顶进行,也就是说,从栈中取出元素的顺序和它们被放入的顺序是反向的。在Python中,可以使用列表类型来实现栈的操作,列表的append和pop方法可以添加和删除元素。 下面是一个栈的示例代码,…

    python 2023年5月20日
    00
  • python中私有函数调用方法解密

    下面我将详细讲解“Python中私有函数调用方法解密”的完整攻略。 什么是Python中的私有函数 在Python中,以双下划线开头的函数被视为私有函数,也称为“受保护的函数”。私有函数只能在对象内部被访问和调用,无法在对象外部被访问和调用。例如: class MyClass: def __init__(self): self.__private_var =…

    python 2023年6月5日
    00
  • python实现粒子群算法

    Python实现粒子群算法 粒子群算法(Particle Swarm Optimization,PSO)是一种基于群体智能的优化算法,可以用于解决各种优化问题。在Python中,可以使用numpy和matplotlib库实现粒子算法。本文将详细讲解实现粒子群算法的整个攻略,包括算法原理、实现过程和示例。 算法原理 粒子群算法是一种基于群体智能的优化算法,其基…

    python 2023年5月14日
    00
  • python爬虫之request模块深入讲解

    Python爬虫之request模块深入讲解 1. 前言 在使用Python爬虫进行网络数据获取时,使用requests模块非常方便快捷。requests模块封装了常见的HTTP请求方法,可以方便地进行GET和POST请求,可以自动处理Cookie、重定向、代理等功能并提供了优雅的API。 2. 安装requests模块 使用pip命令进行安装: pip i…

    python 2023年5月14日
    00
  • Python读取xlsx文件报错:xlrd.biffh.XLRDError: Excel xlsx file;not supported问题解决

    不过在回答之前,我先提供一下Python读取xlsx文件报错:xlrd.biffh.XLRDError: Excel xlsx file; not supported问题解决 的背景和原因: 问题背景: 我们使用Python操作xlsx文件时,有时候会遇到一个奇怪的错误——”xlrd.biffh.XLRDError: Excel xlsx file; not…

    python 2023年5月13日
    00
合作推广
合作推广
分享本页
返回顶部