0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

feat(bec_spin_box): double spin box with setting inside for defining decimals

This commit is contained in:
2025-02-20 17:06:48 +01:00
parent 575c988c4f
commit f19d9485df
6 changed files with 221 additions and 0 deletions

View File

@ -0,0 +1 @@
{'files': ['decimal_spinbox.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 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()

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

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.utility.spinbox.bec_spin_box_plugin import BECSpinBoxPlugin
QPyDesignerCustomWidgetCollection.addCustomWidget(BECSpinBoxPlugin())
if __name__ == "__main__": # pragma: no cover
main()

View 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