mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat(bec_spin_box): double spin box with setting inside for defining decimals
This commit is contained in:
0
bec_widgets/widgets/utility/spinbox/__init__.py
Normal file
0
bec_widgets/widgets/utility/spinbox/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
{'files': ['decimal_spinbox.py']}
|
54
bec_widgets/widgets/utility/spinbox/bec_spin_box_plugin.py
Normal file
54
bec_widgets/widgets/utility/spinbox/bec_spin_box_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 bec_widgets.utils.bec_designer import designer_material_icon
|
||||||
|
from bec_widgets.widgets.utility.spinbox.decimal_spinbox import BECSpinBox
|
||||||
|
|
||||||
|
DOM_XML = """
|
||||||
|
<ui language='c++'>
|
||||||
|
<widget class='BECSpinBox' name='bec_spin_box'>
|
||||||
|
</widget>
|
||||||
|
</ui>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BECSpinBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._form_editor = None
|
||||||
|
|
||||||
|
def createWidget(self, parent):
|
||||||
|
t = BECSpinBox(parent)
|
||||||
|
return t
|
||||||
|
|
||||||
|
def domXml(self):
|
||||||
|
return DOM_XML
|
||||||
|
|
||||||
|
def group(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def icon(self):
|
||||||
|
return designer_material_icon(BECSpinBox.ICON_NAME)
|
||||||
|
|
||||||
|
def includeFile(self):
|
||||||
|
return "bec_spin_box"
|
||||||
|
|
||||||
|
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 "BECSpinBox"
|
||||||
|
|
||||||
|
def toolTip(self):
|
||||||
|
return "BECSpinBox"
|
||||||
|
|
||||||
|
def whatsThis(self):
|
||||||
|
return self.toolTip()
|
83
bec_widgets/widgets/utility/spinbox/decimal_spinbox.py
Normal file
83
bec_widgets/widgets/utility/spinbox/decimal_spinbox.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from bec_qthemes import material_icon
|
||||||
|
from qtpy.QtGui import Qt
|
||||||
|
from qtpy.QtWidgets import (
|
||||||
|
QApplication,
|
||||||
|
QDoubleSpinBox,
|
||||||
|
QInputDialog,
|
||||||
|
QSizePolicy,
|
||||||
|
QToolButton,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bec_widgets.utils import ConnectionConfig
|
||||||
|
from bec_widgets.utils.bec_widget import BECWidget
|
||||||
|
|
||||||
|
|
||||||
|
class BECSpinBox(BECWidget, QDoubleSpinBox):
|
||||||
|
PLUGIN = True
|
||||||
|
RPC = False
|
||||||
|
ICON_NAME = "123"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: QWidget | None = None,
|
||||||
|
config: ConnectionConfig | None = None,
|
||||||
|
client=None,
|
||||||
|
gui_id: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
if config is None:
|
||||||
|
config = ConnectionConfig(widget_class=self.__class__.__name__)
|
||||||
|
super().__init__(client=client, gui_id=gui_id, config=config)
|
||||||
|
QDoubleSpinBox.__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self.setObjectName("BECSpinBox")
|
||||||
|
# Make the widget as compact as possible horizontally.
|
||||||
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||||
|
self.setAlignment(Qt.AlignHCenter)
|
||||||
|
|
||||||
|
# Configure default QDoubleSpinBox settings.
|
||||||
|
self.setRange(-2147483647, 2147483647)
|
||||||
|
self.setDecimals(2)
|
||||||
|
|
||||||
|
# Create an embedded settings button.
|
||||||
|
self.setting_button = QToolButton(self)
|
||||||
|
self.setting_button.setIcon(material_icon("settings"))
|
||||||
|
self.setting_button.setToolTip("Set number of decimals")
|
||||||
|
self.setting_button.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.setting_button.setFocusPolicy(Qt.NoFocus)
|
||||||
|
self.setting_button.setStyleSheet("QToolButton { border: none; padding: 0px; }")
|
||||||
|
|
||||||
|
self.setting_button.clicked.connect(self.change_decimals)
|
||||||
|
|
||||||
|
self._button_size = 12
|
||||||
|
self._arrow_width = 20
|
||||||
|
|
||||||
|
def resizeEvent(self, event):
|
||||||
|
super().resizeEvent(event)
|
||||||
|
arrow_width = self._arrow_width
|
||||||
|
|
||||||
|
# Position the settings button inside the spin box, to the left of the arrow buttons.
|
||||||
|
x = self.width() - arrow_width - self._button_size - 2 # 2px margin
|
||||||
|
y = (self.height() - self._button_size) // 2
|
||||||
|
self.setting_button.setFixedSize(self._button_size, self._button_size)
|
||||||
|
self.setting_button.move(x, y)
|
||||||
|
|
||||||
|
def change_decimals(self):
|
||||||
|
"""
|
||||||
|
Change the number of decimals in the spin box.
|
||||||
|
"""
|
||||||
|
current = self.decimals()
|
||||||
|
new_decimals, ok = QInputDialog.getInt(
|
||||||
|
self, "Set Decimals", "Number of decimals:", current, 0, 10, 1
|
||||||
|
)
|
||||||
|
if ok:
|
||||||
|
self.setDecimals(new_decimals)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
window = BECSpinBox()
|
||||||
|
window.show()
|
||||||
|
sys.exit(app.exec())
|
15
bec_widgets/widgets/utility/spinbox/register_bec_spin_box.py
Normal file
15
bec_widgets/widgets/utility/spinbox/register_bec_spin_box.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.utility.spinbox.bec_spin_box_plugin import BECSpinBoxPlugin
|
||||||
|
|
||||||
|
QPyDesignerCustomWidgetCollection.addCustomWidget(BECSpinBoxPlugin())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
main()
|
68
tests/unit_tests/test_decimal_spin_box.py
Normal file
68
tests/unit_tests/test_decimal_spin_box.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# pylint: disable=missing-function-docstring, missing-module-docstring, unused-import
|
||||||
|
import pytest
|
||||||
|
from qtpy.QtCore import Qt
|
||||||
|
from qtpy.QtWidgets import QInputDialog
|
||||||
|
|
||||||
|
from bec_widgets.widgets.utility.spinbox.decimal_spinbox import BECSpinBox
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def spinbox_fixture(qtbot):
|
||||||
|
widget = BECSpinBox()
|
||||||
|
qtbot.addWidget(widget)
|
||||||
|
qtbot.waitExposed(widget)
|
||||||
|
yield widget
|
||||||
|
|
||||||
|
|
||||||
|
def test_spinbox_initial_values(spinbox_fixture):
|
||||||
|
"""
|
||||||
|
Test the default properties of the BECSpinBox.
|
||||||
|
"""
|
||||||
|
spinbox = spinbox_fixture
|
||||||
|
assert spinbox.decimals() == 2
|
||||||
|
assert spinbox.minimum() == -2147483647
|
||||||
|
assert spinbox.maximum() == 2147483647
|
||||||
|
assert spinbox.setting_button is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_decimals_ui(spinbox_fixture, monkeypatch, qtbot):
|
||||||
|
"""
|
||||||
|
Test that clicking on the setting button triggers the QInputDialog to change decimals.
|
||||||
|
We'll simulate a user entering a new decimals value in the dialog.
|
||||||
|
"""
|
||||||
|
spinbox = spinbox_fixture
|
||||||
|
|
||||||
|
def mock_get_int(*args, **kwargs):
|
||||||
|
return (5, True)
|
||||||
|
|
||||||
|
monkeypatch.setattr(QInputDialog, "getInt", mock_get_int)
|
||||||
|
assert spinbox.decimals() == 2
|
||||||
|
|
||||||
|
qtbot.mouseClick(spinbox.setting_button, Qt.LeftButton)
|
||||||
|
assert spinbox.decimals() == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_decimals_cancel(spinbox_fixture, monkeypatch, qtbot):
|
||||||
|
"""
|
||||||
|
Test that if the user cancels the decimals dialog, the decimals do not change.
|
||||||
|
"""
|
||||||
|
spinbox = spinbox_fixture
|
||||||
|
|
||||||
|
def mock_get_int(*args, **kwargs):
|
||||||
|
return (0, False)
|
||||||
|
|
||||||
|
monkeypatch.setattr(QInputDialog, "getInt", mock_get_int)
|
||||||
|
|
||||||
|
old_decimals = spinbox.decimals()
|
||||||
|
qtbot.mouseClick(spinbox.setting_button, Qt.LeftButton)
|
||||||
|
assert spinbox.decimals() == old_decimals
|
||||||
|
|
||||||
|
|
||||||
|
def test_spinbox_value_change(spinbox_fixture):
|
||||||
|
"""
|
||||||
|
Test that the spinbox accepts user input and updates its value accordingly.
|
||||||
|
"""
|
||||||
|
spinbox = spinbox_fixture
|
||||||
|
assert spinbox.value() == 0.0
|
||||||
|
spinbox.setValue(123.456)
|
||||||
|
assert spinbox.value() == 123.46
|
Reference in New Issue
Block a user