0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 03:31:50 +02:00

feat(toggle): added angular component-like toggle

This commit is contained in:
2024-07-07 17:31:10 +02:00
parent f04862933f
commit b9bff38b64
6 changed files with 257 additions and 0 deletions

View File

View 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()

View 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())

View File

@ -0,0 +1 @@
{'files': ['toggle.py']}

View 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()

View 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