initial fluent-widgets ui
This commit is contained in:
312
qfluentwidgets/components/widgets/switch_button.py
Normal file
312
qfluentwidgets/components/widgets/switch_button.py
Normal file
@ -0,0 +1,312 @@
|
||||
# coding: utf-8
|
||||
from enum import Enum
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer, Property, Signal, QEvent, QPoint, QPropertyAnimation, QEasingCurve
|
||||
from PySide6.QtGui import QColor, QPainter, QHoverEvent
|
||||
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QToolButton, QWidget
|
||||
|
||||
from ...common.style_sheet import FluentStyleSheet, themeColor, ThemeColor, isDarkTheme, setCustomStyleSheet
|
||||
from ...common.overload import singledispatchmethod
|
||||
from ...common.color import fallbackThemeColor, validColor
|
||||
from .button import ToolButton
|
||||
|
||||
|
||||
class Indicator(ToolButton):
|
||||
""" Indicator of switch button """
|
||||
|
||||
checkedChanged = Signal(bool)
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent=parent)
|
||||
self.setCheckable(True)
|
||||
self.setFixedSize(42, 22)
|
||||
self.lightCheckedColor = QColor()
|
||||
self.darkCheckedColor = QColor()
|
||||
|
||||
self._sliderX = 5
|
||||
self.slideAni = QPropertyAnimation(self, b'sliderX', self)
|
||||
self.slideAni.setDuration(120)
|
||||
|
||||
self.toggled.connect(self._toggleSlider)
|
||||
|
||||
def mouseReleaseEvent(self, e):
|
||||
""" toggle checked state when mouse release"""
|
||||
super().mouseReleaseEvent(e)
|
||||
self.checkedChanged.emit(self.isChecked())
|
||||
|
||||
def _toggleSlider(self):
|
||||
self.slideAni.setEndValue(25 if self.isChecked() else 5)
|
||||
self.slideAni.start()
|
||||
|
||||
def toggle(self):
|
||||
self.setChecked(not self.isChecked())
|
||||
|
||||
def setDown(self, isDown: bool):
|
||||
self.isPressed = isDown
|
||||
super().setDown(isDown)
|
||||
|
||||
def setHover(self, isHover: bool):
|
||||
self.isHover = isHover
|
||||
self.update()
|
||||
|
||||
def setCheckedColor(self, light, dark):
|
||||
self.lightCheckedColor = QColor(light)
|
||||
self.darkCheckedColor = QColor(dark)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
""" paint indicator """
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHints(QPainter.Antialiasing)
|
||||
self._drawBackground(painter)
|
||||
self._drawCircle(painter)
|
||||
|
||||
def _drawBackground(self, painter: QPainter):
|
||||
r = self.height() / 2
|
||||
painter.setPen(self._borderColor())
|
||||
painter.setBrush(self._backgroundColor())
|
||||
painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), r, r)
|
||||
|
||||
def _drawCircle(self, painter: QPainter):
|
||||
painter.setPen(Qt.NoPen)
|
||||
painter.setBrush(self._sliderColor())
|
||||
painter.drawEllipse(int(self.sliderX), 5, 12, 12)
|
||||
|
||||
def _backgroundColor(self):
|
||||
isDark = isDarkTheme()
|
||||
|
||||
if self.isChecked():
|
||||
color = self.darkCheckedColor if isDark else self.lightCheckedColor
|
||||
if not self.isEnabled():
|
||||
return QColor(255, 255, 255, 41) if isDark else QColor(0, 0, 0, 56)
|
||||
if self.isPressed:
|
||||
return validColor(color, ThemeColor.LIGHT_2.color())
|
||||
elif self.isHover:
|
||||
return validColor(color, ThemeColor.LIGHT_1.color())
|
||||
|
||||
return fallbackThemeColor(color)
|
||||
else:
|
||||
if not self.isEnabled():
|
||||
return QColor(0, 0, 0, 0)
|
||||
if self.isPressed:
|
||||
return QColor(255, 255, 255, 18) if isDark else QColor(0, 0, 0, 23)
|
||||
elif self.isHover:
|
||||
return QColor(255, 255, 255, 10) if isDark else QColor(0, 0, 0, 15)
|
||||
|
||||
return QColor(0, 0, 0, 0)
|
||||
|
||||
def _borderColor(self):
|
||||
isDark = isDarkTheme()
|
||||
|
||||
if self.isChecked():
|
||||
return self._backgroundColor() if self.isEnabled() else QColor(0, 0, 0, 0)
|
||||
else:
|
||||
if self.isEnabled():
|
||||
return QColor(255, 255, 255, 153) if isDark else QColor(0, 0, 0, 133)
|
||||
|
||||
return QColor(255, 255, 255, 41) if isDark else QColor(0, 0, 0, 56)
|
||||
|
||||
def _sliderColor(self):
|
||||
isDark = isDarkTheme()
|
||||
|
||||
if self.isChecked():
|
||||
if self.isEnabled():
|
||||
return QColor(Qt.black if isDark else Qt.white)
|
||||
|
||||
return QColor(255, 255, 255, 77) if isDark else QColor(255, 255, 255)
|
||||
else:
|
||||
if self.isEnabled():
|
||||
return QColor(255, 255, 255, 201) if isDark else QColor(0, 0, 0, 156)
|
||||
|
||||
return QColor(255, 255, 255, 96) if isDark else QColor(0, 0, 0, 91)
|
||||
|
||||
def getSliderX(self):
|
||||
return self._sliderX
|
||||
|
||||
def setSliderX(self, x):
|
||||
self._sliderX = max(x, 5)
|
||||
self.update()
|
||||
|
||||
sliderX = Property(float, getSliderX, setSliderX)
|
||||
|
||||
|
||||
class IndicatorPosition(Enum):
|
||||
""" Indicator position """
|
||||
LEFT = 0
|
||||
RIGHT = 1
|
||||
|
||||
|
||||
class SwitchButton(QWidget):
|
||||
""" Switch button class
|
||||
|
||||
Constructors
|
||||
------------
|
||||
* SwitchButton(`parent`: QWidget = None)
|
||||
* SwitchButton(`text`: str = "Off", `parent`: QWidget = None, `indicatorPos`=IndicatorPosition.LEFT)
|
||||
"""
|
||||
|
||||
checkedChanged = Signal(bool)
|
||||
|
||||
@singledispatchmethod
|
||||
def __init__(self, parent: QWidget = None, indicatorPos=IndicatorPosition.LEFT):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
parent: QWidget
|
||||
parent widget
|
||||
|
||||
indicatorPosition: IndicatorPosition
|
||||
the position of indicator
|
||||
"""
|
||||
super().__init__(parent=parent)
|
||||
self._text = self.tr('Off')
|
||||
self._offText = self.tr('Off')
|
||||
self._onText = self.tr('On')
|
||||
self.__spacing = 12
|
||||
self.lightTextColor = QColor(0, 0, 0)
|
||||
self.darkTextColor = QColor(255, 255, 255)
|
||||
|
||||
self.indicatorPos = indicatorPos
|
||||
self.hBox = QHBoxLayout(self)
|
||||
self.indicator = Indicator(self)
|
||||
self.label = QLabel(self._text, self)
|
||||
|
||||
self.__initWidget()
|
||||
|
||||
@__init__.register
|
||||
def _(self, text: str = 'Off', parent: QWidget = None, indicatorPos=IndicatorPosition.LEFT):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
text: str
|
||||
the text of switch button
|
||||
|
||||
parent: QWidget
|
||||
parent widget
|
||||
|
||||
indicatorPosition: IndicatorPosition
|
||||
the position of indicator
|
||||
"""
|
||||
self.__init__(parent, indicatorPos)
|
||||
self._offText = text
|
||||
self.setText(text)
|
||||
|
||||
def __initWidget(self):
|
||||
""" initialize widgets """
|
||||
self.setAttribute(Qt.WA_StyledBackground)
|
||||
self.installEventFilter(self)
|
||||
self.setFixedHeight(22)
|
||||
|
||||
# set layout
|
||||
self.hBox.setSpacing(self.__spacing)
|
||||
self.hBox.setContentsMargins(2, 0, 0, 0)
|
||||
|
||||
if self.indicatorPos == IndicatorPosition.LEFT:
|
||||
self.hBox.addWidget(self.indicator)
|
||||
self.hBox.addWidget(self.label)
|
||||
self.hBox.setAlignment(Qt.AlignLeft)
|
||||
else:
|
||||
self.hBox.addWidget(self.label, 0, Qt.AlignRight)
|
||||
self.hBox.addWidget(self.indicator, 0, Qt.AlignRight)
|
||||
self.hBox.setAlignment(Qt.AlignRight)
|
||||
|
||||
# set default style sheet
|
||||
FluentStyleSheet.SWITCH_BUTTON.apply(self)
|
||||
FluentStyleSheet.SWITCH_BUTTON.apply(self.label)
|
||||
|
||||
# connect signal to slot
|
||||
self.indicator.toggled.connect(self._updateText)
|
||||
self.indicator.toggled.connect(self.checkedChanged)
|
||||
|
||||
def eventFilter(self, obj, e: QEvent):
|
||||
if obj is self and self.isEnabled():
|
||||
if e.type() == QEvent.MouseButtonPress:
|
||||
self.indicator.setDown(True)
|
||||
elif e.type() == QEvent.MouseButtonRelease:
|
||||
self.indicator.setDown(False)
|
||||
self.indicator.toggle()
|
||||
elif e.type() == QEvent.Enter:
|
||||
self.indicator.setHover(True)
|
||||
elif e.type() == QEvent.Leave:
|
||||
self.indicator.setHover(False)
|
||||
|
||||
return super().eventFilter(obj, e)
|
||||
|
||||
def isChecked(self):
|
||||
return self.indicator.isChecked()
|
||||
|
||||
def setChecked(self, isChecked):
|
||||
""" set checked state """
|
||||
self._updateText()
|
||||
self.indicator.setChecked(isChecked)
|
||||
|
||||
def setTextColor(self, light, dark):
|
||||
""" set the color of text
|
||||
|
||||
Parameters
|
||||
----------
|
||||
light, dark: str | QColor | Qt.GlobalColor
|
||||
text color in light/dark theme mode
|
||||
"""
|
||||
self.lightTextColor = QColor(light)
|
||||
self.darkTextColor = QColor(dark)
|
||||
|
||||
setCustomStyleSheet(
|
||||
self.label,
|
||||
f"SwitchButton>QLabel{{color:{self.lightTextColor.name(QColor.NameFormat.HexArgb)}}}",
|
||||
f"SwitchButton>QLabel{{color:{self.darkTextColor.name(QColor.NameFormat.HexArgb)}}}"
|
||||
)
|
||||
|
||||
def setCheckedIndicatorColor(self, light, dark):
|
||||
""" set the color of indicator in checked status
|
||||
|
||||
Parameters
|
||||
----------
|
||||
light, dark: str | QColor | Qt.GlobalColor
|
||||
border color in light/dark theme mode
|
||||
"""
|
||||
self.indicator.setCheckedColor(light, dark)
|
||||
|
||||
def toggleChecked(self):
|
||||
""" toggle checked state """
|
||||
self.indicator.setChecked(not self.indicator.isChecked())
|
||||
|
||||
def _updateText(self):
|
||||
self.setText(self.onText if self.isChecked() else self.offText)
|
||||
self.adjustSize()
|
||||
|
||||
def getText(self):
|
||||
return self._text
|
||||
|
||||
def setText(self, text):
|
||||
self._text = text
|
||||
self.label.setText(text)
|
||||
self.adjustSize()
|
||||
|
||||
def getSpacing(self):
|
||||
return self.__spacing
|
||||
|
||||
def setSpacing(self, spacing):
|
||||
self.__spacing = spacing
|
||||
self.hBox.setSpacing(spacing)
|
||||
self.update()
|
||||
|
||||
def getOnText(self):
|
||||
return self._onText
|
||||
|
||||
def setOnText(self, text):
|
||||
self._onText = text
|
||||
self._updateText()
|
||||
|
||||
def getOffText(self):
|
||||
return self._offText
|
||||
|
||||
def setOffText(self, text):
|
||||
self._offText = text
|
||||
self._updateText()
|
||||
|
||||
spacing = Property(int, getSpacing, setSpacing)
|
||||
checked = Property(bool, isChecked, setChecked)
|
||||
text = Property(str, getText, setText)
|
||||
onText = Property(str, getOnText, setOnText)
|
||||
offText = Property(str, getOffText, setOffText)
|
||||
Reference in New Issue
Block a user