大家好,这里是Seon塞翁。笔者在最近的工作中做了一个基于PyQt5实现GUI的数据处理工具,领导表示“我想一双击就能直接看到情况啊,不要打开后还要我自己输入这个点击那个的!”好吧,既然上头有需求,打工人就得照办。想想以前用过的许多桌面软件,启动时都会有个欢迎画面,同时后台预加载一些内容。那么应该要怎么实现呢?先看看效果吧。

pyqt5深度学习界面 pyqt5做界面_多线程


1、界面设计

首先欢迎画面要有一个好看的背景,还有进度条和进度提示信息,那么先用一个 Widget 来做容器,再把用于显示 Logo 和进度信息的 label 拖出来,最后摆上进度条 progressBar

pyqt5深度学习界面 pyqt5做界面_pyqt5深度学习界面_02


然后是启动加载完成后显示的主页,用一个 lineEdit 显示接收到的预处理结果。

pyqt5深度学习界面 pyqt5做界面_多线程_03


此时启动画面还是一个默认风格的窗体,让我们美化一下样式,让它看起来更像样。

class LoadWin(QWidget, loadwin.Ui_Form):  # 启动画面类 -----------
    def __init__(self):
        super(LoadWin, self).__init__()
        self.setupUi(self)
        self.setWindowFlags(Qt.FramelessWindowHint)  # 无边框
        self.setStyleSheet("#Form{background-color:'#4682B4'}"
                           "#label_info{background-color:'#4682B4';color:white;font-weight:600;}"
                           )  # 设置启动窗背景色和进度信息的字体样式等
        pix = QPixmap("./load_logo.png")  # 加载logo
        self.label_logo.setPixmap(pix)
        self.label_logo.setScaledContents(True)  # 图像拉伸填充

pyqt5深度学习界面 pyqt5做界面_pyqt5_04

然后是启动计算完成后要显示的主页,定义一个函数接收计算结果。

class MainWin(QWidget, mainwin.Ui_Form):  # 主页界面类 -----------
    def __init__(self):
        super(MainWin, self).__init__()
        self.setupUi(self)

    def set_data(self, mes):  # 接收进度线程的计算结果
        self.lineEdit.setText(mes)

2、进度条逻辑

继续完善启动画面类 class LoadWin ,在 __init__ 方法中添加代码,通过定时器来为进度条增值,完成初始化后准备启动计算线程。

self.timer = QBasicTimer()  # 定时器对象
self.main_win = MainWin()  # 进度结束后要显示的主页
self.step = 0  # 进度值
self.proess_run()

实例化线程,分别绑定线程类中的进度环节信号和结束时回传数据的信号。

def proess_run(self):  # 启动进度线程
    self.cal = LoadThread()  # 线程对象
    self.cal.part_signal.connect(self.process_set_part)
    self.cal.data_signal.connect(self.show_main_win)
    self.cal.start()  # 调用线程run

根据线程回传的进度环节,启动定时器,分步为进度条调整增值速度和增量,设计为完成一个环节后就从头开始增值,即进度条多次重新充满。一般预处理环节不应过多,每个环节执行时间也不宜过长。如下仅设两个环节,用回传的 0 和 1 来识别到了哪一步,而进度条从 0 或 1 开始增值在视觉上也并无太大区别。当然也可根据需要把进度值 0~100 分为多个环节,即进度条总共仅充满一次。

def process_set_part(self, num):
    self.step = num  # 进度从num开始
    self.progressBar.setValue(self.step)
    if num == 0:
        self.timer.start(20, self)  # 启动QBasicTimer, 每20毫秒调用一次回调函数
        self.label_info.setText("正在计算数据...")
    if num == 1:
        self.timer.stop()  # 重启,调整进度条增值速度
        self.timer.start(10, self)
        self.label_info.setText("已完成计算,等待主页加载...")

重写定时器回调函数,为进度条增值。

def timerEvent(self, *args, **kwargs):  # QBasicTimer的事件回调函数
    self.progressBar.setValue(self.step)  # 设置进度条的值
    if self.step < 100:
        self.step += 1

完成计算后将结果传给主页,显示。

def show_main_win(self, mes):
    self.main_win.set_data(mes)
    self.main_win.show()
    self.close()

3、计算线程类

经过以上操作,基础准备才算做好了,下面继续实现真正用于执行任务的部分。run 方法被调用时发射信号,告诉启动界面我要开始第一步啦!完成第一步后再说一声,如此递进到最后回传计算结果。

class LoadThread(QThread):  # 自定义计算线程类 -----------
    part_signal = pyqtSignal(int)  # 进度环节信号
    data_signal = pyqtSignal(str)  # 数据传递信号

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

    def run(self):
        self.part_signal.emit(0)
        self.fun_part_one()
        self.part_signal.emit(1)
        sleep(1)  # 模拟加载耗时
        self.data_signal.emit("计算结果:2021")

    def fun_part_one(self):
        sleep(3)  # 模拟计算耗时

4、回顾流程及源码

一图流回顾执行流程:

pyqt5深度学习界面 pyqt5做界面_python_05

附完整代码如下:

from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt, pyqtSignal, QThread, QBasicTimer
import sys
from time import sleep
from myqt import loadwin, mainwin


class LoadWin(QWidget, loadwin.Ui_Form):  # 启动画面类 -----------
    def __init__(self):
        super(LoadWin, self).__init__()
        self.setupUi(self)
        self.setWindowFlags(Qt.FramelessWindowHint)  # 无边框
        self.setStyleSheet("#Form{background-color:'#4682B4'}"
                           "#label_info{background-color:'#4682B4';color:white;font-weight:600;}"
                           )  # 设置启动窗背景色和进度信息的字体样式等
        pix = QPixmap("./load_logo.png")  # 加载logo
        self.label_logo.setPixmap(pix)
        self.label_logo.setScaledContents(True)  # 图像拉伸填充
        self.timer = QBasicTimer()  # 定时器对象
        self.main_win = MainWin()  # 进度结束后要显示的主页
        self.step = 0  # 进度值
        self.proess_run()

    def proess_run(self):  # 启动进度线程
        self.cal = LoadThread()  # 线程对象
        self.cal.part_signal.connect(self.process_set_part)
        self.cal.data_signal.connect(self.show_main_win)
        self.cal.start()  # 调用线程run

    def process_set_part(self, num):
        self.step = num  # 进度从num开始
        self.progressBar.setValue(self.step)
        if num == 0:
            self.timer.start(20, self)  # 启动QBasicTimer, 每20毫秒调用一次回调函数
            self.label_info.setText("正在计算数据...")
        if num == 1:
            self.timer.stop()  # 重启,调整进度条增值速度
            self.timer.start(10, self)
            self.label_info.setText("已完成计算,等待主页加载...")
    
    def timerEvent(self, *args, **kwargs):  # QBasicTimer的事件回调函数
        self.progressBar.setValue(self.step)  # 设置进度条的值
        if self.step < 100:
            self.step += 1
            
    def show_main_win(self, mes):
        self.main_win.set_data(mes)
        self.main_win.show()
        self.close()


class LoadThread(QThread):  # 自定义计算线程类 -----------
    part_signal = pyqtSignal(int)  # 进度环节信号
    data_signal = pyqtSignal(str)  # 数据传递信号

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

    def run(self):
        self.part_signal.emit(0)
        self.fun_part_one()
        self.part_signal.emit(1)
        sleep(1)  # 模拟加载耗时
        self.data_signal.emit("计算结果:2021")

    def fun_part_one(self):
        sleep(3)  # 模拟计算耗时


class MainWin(QWidget, mainwin.Ui_Form):  # 主页界面类 -----------
    def __init__(self):
        super(MainWin, self).__init__()
        self.setupUi(self)

    def set_data(self, mes):  # 接收进度线程的计算结果
        self.lineEdit.setText(mes)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = LoadWin()
    w.show()
    sys.exit(app.exec())

另外,笔者入坑PyQt5时写的第一篇文章中也提到了启动画面,是个简易版的仅显示欢迎画面而未有耗时预处理计算的例子,有兴趣可以点击 启动画面与窗口间跳转的实现 看一看哈哈哈。
感谢阅读!