🐟🐟🐟 通过自定义QPushButton实现进度条按钮功能
实现方法
- 基本思路
要想实现在QPushButton中带有进度条的功能,毫无疑问是需要对QPushButton进行自定义操作的。其次就是对其中的paintEvent的方法进行覆写。这个带进度条的按钮总共可以分为三个部分来进行绘制,一是进度条区域、二是文本区域、三是图标区域。三者间的关系是进度条区域为最下层(先绘制),文本和图标区域在进度条区域的上面。其中进度条实现的原理,此次demo将通过定时器来改变进度条的宽度模拟进度条效果。后续可以根据实际场景通过信号传输等方式来实现实际功能。
- 参数初始化
首先是一些参数的初始化,其中num,percent是用于进度条进度的控制。painter_size用于设置图标和文本的字体大小,pos_x,pos_y用于设置进度条绘制的起点来改变按钮和进度条之间的间距
class ProgressButton(QPushButton):"""用于HomeWin self.btn_FT ,self.btn_PT"""demoSignal = Signal()def __init__(self, *args, **kwargs):super(ProgressButton, self).__init__(*args, **kwargs)self.num = 0 # 进度条初始进度self.painter_size = 15 # 图标和字体的大小self.percent = 10 # 进度条前进进度self.text = "" # 进度条文本self.text_width = 0 # 进度条文本实际宽度self.pos_x = 5 # 进度条x轴self.pos_y = 9 # 进度条y轴self.timer = QTimer() # 创建定时器
- 覆写paintEvent事件
参数初始化后就开始对paintEvent事件进行覆写,其中绘制的顺序为(进度条->文本->图标),在图标绘制中需要注意的事,因为文本的绘制是采用Qt.AlignCenter进行居中的绘制的,所以在进行图标位置的x轴起点定位时需要通过fontMetrics().horizontalAdvance(str)的方法来确定文本的宽度,以此来定位图标绘制的位置防止图标和文本重叠
def paintEvent(self, event):super().paintEvent(event)if self.num == 0:self.text = "1000个文件正在传输"self.timer.start(1000)painter = QPainter(self)brush = QBrush(QColor(71, 102, 45, 255), Qt.SolidPattern)painter.setBrush(brush)# 绘制进度条rect_progress = QRect(self.pos_x, self.pos_y,self.num, self.height() - self.pos_y * 2) # 进度条区域painter.setPen(Qt.NoPen)painter.drawRect(rect_progress)# 绘制文字rect_text = QRect(self.pos_x, self.pos_y, self.width() - self.pos_x * 2,self.height() - self.pos_y * 2) # 文本区域painter.setPen(QColor(167, 216, 169))painter.setFont(QFont("SourceHanSansSC-Regular, SourceHanSansSC",self.painter_size))painter.drawText(rect_text, Qt.AlignCenter, self.text)# 绘制图标fm = painter.fontMetrics()self.text_width = fm.horizontalAdvance(self.text)rect_icon = QRect(self.width() // 2 - self.text_width // 2 - self.painter_size,self.height() // 2 - self.painter_size // 2,self.painter_size, self.painter_size) # 图标区域painter.drawImage(rect_icon, "icon.png")
效果展示

完整代码
from PySide2.QtCore import QRect, Qt, Signal, QTimerfrom PySide2.QtGui import QPainter, QBrush, QColor, QFontfrom PySide2.QtWidgets import *class ProgressButton(QPushButton):"""用于HomeWin self.btn_FT ,self.btn_PT"""demoSignal = Signal()def __init__(self, *args, **kwargs):super(ProgressButton, self).__init__(*args, **kwargs)self.num = 0self.painter_size = 15self.percent = 10self.text = "" # 进度条文本self.text_width = 0 # 进度条文本实际宽度self.pos_x = 5 # 进度条x轴self.pos_y = 9 # 进度条y轴self.timer = QTimer() # 创建定时器self.timer.timeout.connect(self.emitDemoSignal)self.demoSignal.connect(self.updateProgressBar)def paintEvent(self, event):super().paintEvent(event)if self.num == 0:self.text = "1000个文件正在传输"self.timer.start(1000)painter = QPainter(self)brush = QBrush(QColor(71, 102, 45, 255), Qt.SolidPattern)painter.setBrush(brush)# 绘制进度条rect_progress = QRect(self.pos_x, self.pos_y,self.num, self.height() - self.pos_y * 2) # 进度条区域painter.setPen(Qt.NoPen)painter.drawRect(rect_progress)# 绘制文字rect_text = QRect(self.pos_x, self.pos_y, self.width() - self.pos_x * 2,self.height() - self.pos_y * 2) # 文本区域painter.setPen(QColor(167, 216, 169))painter.setFont(QFont("SourceHanSansSC-Regular, SourceHanSansSC",self.painter_size))painter.drawText(rect_text, Qt.AlignCenter, self.text)# 绘制图标fm = painter.fontMetrics()self.text_width = fm.horizontalAdvance(self.text)rect_icon = QRect(self.width() // 2 - self.text_width // 2 - self.painter_size,self.height() // 2 - self.painter_size // 2,self.painter_size, self.painter_size) # 图标区域painter.drawImage(rect_icon, "icon.png")# 触发demoSignaldef emitDemoSignal(self):self.demoSignal.emit()# 更新进度条def updateProgressBar(self):self.percent = (self.width() - self.pos_x * 2) / 20total = self.width() - self.pos_x * 2if self.num < total:if self.num + self.percent >= total:self.num = totalself.text = "完成上传"self.timer.stop()else:self.num += self.percentself.update()# 更新字体以及图标大小def setPainterSize(self, size: int):self.painter_size = sizeself.update()class Window(QWidget):def __init__(self):super().__init__()self.setWindowTitle("demo")self.resize(800, 800)self.setup_ui()def setup_ui(self):self.red_btn = ProgressButton(self)self.red_btn.resize(400, 80)self.red_btn.setPainterSize(20)self.red_btn.setStyleSheet("""border-color: transparents;""")self.red_btn.clicked.connect(self.restartProgress)def restartProgress(self):self.red_btn.num = 0if __name__ == '__main__':import sysapp = QApplication(sys.argv)window = Window()window.show()sys.exit(app.exec_())
