🐟🐟🐟 通过自定义QPushButton实现进度条按钮功能

实现方法

  • 基本思路

要想实现在QPushButton中带有进度条的功能,毫无疑问是需要对QPushButton进行自定义操作的。其次就是对其中的paintEvent的方法进行覆写。这个带进度条的按钮总共可以分为三个部分来进行绘制,一是进度条区域、二是文本区域、三是图标区域。三者间的关系是进度条区域为最下层(先绘制),文本和图标区域在进度条区域的上面。其中进度条实现的原理,此次demo将通过定时器来改变进度条的宽度模拟进度条效果。后续可以根据实际场景通过信号传输等方式来实现实际功能。

  • 参数初始化

首先是一些参数的初始化,其中numpercent是用于进度条进度的控制。painter_size用于设置图标和文本的字体大小,pos_xpos_y用于设置进度条绘制的起点来改变按钮和进度条之间的间距

  1. class ProgressButton(QPushButton):
  2. """
  3. 用于HomeWin self.btn_FT ,self.btn_PT
  4. """
  5. demoSignal = Signal()
  6. def __init__(self, *args, **kwargs):
  7. super(ProgressButton, self).__init__(*args, **kwargs)
  8. self.num = 0 # 进度条初始进度
  9. self.painter_size = 15 # 图标和字体的大小
  10. self.percent = 10 # 进度条前进进度
  11. self.text = "" # 进度条文本
  12. self.text_width = 0 # 进度条文本实际宽度
  13. self.pos_x = 5 # 进度条x轴
  14. self.pos_y = 9 # 进度条y轴
  15. self.timer = QTimer() # 创建定时器
  • 覆写paintEvent事件

参数初始化后就开始对paintEvent事件进行覆写,其中绘制的顺序为(进度条->文本->图标),在图标绘制中需要注意的事,因为文本的绘制是采用Qt.AlignCenter进行居中的绘制的,所以在进行图标位置的x轴起点定位时需要通过fontMetrics().horizontalAdvance(str)的方法来确定文本的宽度,以此来定位图标绘制的位置防止图标和文本重叠

  1. def paintEvent(self, event):
  2. super().paintEvent(event)
  3. if self.num == 0:
  4. self.text = "1000个文件正在传输"
  5. self.timer.start(1000)
  6. painter = QPainter(self)
  7. brush = QBrush(QColor(71, 102, 45, 255), Qt.SolidPattern)
  8. painter.setBrush(brush)
  9. # 绘制进度条
  10. rect_progress = QRect(self.pos_x, self.pos_y,
  11. self.num, self.height() - self.pos_y * 2) # 进度条区域
  12. painter.setPen(Qt.NoPen)
  13. painter.drawRect(rect_progress)
  14. # 绘制文字
  15. rect_text = QRect(self.pos_x, self.pos_y, self.width() - self.pos_x * 2,
  16. self.height() - self.pos_y * 2) # 文本区域
  17. painter.setPen(QColor(167, 216, 169))
  18. painter.setFont(QFont("SourceHanSansSC-Regular, SourceHanSansSC",
  19. self.painter_size))
  20. painter.drawText(rect_text, Qt.AlignCenter, self.text)
  21. # 绘制图标
  22. fm = painter.fontMetrics()
  23. self.text_width = fm.horizontalAdvance(self.text)
  24. rect_icon = QRect(self.width() // 2 - self.text_width // 2 - self.painter_size,
  25. self.height() // 2 - self.painter_size // 2,
  26. self.painter_size, self.painter_size) # 图标区域
  27. painter.drawImage(rect_icon, "icon.png")

效果展示

🍙 QPushButton 实现进度条功能 - 图1

完整代码

  1. from PySide2.QtCore import QRect, Qt, Signal, QTimer
  2. from PySide2.QtGui import QPainter, QBrush, QColor, QFont
  3. from PySide2.QtWidgets import *
  4. class ProgressButton(QPushButton):
  5. """
  6. 用于HomeWin self.btn_FT ,self.btn_PT
  7. """
  8. demoSignal = Signal()
  9. def __init__(self, *args, **kwargs):
  10. super(ProgressButton, self).__init__(*args, **kwargs)
  11. self.num = 0
  12. self.painter_size = 15
  13. self.percent = 10
  14. self.text = "" # 进度条文本
  15. self.text_width = 0 # 进度条文本实际宽度
  16. self.pos_x = 5 # 进度条x轴
  17. self.pos_y = 9 # 进度条y轴
  18. self.timer = QTimer() # 创建定时器
  19. self.timer.timeout.connect(self.emitDemoSignal)
  20. self.demoSignal.connect(self.updateProgressBar)
  21. def paintEvent(self, event):
  22. super().paintEvent(event)
  23. if self.num == 0:
  24. self.text = "1000个文件正在传输"
  25. self.timer.start(1000)
  26. painter = QPainter(self)
  27. brush = QBrush(QColor(71, 102, 45, 255), Qt.SolidPattern)
  28. painter.setBrush(brush)
  29. # 绘制进度条
  30. rect_progress = QRect(self.pos_x, self.pos_y,
  31. self.num, self.height() - self.pos_y * 2) # 进度条区域
  32. painter.setPen(Qt.NoPen)
  33. painter.drawRect(rect_progress)
  34. # 绘制文字
  35. rect_text = QRect(self.pos_x, self.pos_y, self.width() - self.pos_x * 2,
  36. self.height() - self.pos_y * 2) # 文本区域
  37. painter.setPen(QColor(167, 216, 169))
  38. painter.setFont(QFont("SourceHanSansSC-Regular, SourceHanSansSC",
  39. self.painter_size))
  40. painter.drawText(rect_text, Qt.AlignCenter, self.text)
  41. # 绘制图标
  42. fm = painter.fontMetrics()
  43. self.text_width = fm.horizontalAdvance(self.text)
  44. rect_icon = QRect(self.width() // 2 - self.text_width // 2 - self.painter_size,
  45. self.height() // 2 - self.painter_size // 2,
  46. self.painter_size, self.painter_size) # 图标区域
  47. painter.drawImage(rect_icon, "icon.png")
  48. # 触发demoSignal
  49. def emitDemoSignal(self):
  50. self.demoSignal.emit()
  51. # 更新进度条
  52. def updateProgressBar(self):
  53. self.percent = (self.width() - self.pos_x * 2) / 20
  54. total = self.width() - self.pos_x * 2
  55. if self.num < total:
  56. if self.num + self.percent >= total:
  57. self.num = total
  58. self.text = "完成上传"
  59. self.timer.stop()
  60. else:
  61. self.num += self.percent
  62. self.update()
  63. # 更新字体以及图标大小
  64. def setPainterSize(self, size: int):
  65. self.painter_size = size
  66. self.update()
  67. class Window(QWidget):
  68. def __init__(self):
  69. super().__init__()
  70. self.setWindowTitle("demo")
  71. self.resize(800, 800)
  72. self.setup_ui()
  73. def setup_ui(self):
  74. self.red_btn = ProgressButton(self)
  75. self.red_btn.resize(400, 80)
  76. self.red_btn.setPainterSize(20)
  77. self.red_btn.setStyleSheet("""
  78. border-color: transparents;
  79. """)
  80. self.red_btn.clicked.connect(self.restartProgress)
  81. def restartProgress(self):
  82. self.red_btn.num = 0
  83. if __name__ == '__main__':
  84. import sys
  85. app = QApplication(sys.argv)
  86. window = Window()
  87. window.show()
  88. sys.exit(app.exec_())