Qt第四十二章:自定义按钮

Love The Way You Lie 2024-03-30 08:59 203阅读 0赞

目标:完全自定义一个开关按钮

涉及知识点:QPainter绘制、paintEvent事件、QPoint是否坐落在区域内、pyside的Property类、QPropertyAnimation动画。

代码

  1. import PySide6
  2. from PySide6.QtCore import QRect, QSize, QTimer, QPoint, Qt, QPropertyAnimation, Property, PyClassProperty, \
  3. QMetaProperty, Signal, QEasingCurve
  4. from PySide6.QtGui import QPixmap, QCursor, QIcon, QMouseEvent, QResizeEvent, QColor, QPainter, QPainterPath, QBrush, \
  5. QGradient, QImage
  6. from PySide6.QtWidgets import QApplication, QDialog, QPushButton, QHBoxLayout, QLabel, QSizePolicy, QWidget, \
  7. QVBoxLayout, QSpacerItem, QGraphicsDropShadowEffect
  8. # 自定义开关按钮
  9. class SwitchButton(QWidget):
  10. # 转换信号
  11. change_signal = Signal(bool)
  12. def set_status(self):
  13. self.status_init = not self.status_init
  14. # 立即重绘
  15. self.repaint()
  16. # 下次处理事件时重绘(推荐)
  17. self.update()
  18. # 动画(必须声明为QWidget中的某个属性)
  19. self.animation_position = QPropertyAnimation(self, b'position')
  20. self.animation_color = QPropertyAnimation(self, b'color')
  21. # 持续时间
  22. self.animation_position.setDuration(self.slip_time)
  23. self.animation_color.setDuration(self.slip_time)
  24. # 速度曲线(弹性效果)
  25. # self.animation_position.setEasingCurve(QEasingCurve.InOutElastic)
  26. # 重复次数
  27. self.animation_position.setLoopCount(1)
  28. self.animation_color.setLoopCount(1)
  29. if self.status_init:
  30. # 开始时属性值(此时的位置)
  31. self.animation_position.setStartValue(self.position_init)
  32. # 结束时属性值
  33. self.animation_position.setEndValue(self.width() - self.height() / 2)
  34. # 开始时属性值(此时的颜色)
  35. self.animation_color.setStartValue(self.color_init)
  36. # 结束时属性值
  37. self.animation_color.setEndValue(self.open_color)
  38. else:
  39. # 开始时属性值(此时的位置)
  40. self.animation_position.setStartValue(self.position_init)
  41. # 结束时属性值
  42. self.animation_position.setEndValue(self.height() / 2)
  43. # 开始时属性值(此时的颜色)
  44. self.animation_color.setStartValue(self.color_init)
  45. # 结束时属性值
  46. self.animation_color.setEndValue(self.close_color)
  47. # 开始
  48. self.animation_position.start()
  49. self.animation_color.start()
  50. self.change_signal.emit(self.status_init)
  51. def get_status(self):
  52. return self.status_init
  53. def set_color(self, color: QColor):
  54. self.color_init = color
  55. def get_color(self):
  56. return self.color_init
  57. def set_position(self, position: int):
  58. self.position_init = position
  59. self.update()
  60. def get_position(self):
  61. return self.position_init
  62. # 开关状态
  63. status = Property(type=int, fset=set_status, fget=get_status, notify=change_signal)
  64. # 组件颜色
  65. color = Property(type=QColor, fset=set_color, fget=get_color)
  66. # 开关位置
  67. position = Property(type=int, fset=set_position, fget=get_position)
  68. def __init__(self, parent=None,
  69. open_color: QColor = QColor(64, 158, 255, 255),
  70. close_color: QColor = QColor(220, 223, 230, 255),
  71. btn_color: QColor = QColor(255, 255, 255, 255),
  72. slip_time: int = 200):
  73. """
  74. :param parent:
  75. :param open_color: 打开时的颜色
  76. :param close_color: 关闭时的颜色
  77. :param btn_color: 按钮颜色
  78. :param slip_time: 滑动时间(ms)
  79. """
  80. super(SwitchButton, self).__init__(parent)
  81. self.resize(200, 100)
  82. # 记录鼠标按下时的位置
  83. self.press_pos = QPoint(0, 0)
  84. # 当前状态
  85. self.status_init: bool = False
  86. # 当前颜色
  87. self.color_init: QColor = close_color
  88. # 当前位置
  89. self.position_init: int = int(self.height() / 2)
  90. # 打开时的颜色
  91. self.open_color = open_color
  92. # 关闭时的颜色
  93. self.close_color = close_color
  94. # 圆形按钮的颜色
  95. self.btn_color = btn_color
  96. self.slip_time = slip_time
  97. def mousePressEvent(self, event: PySide6.QtGui.QMouseEvent) -> None:
  98. # 记录按下的坐标
  99. self.press_pos = event.scenePosition().toPoint()
  100. super().mousePressEvent(event)
  101. def mouseReleaseEvent(self, event: PySide6.QtGui.QMouseEvent) -> None:
  102. # 释放时的坐标
  103. release_point = event.scenePosition().toPoint()
  104. # 圆心坐标
  105. if self.parent() is None:
  106. point = QPoint(self.position_init, 50)
  107. else:
  108. point = QPoint(self.pos().x() + self.position_init, self.pos().y() + 50)
  109. # 是否在圆内
  110. inner_circle = (release_point.x() - point.x()) ** 2 + (release_point.y() - point.y()) ** 2 < 45 ** 2
  111. # 鼠标按下和释放在同一位置,且点击位置在圆内
  112. if release_point == self.press_pos and inner_circle:
  113. self.set_status()
  114. super().mousePressEvent(event)
  115. # 绘图事件
  116. def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
  117. painter = QPainter(self)
  118. # 反锯齿
  119. painter.setRenderHint(QPainter.Antialiasing)
  120. painter.begin(self)
  121. # 绘制一个圆角矩形
  122. painter.setBrush(self.color_init)
  123. path = QPainterPath()
  124. path.addRoundedRect(QRect(0, 0, self.width(), self.height()), self.height() / 2, self.height() / 2,
  125. Qt.AbsoluteSize)
  126. painter.drawPath(path)
  127. # 绘制白色圆形
  128. painter.setBrush(self.btn_color)
  129. painter.drawEllipse(QPoint(self.position_init, 50), 45, 45)
  130. painter.end()
  131. super().paintEvent(event)

使用

  1. class ExampleWidget(QWidget):
  2. def __init__(self, parent=None):
  3. super(ExampleWidget, self).__init__(parent)
  4. switch_button = SwitchButton(self, open_color=QColor(19, 206, 102), close_color=QColor(255, 73, 73, 255), )
  5. switch_button.change_signal.connect(lambda x: print('打开') if x else print("关闭"))
  6. if __name__ == '__main__':
  7. app = QApplication([])
  8. main = ExampleWidget()
  9. main.show()
  10. app.exec()

3a119078df8c42a997a8af8f6523f0fd.pngc5ee9bdb93dd4f83a059d2408fca181e.png

发表评论

表情:
评论列表 (有 0 条评论,203人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Qt:QML之Item

    Item是Qt Quick中所有可视组件的基本类型,Qt Quick中的所有可视组件都继承于Item。它定义了所有可视组件的通用属性,如坐标、宽高、锚定关系、事件处理等等。