127 lines
3.6 KiB
Python
127 lines
3.6 KiB
Python
|
|
from PySide6.QtWidgets import (
|
|||
|
|
QAbstractButton,
|
|||
|
|
QApplication,
|
|||
|
|
QSizePolicy,
|
|||
|
|
QWidget,
|
|||
|
|
QVBoxLayout,
|
|||
|
|
)
|
|||
|
|
from PySide6.QtCore import Qt, QRect, Signal
|
|||
|
|
from PySide6.QtGui import QPainter, QBrush
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SwitchButton(QAbstractButton):
|
|||
|
|
# 开关切换信号
|
|||
|
|
# 开: True, 关: False
|
|||
|
|
switched = Signal(bool)
|
|||
|
|
|
|||
|
|
def __init__(self, parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self._checked = False
|
|||
|
|
|
|||
|
|
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
|||
|
|
|
|||
|
|
# 颜色映射
|
|||
|
|
self.color_keymap = {
|
|||
|
|
"slider": "#16ffff",
|
|||
|
|
"text": Qt.GlobalColor.white,
|
|||
|
|
"on_bg": "#008ee8", # 开的时候的背景颜色
|
|||
|
|
"off_bg": "#001c83", # 关的时候的背景颜色
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self.clicked.connect(self.onClicked)
|
|||
|
|
self._init_style() # 新尺寸的样式表
|
|||
|
|
self._init_size_policy()
|
|||
|
|
|
|||
|
|
def _init_style(self):
|
|||
|
|
self.setStyleSheet(
|
|||
|
|
"""
|
|||
|
|
SwitchButton {
|
|||
|
|
font-family: "Microsoft YaHei";
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: white;
|
|||
|
|
border-radius: 9px;
|
|||
|
|
margin: 2px;
|
|||
|
|
min-width: 39px;
|
|||
|
|
max-width: 39px;
|
|||
|
|
min-height: 18px;
|
|||
|
|
max-height: 18px;
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def _init_size_policy(self):
|
|||
|
|
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
|||
|
|
|
|||
|
|
def paintEvent(self, event):
|
|||
|
|
# 1. 调整滑块间距
|
|||
|
|
slider_space = 1 # 滑块与背景的间距(上下左右各1px)
|
|||
|
|
painter = QPainter(self)
|
|||
|
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 2. 绘制背景
|
|||
|
|
background_rect = self.rect()
|
|||
|
|
painter.setPen(Qt.PenStyle.NoPen)
|
|||
|
|
bg_color = (
|
|||
|
|
self.color_keymap["on_bg"]
|
|||
|
|
if self._checked
|
|||
|
|
else self.color_keymap["off_bg"]
|
|||
|
|
)
|
|||
|
|
painter.setBrush(QBrush(bg_color))
|
|||
|
|
painter.drawRoundedRect(
|
|||
|
|
background_rect, self.height() / 2, self.height() / 2
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 3. 计算新滑块尺寸
|
|||
|
|
slider_width = self.height() - slider_space * 2
|
|||
|
|
|
|||
|
|
# 4. 计算新滑块位置
|
|||
|
|
if self._checked:
|
|||
|
|
slider_x = self.width() - slider_width - slider_space
|
|||
|
|
else:
|
|||
|
|
slider_x = slider_space
|
|||
|
|
slider_y = slider_space
|
|||
|
|
|
|||
|
|
# 5. 绘制滑块
|
|||
|
|
slider_rect = QRect(slider_x, slider_y, slider_width, slider_width)
|
|||
|
|
painter.setBrush(QBrush(self.color_keymap["slider"]))
|
|||
|
|
painter.drawEllipse(slider_rect)
|
|||
|
|
finally:
|
|||
|
|
painter.end()
|
|||
|
|
|
|||
|
|
def onClicked(self):
|
|||
|
|
self._checked = not self._checked
|
|||
|
|
self.update()
|
|||
|
|
self.switched.emit(self._checked)
|
|||
|
|
|
|||
|
|
def setChecked(self, checked: bool):
|
|||
|
|
self._checked = checked
|
|||
|
|
self.update()
|
|||
|
|
self.switched.emit(self._checked)
|
|||
|
|
|
|||
|
|
def isChecked(self) -> bool:
|
|||
|
|
return self._checked
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 测试示例
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
app = QApplication(sys.argv)
|
|||
|
|
|
|||
|
|
test_window = QWidget()
|
|||
|
|
test_window.setWindowTitle("开关测试")
|
|||
|
|
test_window.resize(200, 100)
|
|||
|
|
|
|||
|
|
layout = QVBoxLayout(test_window)
|
|||
|
|
layout.setContentsMargins(50, 30, 50, 30)
|
|||
|
|
|
|||
|
|
switch = SwitchButton()
|
|||
|
|
switch.switched.connect(
|
|||
|
|
lambda state: print(f"开关状态:{'选中' if state else '未选中'}")
|
|||
|
|
)
|
|||
|
|
layout.addWidget(switch)
|
|||
|
|
|
|||
|
|
test_window.show()
|
|||
|
|
sys.exit(app.exec())
|