mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 11:41:49 +02:00
feat(toggle): added angular component-like toggle
This commit is contained in:
0
bec_widgets/widgets/toggle/__init__.py
Normal file
0
bec_widgets/widgets/toggle/__init__.py
Normal file
15
bec_widgets/widgets/toggle/register_toggle_switch.py
Normal file
15
bec_widgets/widgets/toggle/register_toggle_switch.py
Normal file
@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.toggle.toggle_switch_plugin import ToggleSwitchPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(ToggleSwitchPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
149
bec_widgets/widgets/toggle/toggle.py
Normal file
149
bec_widgets/widgets/toggle/toggle.py
Normal file
@ -0,0 +1,149 @@
|
||||
import sys
|
||||
|
||||
from qtpy.QtCore import Property, QEasingCurve, QPointF, QPropertyAnimation, Qt
|
||||
from qtpy.QtGui import QColor, QPainter
|
||||
from qtpy.QtWidgets import QApplication, QWidget
|
||||
|
||||
|
||||
class ToggleSwitch(QWidget):
|
||||
"""
|
||||
A simple toggle.
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setFixedSize(40, 21)
|
||||
|
||||
self._thumb_pos = QPointF(3, 2) # Use QPointF for the thumb position
|
||||
self._active_track_color = QColor(33, 150, 243)
|
||||
self._active_thumb_color = QColor(255, 255, 255)
|
||||
self._inactive_track_color = QColor(200, 200, 200)
|
||||
self._inactive_thumb_color = QColor(255, 255, 255)
|
||||
|
||||
self._checked = False
|
||||
self._track_color = self.inactive_track_color
|
||||
self._thumb_color = self.inactive_thumb_color
|
||||
|
||||
self._animation = QPropertyAnimation(self, b"thumb_pos")
|
||||
self._animation.setDuration(200)
|
||||
self._animation.setEasingCurve(QEasingCurve.Type.OutBack)
|
||||
self.setProperty("checked", True)
|
||||
|
||||
@Property(bool)
|
||||
def checked(self):
|
||||
"""
|
||||
The checked state of the toggle switch.
|
||||
"""
|
||||
return self._checked
|
||||
|
||||
@checked.setter
|
||||
def checked(self, state):
|
||||
self._checked = state
|
||||
self.update_colors()
|
||||
self.set_thumb_pos_to_state()
|
||||
|
||||
@Property(QPointF)
|
||||
def thumb_pos(self):
|
||||
return self._thumb_pos
|
||||
|
||||
@thumb_pos.setter
|
||||
def thumb_pos(self, pos):
|
||||
self._thumb_pos = pos
|
||||
self.update()
|
||||
|
||||
@Property(QColor)
|
||||
def active_track_color(self):
|
||||
return self._active_track_color
|
||||
|
||||
@active_track_color.setter
|
||||
def active_track_color(self, color):
|
||||
self._active_track_color = color
|
||||
self.update_colors()
|
||||
self.update()
|
||||
|
||||
@Property(QColor)
|
||||
def active_thumb_color(self):
|
||||
return self._active_thumb_color
|
||||
|
||||
@active_thumb_color.setter
|
||||
def active_thumb_color(self, color):
|
||||
self._active_thumb_color = color
|
||||
self.update_colors()
|
||||
self.update()
|
||||
|
||||
@Property(QColor)
|
||||
def inactive_track_color(self):
|
||||
return self._inactive_track_color
|
||||
|
||||
@inactive_track_color.setter
|
||||
def inactive_track_color(self, color):
|
||||
self._inactive_track_color = color
|
||||
self.update_colors()
|
||||
self.update()
|
||||
|
||||
@Property(QColor)
|
||||
def inactive_thumb_color(self):
|
||||
return self._inactive_thumb_color
|
||||
|
||||
@inactive_thumb_color.setter
|
||||
def inactive_thumb_color(self, color):
|
||||
self._inactive_thumb_color = color
|
||||
self.update_colors()
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
# Draw track
|
||||
painter.setBrush(self._track_color)
|
||||
painter.setPen(Qt.NoPen)
|
||||
painter.drawRoundedRect(
|
||||
0, 0, self.width(), self.height(), self.height() / 2, self.height() / 2
|
||||
)
|
||||
|
||||
# Draw thumb
|
||||
painter.setBrush(self._thumb_color)
|
||||
diameter = int(self.height() * 0.8)
|
||||
painter.drawEllipse(int(self._thumb_pos.x()), int(self._thumb_pos.y()), diameter, diameter)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self._checked = not self._checked
|
||||
self.update_colors()
|
||||
self.animate_thumb()
|
||||
|
||||
def update_colors(self):
|
||||
|
||||
self._thumb_color = self.active_thumb_color if self._checked else self.inactive_thumb_color
|
||||
self._track_color = self.active_track_color if self._checked else self.inactive_track_color
|
||||
|
||||
def get_thumb_pos(self, checked):
|
||||
return QPointF(self.width() - self.height() + 3, 2) if checked else QPointF(3, 2)
|
||||
|
||||
def set_thumb_pos_to_state(self):
|
||||
# this is to avoid that linter complains about the thumb_pos setter
|
||||
self.setProperty("thumb_pos", self.get_thumb_pos(self._checked))
|
||||
self.update_colors()
|
||||
|
||||
def animate_thumb(self):
|
||||
start_pos = self.thumb_pos
|
||||
end_pos = self.get_thumb_pos(self._checked)
|
||||
|
||||
self._animation.stop()
|
||||
self._animation.setStartValue(start_pos)
|
||||
self._animation.setEndValue(end_pos)
|
||||
self._animation.start()
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSizeHint()
|
||||
|
||||
def minimumSizeHint(self):
|
||||
return self.size()
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
app = QApplication(sys.argv)
|
||||
window = ToggleSwitch()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
1
bec_widgets/widgets/toggle/toggle_switch.pyproject
Normal file
1
bec_widgets/widgets/toggle/toggle_switch.pyproject
Normal file
@ -0,0 +1 @@
|
||||
{'files': ['toggle.py']}
|
54
bec_widgets/widgets/toggle/toggle_switch_plugin.py
Normal file
54
bec_widgets/widgets/toggle/toggle_switch_plugin.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
from bec_widgets.widgets.toggle.toggle import ToggleSwitch
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='ToggleSwitch' name='toggle_switch'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
|
||||
class ToggleSwitchPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = ToggleSwitch(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
|
||||
def includeFile(self):
|
||||
return "toggle_switch"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "ToggleSwitch"
|
||||
|
||||
def toolTip(self):
|
||||
return "ToggleSwitch"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
38
tests/unit_tests/test_toggle.py
Normal file
38
tests/unit_tests/test_toggle.py
Normal file
@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
from qtpy.QtCore import QPointF, Qt
|
||||
|
||||
from bec_widgets.widgets.toggle.toggle import ToggleSwitch
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def toggle(qtbot):
|
||||
widget = ToggleSwitch()
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
|
||||
|
||||
def test_toggle(toggle):
|
||||
toggle.checked = False
|
||||
assert toggle.checked is False
|
||||
|
||||
assert toggle.thumb_pos == QPointF(3, 2)
|
||||
|
||||
toggle.checked = True
|
||||
assert toggle.checked is True
|
||||
|
||||
assert toggle.thumb_pos == QPointF(22, 2)
|
||||
|
||||
|
||||
def test_toggle_click(qtbot, toggle):
|
||||
init_state = toggle.checked
|
||||
|
||||
qtbot.mouseClick(toggle, Qt.LeftButton)
|
||||
toggle.paintEvent(None)
|
||||
assert toggle.checked is not init_state
|
||||
|
||||
init_state = toggle.checked
|
||||
|
||||
qtbot.mouseClick(toggle, Qt.LeftButton)
|
||||
toggle.paintEvent(None)
|
||||
assert toggle.checked is not init_state
|
Reference in New Issue
Block a user