在日常的 GUI 编程中,开关按钮 是一个非常实用的功能。在用 PyQt5 设计界面时,我需要一个美观且用户体验良好的开关按钮,但是在网络上查找时发现许多例子要么样式简单粗糙,要么相关内容非常少。于是,我决定自己动手,利用 PyQt5 制作一个更灵活、符合现代 UI 设计的开关组件。以下是我的实现过程,希望对大家有所帮助。
初衷与设计目标
我希望这个开关按钮能满足以下需求:
- 视觉效果:简洁、带有渐变和过度效果,看上去不“土”。
- 状态样式:可以在“开”和“关”状态间切换,按钮和滑块颜色也要随状态改变。
- 易用性:能灵活应用于PyQt5的其他窗口部件中。
经过多次尝试和调试,最终实现了一个拥有平滑动画过渡效果、样式现代的开关按钮。
效果图
实现细节
1. 基础组件搭建
首先创建了一个继承自 QWidget
的 SwitchButton
类,设置了自定义信号 stateChanged
,用于在按钮状态变化时通知主窗口组件。同时,通过 paintEvent
方法自定义了绘制逻辑,使开关按钮在“开”与“关”状态切换时显示不同的背景和滑块位置。
2. 样式优化
为了实现悬停效果和状态切换后的不同样式,我在 paintEvent
中动态调整了按钮背景、边框和滑块颜色。例如:
- 在“关”状态下,背景是浅灰色,滑块颜色是深灰色,而当用户悬停时,背景变为略深的灰色。
- 在“开”状态下,背景变成绿色,滑块颜色是白色,并且在悬停时进一步加深,给用户更直观的视觉反馈。
如果颜色、滑动速度、大小等等不喜欢,大家都可以自己去更改~
3. 动画效果
开关按钮的滑块切换使用了 PyQt5 的 QPropertyAnimation
类,通过调整滑块的 indicator_pos
属性实现平滑过渡效果。这种动画过渡可以避免按钮看起来突兀,提升整体用户体验。
代码实现
以下是完整代码,包含了自定义开关组件 SwitchButton
类
class SwitchButton(QtWidgets.QWidget):
stateChanged = QtCore.pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedSize(60, 30)
self.isChecked = False
self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
# 初始化滑块的位置属性,用于动画
self.indicator_pos = 3
self.animation = QtCore.QPropertyAnimation(self, b"indicator_pos")
self.animation.setDuration(200) # 动画时长 200ms
self.updateStyleSheet()
# 属性用于动画
@QtCore.pyqtProperty(int)
def indicator_pos(self):
return self._indicator_pos
@indicator_pos.setter
def indicator_pos(self, pos):
self._indicator_pos = pos
self.update()
def updateStyleSheet(self):
# 设置不同状态的样式
if self.isChecked:
self.setStyleSheet("""
background-color: #009faa;
border-radius: 15px;
border: 2px solid #009faa;
""")
else:
self.setStyleSheet("""
background-color: #f1f3f6;
border-radius: 15px;
border: 2px solid #5e5e60;
""")
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# 绘制背景,根据不同状态和鼠标悬停改变颜色
rect = self.rect()
if self.isChecked:
background_color = "#009faa" if not self.underMouse() else "#00a7b3"
border_color = "#009faa"
else:
background_color = "#e3e5e8" if self.underMouse() else "#f1f3f6"
border_color = "#5e5e60"
painter.setBrush(QtGui.QColor(background_color))
painter.setPen(QtGui.QPen(QtGui.QColor(border_color), 2))
painter.drawRoundedRect(rect, 15, 15)
# 绘制滑块
indicator_rect = QtCore.QRect(self.indicator_pos, 3, 22, 22)
indicator_color = "#ffffff" if self.isChecked else "#5e5e60"
painter.setBrush(QtGui.QColor(indicator_color))
painter.setPen(QtCore.Qt.NoPen)
painter.drawEllipse(indicator_rect)
def enterEvent(self, event):
self.update() # 鼠标进入时更新样式
super().enterEvent(event)
def leaveEvent(self, event):
self.update() # 鼠标离开时更新样式
super().leaveEvent(event)
def mousePressEvent(self, event):
# 切换状态
self.isChecked = not self.isChecked
self.updateStyleSheet()
# 设置滑块动画目标位置
target_pos = 34 if self.isChecked else 3
self.animation.setEndValue(target_pos)
self.animation.start()
# 发出信号通知状态改变
self.stateChanged.emit(self.isChecked)
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(450, 350)
self.SwitchButton = SwitchButton(Form)
self.SwitchButton.setGeometry(QtCore.QRect(310, 20, 60, 30))
self.SwitchButton.stateChanged.connect(self.on_switch_clicked)
# 调整标签宽度,确保“开”和“关”完整显示
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(380, 20, 40, 30)) # 增加标签宽度
self.label.setAlignment(QtCore.Qt.AlignCenter) # 居中显示文字
self.label.setText("关")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
def on_switch_clicked(self, state):
if state:
self.label.setText("开")
print("开关已打开")
else:
self.label.setText("关")
print("开关已关闭")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
总结
通过自定义 SwitchButton
组件,我成功实现了一个拥有现代风格、动画效果和用户交互优化的开关按钮。希望这个小组件能为你的 PyQt5 项目增添一份视觉效果,也能帮助到对美观界面有要求的朋友们!