pyqt5进度条qprogressbar的使用/多线程更新/按钮美化/图片编码/开机自启动(代码片段)

zstar-_ zstar-_     2022-12-02     635

关键词:

前言

诚如标题所见,我在使用Pyqt5进行开发时,先后遇到了上面几个问题。本篇博客就用来记录遇到问题/解决问题的过程,希望能给遇到相同问题的读者一些参考。

项目背景

我的项目是构建一个可视化的交互界面,通过界面上的按钮可调用后台的爬虫程序。因此,需要通过添加一个进度条来反映当前的爬取进度。

进度条

Pyqt5设有进度条控件QProgressBar,官方文档提供了一个按钮驱动定时器加载进度条的例子。本次应用和官方例程略有不同。

进度条创建与样式设定

首先创建一个进度条,设定位置,并用QSS设定样式。

self.pb = QtWidgets.QProgressBar(self.centralwidget)
self.pb.setGeometry(QtCore.QRect(230, 690, 1021, 41))
self.pb.setStyleSheet("QProgressBar border: 2px solid grey; border-radius: 5px; background-color: #FFFFFF; text-align:center; font-size:20px")

这里的样式主要是对进度条外框进行修改,默认情况进度字体显示在进度条右侧,设置后将字体居中在进度条内,进度条则使用默认情况的绿色进度条,自带了动态加载光效,效果如图所示。

之后,设置进度条的范围[0,100],并将进度条在默认情况下进行隐藏。

self.pb.setRange(0, 100)
self.pb.hide() 

进度条更新

使用pyqt5独特的信号与槽函数可进行进度条的更新。进度条设置函数setvalue()
由于进度条总长度是未知的,因此首先在进度条开始更新之前,需要先获取总任务量的数据,然后将完成任务量/总任务量,映射到[0,100]的区间内进行更新。
此外,还需要获取一个信号来标记是否结束,如果结束,则立刻将进度条设置为100%,同时弹出提示信息(本程序是弹出一个提示框)

定义两个信号:
progressBarValue:用来回传当前换算后的进度数值
signal_done:用来回传完成标记(由于pyqtSignal无法回传bool型数据,采用int型来进行区分。0表示未完成,1表示完成)

对应两个槽函数:
callback:接收progressBarValue信号
callback_done:接收signal_done信号

更新逻辑:初始进度条为隐藏状态,点击按钮,进度条进行显示,并设定初始值为0。当所有链接获取完之后,进度条开始逐渐更新(每间隔十个数据进行一次进度条更新)。若全部爬取完成(signal_done发送1信号),进度条填满,并弹出提示框。
核心代码如下:

 # 两个参数初始化
 self.pb.setValue(0)  # 设置进度条为0
 self.is_done = 0  # 设置完成标记 完成/未完成 1/0
 self.progressBarValue.connect(self.callback)
 self.signal_done.connect(self.callback_done)
 
# 回传进度条参数
def callback(self, i):
    self.pb.setValue(i)

# 回传结束信号
def callback_done(self, i):
    self.is_done = i
    if self.is_done == 1:
        self.messageDialog1()

progressBarValue = pyqtSignal(int)  # 更新进度条
signal_done = pyqtSignal(int)  # 是否结束信号

Linklist_sum = self.pro_name.get_crawler_link()
length = len(Linklist_sum)
for start in range(0, length, 10):
	self.pro_name.run_crawlertask(start, Linklist_sum)
	self.progressBarValue.emit(int(start / length * 100))  # 发送进度条的值信号
self.signal_done.emit(1)  # 发送结束信号

def messageDialog1(self):
    msg_box = QMessageBox(QMessageBox.Information, '通知', '信息爬取已结束')
    self.pb.setValue(100)  # 如果爬取成功
    msg_box.exec_()

多线程更新

直接将进度条更新的程序段和要调用的程序段放在一起会出现一个问题。当调用程序段运行时,qt界面会卡住不动,造成“假死”现象。
因此,要解决这个问题,就要引入多线程。将后台程序放入到一个子线程中运行,同时将数值传递给主线程,在主线程中进行UI的更新。
修改后的进度条更新程序段如下:

# 封装调用子线程执行程序name
def run_py(self, name):
    # 两个参数初始化
    self.pb.setValue(0)  # 设置进度条为0
    self.is_done = 0  # 设置完成标记 完成/未完成 1/0
    self.thread_1 = Runthread(pro_name=name)
    self.thread_1.progressBarValue.connect(self.callback)
    self.thread_1.signal_done.connect(self.callback_done)
    self.thread_1.start()

# 回传进度条参数
def callback(self, i):
    self.pb.setValue(i)

# 回传结束信号
def callback_done(self, i):
    self.is_done = i
    if self.is_done == 1:
        self.messageDialog1()

# Runthread子线程
class Runthread(QThread):
    progressBarValue = pyqtSignal(int)  # 更新进度条
    signal_done = pyqtSignal(int)  # 是否结束信号

    def __init__(self, pro_name):
        super(Runthread, self).__init__()
        self.pro_name = pro_name

    def run(self):
        Linklist_sum = self.pro_name.get_crawler_link()
        length = len(Linklist_sum)
        for start in range(0, length, 10):
            self.pro_name.run_crawlertask(start, Linklist_sum)
            self.progressBarValue.emit(int(start / length * 100))  # 发送进度条的值信号
        self.signal_done.emit(1)  # 发送结束信号

按钮美化

甲方要求我做一个科技风格的按钮,然而没给我设计贴图,于是我采用QSS的qlineargradient实现渐变填充,先看效果。
常规状态:

鼠标悬浮状态:边框变红

鼠标按下状态:字体下沉

相关代码:

font = QtGui.QFont()
font.setFamily("方正粗黑宋简体")
font.setPointSize(18)
self.pushButton.setFont(font)
self.pushButton.setObjectName("pushButton")
self.pushButton.setStyleSheet("QPushButtonbackground:qlineargradient(spread:reflect, x1:0, y1:1, x2:0, y2:0, "
                             "stop:0 #0a4a83, stop:0.5 #186e99, stop:1 #239cd8);"
                             "border:2px solid qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0,"
                             "stop:0 #002aff, stop:0.5 #00aeff, stop:1 #00e6fc); border-radius:1px; "
                             "color:#d0f2f5 "
                             "QPushButton:hover:!pressed border:1px solid #f8878f;"
                             "QPushButton:pressed padding-left:6px;padding-top:6px;border:1px solid #f8878f;")

另外提供另一种美化示例:

相关代码

self.pushButton.setStyleSheet("QPushButtonbackground:qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, "
                              "stop:0 #00ffff, stop:0.5 #1D2B56, stop:1 "
                              "#00ffff);border:0px;border-radius:1px;color:white;")

选择框美化

顺带一提选择框美化,我的界面风格是暗黑系的,有一个开源的QSS风格qdarkstyle可以直接套用。

import qdarkstyle

self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())  # 设置暗黑风格

当然,也可以全局配置该风格。

app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

但是我觉得该风格的按钮设计没上面自己设计的靓眼,因此局部应用了该风格。

图片编码

如果在pyqt中的界面使用了贴图,在用pyinstaller打包成exe文件后,如果贴图和exe文件不在同一路径下,则会无法显示。
为了解决这一问题,可以使用pyqt自带的Pyqrc将图片资源编码成二进制数据,从而能够一起打包进exe文件。

首先建立文件img.qrc
将用到的图片写进去,比如,我用到了四张png图片。

<RCC>
  <qresource prefix="png">
    <file>images/title_bg.png</file>
    <file>images/button_bg.png</file>
    <file>images/bg.png</file>
    <file>images/2.png</file>
  </qresource>
</RCC>

之后,在pycharm中配置Pyqrc,配置方法可以参见这篇博客PyCharm中配置与PyQT5相关的External tools
然后,就能在pycharm中快速使用pyqrc进行转换。

转换之后,会生成img_rc.py文件。
在引用贴图的py文件中,引入该文件即可。

import img_rc

再次进行打包,生成的exe即包含图片信息。

开机自启动

exe文件完成了,甲方又给我提了最后一条需求,要求能够设置开机自启动。
这里,我提供两种方法。

添加注册表方法

第一种方式稍微复杂一些,通过python程序,将生成的exe添加到系统的启动注册表内。

import win32api
import win32con
import sys
import os

sys.setrecursionlimit(1000000)
name = 'auto_run'
cur_path = os.getcwd()
path = cur_path + '\\\\' '你的程序名.exe'
KeyName = r'Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run'

try:
    key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_ALL_ACCESS)
    win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, path)
    win32api.RegCloseKey(key)
except:
    print('error!')
print('success!')

运行之后输出success即添加成功。
打开系统注册表和任务管理器,可以看到添加的内容。

如果需要关闭,在任务管理器内设置禁用即可。

bat脚本方法

正常来说,上面那种方法能够实现开机自启动,但是如果exe有个功能是打开当前程序文件夹,该方法会出现问题。开机启动后,打开当前文件夹会诡异地定位到C盘的system32文件夹里。
因此,有了下面这个方式,不仅可以解决这个问题,并且更加方便。

Windows在C盘中提供了一个启动文件夹(win+R:输入shell:startup即可进入),程序放入该文件夹中后,开机就能自动启动程序。由于我的程序涉及打开当前文件夹的操作,因此不能直接将程序放进去,而是将程序的快捷方式放进去。

首先创建快捷方式,命名为"shortcut"。
然后创建bat脚本,输入

xcopy "shortcut.lnk" "C:\\Users\\%username%\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup" /Y

这里使用xcopy命令进行快捷方式的复制,其中/Y表示目标存在同名文件的情况取消提示以确认要覆盖。xcopy的更多参数设置可参考批处理(bat)xcopy详解
另外,创建快捷方式后的扩展名lnk不会直接显示,但在写脚本中需要进行补充。
如果快捷方式中有中文,需要将bat脚本内容改成ANSI编码,修改方式可参考Bat批处理命令执行中文路径方法

PyQt5:具有多处理功能的单个 QProgressBar 卡住了

】PyQt5:具有多处理功能的单个QProgressBar卡住了【英文标题】:PyQt5:singleQProgressBarwithmultiprocessinggetsstuck【发布时间】:2021-11-0109:23:47【问题描述】:我有一个很长的列表要处理,所以我使用多处理来加快处理速度。现在我想在PyQ... 查看详情

pyqt5-qtdesignerqprogressbar()进度条(代码片段)

QProgressBar()进度条QProgressBar简介QProgressBar小部件提供了一个水平或垂直的进度条。进度条用于向用户指示操作的进度,并向他们保证应用程序仍在运行。进度条使用steps的概念。您可以通过指定最小和最大可能的step值来设置它,... 查看详情

qt软件开发之基础控件--2.5.2qprogressbar进度条(代码片段)

2.5.2QProgressBar进度条 QT的QProgressBar类提供了一个水平和垂直样式的进度条。QProgressBar通常用来给用户显示一个操作的进度。比如:安装软件时显示的进度条、安装系统的进度条等等。    进度条使用步骤:可以设置它的... 查看详情

qt中可以用qprogressbar或着qprogressdialog来实现进度条

QProgressBar的使用首先在designer中拖一个按钮和进度条部件,按下面初始化 //补充:下面两句写在MainWindow的构造函数里进行初始化ui->progressBar->setRange(0,50000-1); ui->progressBar->setValue(0);  按钮的事件处理:voidMai... 查看详情

qt中如何改变qprogressbar中进度条的颜色?

...己会的,然后告诉我该怎么做,因为baidu和google上的关于QProgressBar的网页我几乎都浏览过了,没啥收获~我想要的是这样的结果:进度条的range是0~100然后当value在0~40的时候,进度条显示红色0~80的时候,进度条显示黄色80~100的时候... 查看详情

进度条qprogressbar(代码片段)

...mPyQt5.QtCoreimportQt,QTimer3fromPyQt5.QtWidgetsimportQApplication,QWidget,QProgressBar,QPushButton,QHBoxLayout,QVBoxLayout456classDemo(QWidget):7def__init__(self):8super(Demo,self).__init__()9self.progressbar=QProgressBar(self)#实例化进度条10#self.progressbar.setOrientation(Qt.Vertical)#设... 查看详情

如何在 QT 中获得具有圆形边缘和圆形进度边缘的 QProgressBar?

】如何在QT中获得具有圆形边缘和圆形进度边缘的QProgressBar?【英文标题】:HowtogetaQProgressBarinQTwithroundededgesandroundedprogressedges?【发布时间】:2016-03-1723:20:32【问题描述】:我创建了一个垂直进度条,并尝试使用圆角设置它的样... 查看详情

qprogressbar怎么显示读取文件进度

...己会的,然后告诉我该怎么做,因为baidu和google上的关于QProgressBar的网页我几乎都浏览过了,没啥收获~我想要的是这样的结果:进度条的range是0~100然后当value在0~40的时候,进度条显示红色0~80的时候,进 查看详情

27.qt-qprogressbar动态实现多彩进度条(详解)

#include"ProgressBar.h"ProgressBar::ProgressBar(constQString&fileName,QWidget*parent):QWidget(parent),m_bar(this),m_value(this),m_slider(this),m_image(fileName)m_bar.setMaximum(100);m_bar.setMini 查看详情

如何在 Ui_MainWindow 中更新 pyqt5 进度条

】如何在Ui_MainWindow中更新pyqt5进度条【英文标题】:HowdoIupdatepyqt5progressbarinUi_MainWindow【发布时间】:2018-03-3023:16:16【问题描述】:我尝试使用pyqt5实时更新进度条,但是,目前在线文档似乎非常有限。我在网上读到我可以使用线... 查看详情

Python 3.5,单独窗口中的 pyqt5 进度条 gui

】Python3.5,单独窗口中的pyqt5进度条gui【英文标题】:Python3.5,pyqt5progressbarguiinseperatewindow【发布时间】:2016-11-0423:23:09【问题描述】:我是PyQt5的新手,对Python还是很陌生。我正在尝试在Python3.5中使用PyQt5创建一个图形用户界面,... 查看详情

PyQT5 多线程问题

...使用PYQT5和Qthread运行多线程我有两个与线程关联的按钮(进度条和一个操作等待1秒,然后打印“完成”),它们在同一个类中声明时运行良好。我确实有第三个按钮,我链接到另一个类中插入的操作。这使我的程序在没有任何... 查看详情

PySide2 使用 QProgressBar 作为信号参数

】PySide2使用QProgressBar作为信号参数【英文标题】:PySide2useQProgressBarassignalargument【发布时间】:2020-02-2114:59:10【问题描述】:我正在尝试解决PySide2、QThread和Signal/Slot机制的问题。我想要实现的是更新通过信号/槽机制作为参考传... 查看详情

如何动态设置 QProgressBar 样式表?

】如何动态设置QProgressBar样式表?【英文标题】:HowtosetQProgressBarstylesheetdynamically?【发布时间】:2021-04-0517:45:19【问题描述】:我正在尝试制作一个进度条,它可以根据滑块的值动态更改部分颜色。我现在最接近的方法是使用:... 查看详情

在 QProgressBar 中显示进度

】在QProgressBar中显示进度【英文标题】:DisplayprogressinQProgressBar【发布时间】:2019-02-2610:43:24【问题描述】:我正在使用QProgress来显示读取配置文件的状态。我有一个类向主窗口发出信号以显示状态。目前,我可以显示状态,但... 查看详情

达到/设置某个值后如何重置 PYQT5 进度条?

】达到/设置某个值后如何重置PYQT5进度条?【英文标题】:HowtoresetaPYQT5progressbaronceacertainvaluehasbeenreached/set?【发布时间】:2020-07-2014:14:35【问题描述】:我目前正在创建将一些数据分析脚本联系在一起的基本GUI。这些脚本可能需... 查看详情

带有样式表的 QProgressBar

】带有样式表的QProgressBar【英文标题】:QProgressBarwithStyleSheet【发布时间】:2014-07-0714:36:26【问题描述】:我想要一个带有垂直(从上到下)文本的垂直进度条。我想知道是否以及如何使用样式表来实现这一点。我无法更改整个... 查看详情

根据从导入包中打印的标准输出更新 PyQt 进度条(PyQt5)

】根据从导入包中打印的标准输出更新PyQt进度条(PyQt5)【英文标题】:UpdatingPyQtprogressbarbasedonprintedstdoutfromimportedpackage(PyQt5)【发布时间】:2017-05-2005:57:43【问题描述】:我对PyQt5比较陌生,希望能获得任何指导以实现以下目标... 查看详情