使用 PyQt5 自定义开关按钮:设计美观实用的开关组件

在日常的 GUI 编程中,开关按钮 是一个非常实用的功能。在用 PyQt5 设计界面时,我需要一个美观且用户体验良好的开关按钮,但是在网络上查找时发现许多例子要么样式简单粗糙,要么相关内容非常少。于是,我决定自己动手,利用 PyQt5 制作一个更灵活、符合现代 UI 设计的开关组件。以下是我的实现过程,希望对大家有所帮助。

初衷与设计目标

我希望这个开关按钮能满足以下需求:

  1. 视觉效果:简洁、带有渐变和过度效果,看上去不“土”。
  2. 状态样式:可以在“开”和“关”状态间切换,按钮和滑块颜色也要随状态改变。
  3. 易用性:能灵活应用于PyQt5的其他窗口部件中。

经过多次尝试和调试,最终实现了一个拥有平滑动画过渡效果、样式现代的开关按钮。

效果图

实现细节

1. 基础组件搭建

首先创建了一个继承自 QWidgetSwitchButton 类,设置了自定义信号 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 项目增添一份视觉效果,也能帮助到对美观界面有要求的朋友们!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值