diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 84574c6..6ae789e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: os: [windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7eebcb..eb0b3ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Remember last save location (issue 136) - Create a grid layout / matrix box as option (issue 102) - Move within this matrix box with the arrows (issue 101) +- Use insert in FloatBox (issue 103) - Value Hint (issue 151) ## Fixed diff --git a/README.md b/README.md index 65af944..ce04770 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ This can be found on [ScenarioGUI.readthedocs.io](https://scenariogui.readthedoc ## Requirements -This code is tested with Python 3.10 and 3.11 and requires the following libraries (the versions mentioned are the ones with which the code is tested) +This code is tested with Python 3.8 to 3.11 and requires the following libraries (the versions mentioned are the ones with which the code is tested) -* PySide6>=6.4.1 +* PySide6>=6.5.3 * matplotlib>=3.5.2 * numpy>=1.23.1 * pandas>=1.4.3 @@ -34,7 +34,6 @@ For the tests * pytest-cov>=3.0.0 * pytest-timeout>=2.1.0 * pytest-qt>=4.1.0 -* keyboard>=0.13.5 ## Quick start ### Installation diff --git a/ScenarioGUI/gui_classes/gui_structure_classes/float_box.py b/ScenarioGUI/gui_classes/gui_structure_classes/float_box.py index 030b2c5..25a7718 100644 --- a/ScenarioGUI/gui_classes/gui_structure_classes/float_box.py +++ b/ScenarioGUI/gui_classes/gui_structure_classes/float_box.py @@ -9,6 +9,7 @@ import PySide6.QtCore as QtC # type: ignore import PySide6.QtGui as QtG import PySide6.QtWidgets as QtW # type: ignore +import numpy as np import ScenarioGUI.global_settings as globs @@ -46,11 +47,56 @@ def validate(self, float_str: str, pos: int) -> object: ------- object """ - # position is index +1 in input - nb_of_chars = len(float_str) - 1 if "," in float_str else 0 - nb_of_decimals = 0 if "," not in float_str else nb_of_chars - float_str.index(",") + # get sign and decimal symbols + decimal_sign = self.locale().decimalPoint() + sep_sign = self.locale().groupSeparator() + has_sep = sep_sign in float_str + len_bef = float_str[:pos].count(sep_sign) + if pos > len(float_str) or float_str == "": + return QtW.QDoubleSpinBox.validate(self, float_str, pos) + is_number = not (float_str[pos-1] in [sep_sign, decimal_sign]) + has_no_decimal_sign = decimal_sign not in float_str and self.decimals() > 0 + if has_no_decimal_sign: + float_str += decimal_sign + # move the curser to the next number if the current one is not + # float_str = float_str.replace(sep_sign, "") + nb_of_chars = len(float_str) - 1 if decimal_sign in float_str else 0 + nb_of_decimals = 0 if decimal_sign not in float_str else nb_of_chars - float_str.index(decimal_sign) # overwrite decimals - float_str = float_str[:-1] if nb_of_decimals > self.decimals() and pos != nb_of_chars + 1 else float_str + if nb_of_decimals > self.decimals() and pos != nb_of_chars + 1: + float_str = float_str[:-1] + # move values if the current one is above the maximum + limit_reached: bool = False + if self.maximum() > 1 or self.minimum() < -1: + float_str = float_str.replace(sep_sign, "") + strings = float_str.split(decimal_sign) + strings[0] = "0" if strings[0] == "" else strings[0] + strings[0] = "-0" if strings[0] == "-" else strings[0] + new_float_str = f"{strings[0]}.{strings[1]}" if len(strings) > 1 else strings[0] + limit_reached = (float(new_float_str) > self.maximum() and float(new_float_str) > 0) or float(new_float_str) < self.minimum() + if limit_reached: + float_str = f"{strings[0][:-1]}{decimal_sign}{strings[0][-1]}{strings[1][:-1]}" if len(strings) > 1 else strings[0][:-1] + if has_sep: + minus = float_str[0] == "-" + if minus: + float_str = float_str[1:] + dec_idx = float_str.index(decimal_sign) if self.decimals() > 0 and float_str.index(decimal_sign) > 2 else len(float_str) + # Initialize an empty result string + result_string = "" + # Iterate through the original string in reverse + for i, char in enumerate(reversed(float_str[: dec_idx])): + # Check if it's time to insert the symbol + if i > 0 and i % 3 == 0: + result_string = sep_sign + result_string # Insert the symbol + result_string = char + result_string # Add the character + float_str = result_string + float_str[dec_idx:] + if minus: + float_str = f"-{float_str}" + + pos = (pos + 1) if limit_reached and is_number and len(float_str) > pos and float_str[pos-1] == decimal_sign else pos + pos = (pos + 1) if len_bef < float_str[:pos].count(sep_sign) else pos + pos = (pos - 1) if len_bef > float_str[:pos].count(sep_sign) else pos + float_str = float_str[:-1] if has_no_decimal_sign else float_str return QtW.QDoubleSpinBox.validate(self, float_str, pos) @@ -61,15 +107,15 @@ class FloatBox(Option): """ def __init__( # noqa: PLR0913 - self, - label: str | list[str], - default_value: float, - category: Category, - *, - decimal_number: int = 0, - minimal_value: float = 0.0, - maximal_value: float = 100.0, - step: float = 1.0, + self, + label: str | list[str], + default_value: float, + category: Category, + *, + decimal_number: int = 0, + minimal_value: float = 0.0, + maximal_value: float = 100.0, + step: float = 1.0, ): """ @@ -153,10 +199,10 @@ def _check_value(self) -> bool: return self.minimal_value <= self.get_value() <= self.maximal_value def add_link_2_show( - self, - option: Option | Category | FunctionButton | Hint, - below: float = None, - above: float = None, + self, + option: Option | Category | FunctionButton | Hint, + below: float = None, + above: float = None, ) -> None: """ This function couples the visibility of an option to the value of the FloatBox object. @@ -186,11 +232,11 @@ def add_link_2_show( check_conditional_visibility(option) def show_option( - self, - option: Option | Category | FunctionButton | Hint, - below: float | None, - above: float | None, - args=None, + self, + option: Option | Category | FunctionButton | Hint, + below: float | None, + above: float | None, + args=None, ): """ This function shows the option if the value of the FloatBox is between the below and above value. @@ -261,12 +307,12 @@ def create_function_2_check_linked_value(self, value: tuple[float | None, float return ft_partial(self.check_linked_value, value, value_if_hidden) def create_widget( - self, - frame: QtW.QFrame, - layout_parent: QtW.QLayout, - *, - row: int | None = None, - column: int | None = None, + self, + frame: QtW.QFrame, + layout_parent: QtW.QLayout, + *, + row: int | None = None, + column: int | None = None, ) -> None: """ This functions creates the FloatBox widget in the frame. @@ -291,10 +337,10 @@ def create_widget( layout = self.create_frame(frame, layout_parent) self.widget.setParent(self.frame) self.widget.setStyleSheet( - f'QDoubleSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};' f'border: 1px solid {globs.WHITE};{"}"}' + f'QDoubleSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};border: 1px solid {globs.WHITE};{"}"}' ) self.widget.setAlignment(QtC.Qt.AlignRight | QtC.Qt.AlignTrailing | QtC.Qt.AlignVCenter) - self.widget.setProperty("showGroupSeparator", True) + self.widget.setGroupSeparatorShown(True) self.widget.setMinimum(self.minimal_value) self.widget.setMaximum(self.maximal_value) self.widget.setDecimals(self.decimal_number) diff --git a/ScenarioGUI/gui_classes/gui_structure_classes/int_box.py b/ScenarioGUI/gui_classes/gui_structure_classes/int_box.py index e86e575..52288d9 100644 --- a/ScenarioGUI/gui_classes/gui_structure_classes/int_box.py +++ b/ScenarioGUI/gui_classes/gui_structure_classes/int_box.py @@ -10,6 +10,7 @@ import PySide6.QtWidgets as QtW # type: ignore import ScenarioGUI.global_settings as globs +from .float_box import DoubleSpinBox from ...utils import set_default_font from .functions import check_and_set_max_min_values, check_conditional_visibility @@ -25,14 +26,6 @@ from .hint import Hint -class SpinBox(QtW.QSpinBox): # pragma: no cover - def wheelEvent(self, event: QtG.QWheelEvent): - if self.hasFocus(): - super().wheelEvent(event) - return - self.parent().wheelEvent(event) - - class IntBox(Option): """ This class contains all the functionalities of the IntBox (integer box) option in the GUI. @@ -85,7 +78,8 @@ def __init__( self.minimal_value: int = minimal_value self.maximal_value: int = maximal_value self.step: int = step - self.widget: SpinBox = SpinBox(self.default_parent, valueChanged=self.valueChanged.emit) + self.widget: DoubleSpinBox = DoubleSpinBox(self.default_parent, valueChanged=self.valueChanged.emit) + self.widget.setDecimals(0) def get_value(self) -> int: """ @@ -96,7 +90,7 @@ def get_value(self) -> int: int Value of the IntBox """ - return self.widget.value() + return int(self.widget.value()) def set_value(self, value: int) -> None: """ @@ -265,9 +259,10 @@ def create_widget( layout = self.create_frame(frame, layout_parent) self.widget.setParent(self.frame) self.widget.setStyleSheet( - f'QSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};border: 1px solid {globs.WHITE};{"}"}' + f'QDoubleSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};border: 1px solid {globs.WHITE};{"}"}' ) self.widget.setAlignment(QtC.Qt.AlignRight | QtC.Qt.AlignTrailing | QtC.Qt.AlignVCenter) + self.widget.setGroupSeparatorShown(True) self.widget.setMinimum(self.minimal_value) self.widget.setMaximum(self.maximal_value) self.widget.setValue(self.default_value) diff --git a/ScenarioGUI/gui_classes/gui_structure_classes/matrix_option.py b/ScenarioGUI/gui_classes/gui_structure_classes/matrix_option.py index 8ab6585..b5ae7b2 100644 --- a/ScenarioGUI/gui_classes/gui_structure_classes/matrix_option.py +++ b/ScenarioGUI/gui_classes/gui_structure_classes/matrix_option.py @@ -8,6 +8,7 @@ import numpy as np import ScenarioGUI.global_settings as globs +from .float_box import DoubleSpinBox from .functions import check_and_set_max_min_values, check_conditional_visibility from ...utils import set_default_font @@ -23,7 +24,7 @@ from .hint import Hint -class DoubleSpinBox(QtW.QDoubleSpinBox): # pragma: no cover +class ArrowDoubleSpinBox(DoubleSpinBox): # pragma: no cover def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.up = self @@ -32,7 +33,7 @@ def __init__(self, *args, **kwargs): self.right = self self.setButtonSymbols(QtW.QDoubleSpinBox.NoButtons) - def set_boxes(self, up: DoubleSpinBox, down: DoubleSpinBox, left: DoubleSpinBox, right: DoubleSpinBox): + def set_boxes(self, up: ArrowDoubleSpinBox, down: ArrowDoubleSpinBox, left: ArrowDoubleSpinBox, right: ArrowDoubleSpinBox): self.up = up self.down = down self.left = left @@ -54,13 +55,6 @@ def keyPressEvent(self, event): else: super().keyPressEvent(event) - def wheelEvent(self, event: QtG.QWheelEvent): - if self.hasFocus(): - super().wheelEvent(event) - return - self.parent().wheelEvent(event) - - class MatrixBox(Option): """ This class contains all the functionalities of the MatrixBox option in the GUI. @@ -118,8 +112,8 @@ def __init__( self.maximal_value: list[list[float]] = ([[maximal_value] * column] * row) if isinstance(maximal_value, (float, int)) else maximal_value self.column = column self.row = row - self.widget: list[list[DoubleSpinBox]] = [ - [DoubleSpinBox(self.default_parent, valueChanged=self.valueChanged.emit) for _ in range(column)] for _ in range(row) + self.widget: list[list[ArrowDoubleSpinBox]] = [ + [ArrowDoubleSpinBox(self.default_parent, valueChanged=self.valueChanged.emit) for _ in range(column)] for _ in range(row) ] def get_value(self) -> list[list[float]]: @@ -361,7 +355,7 @@ def create_widget( f'QDoubleSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};' f'border: 1px solid {globs.WHITE};{"}"}' ) widget.setAlignment(QtC.Qt.AlignRight | QtC.Qt.AlignTrailing | QtC.Qt.AlignVCenter) - widget.setProperty("showGroupSeparator", True) + widget.setGroupSeparatorShown(True) widget.setMinimum(self.minimal_value[row][column]) widget.setMaximum(self.maximal_value[row][column]) widget.setDecimals(self.decimal_number[row][column]) diff --git a/ScenarioGUI/gui_classes/gui_structure_classes/multiple_int_box.py b/ScenarioGUI/gui_classes/gui_structure_classes/multiple_int_box.py index 91d39d2..5a638f0 100644 --- a/ScenarioGUI/gui_classes/gui_structure_classes/multiple_int_box.py +++ b/ScenarioGUI/gui_classes/gui_structure_classes/multiple_int_box.py @@ -15,7 +15,7 @@ from ...utils import set_default_font from .functions import check_conditional_visibility -from .int_box import SpinBox +from .int_box import DoubleSpinBox from .option import Option if TYPE_CHECKING: # pragma: no cover @@ -78,7 +78,8 @@ def __init__( # noqa: PLR0913 self.minimal_value: list[int] = [minimal_value for _ in default_value] if not isinstance(minimal_value, Iterable) else minimal_value self.maximal_value: list[int] = [maximal_value for _ in default_value] if not isinstance(maximal_value, Iterable) else maximal_value self.step: list[int] = [step for _ in default_value] if not isinstance(step, Iterable) else step - self.widget: list[SpinBox] = [SpinBox(self.default_parent, valueChanged=self.valueChanged.emit) for _ in default_value] + self.widget: list[DoubleSpinBox] = [DoubleSpinBox(self.default_parent, valueChanged=self.valueChanged.emit) for _ in default_value] + _ = [wid.setDecimals(0) for wid in self.widget] def get_value(self) -> tuple[int]: """ @@ -260,7 +261,7 @@ def create_widget( for widget, max_val, min_val, step, def_val in zip(self.widget, self.maximal_value, self.minimal_value, self.step, self.default_value): widget.setParent(self.frame) widget.setStyleSheet( - f'QSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};' f'border: 1px solid {globs.WHITE};{"}"}' + f'QDoubleSpinBox{"{"}selection-color: {globs.WHITE};selection-background-color: {globs.LIGHT};' f'border: 1px solid {globs.WHITE};{"}"}' ) widget.setAlignment(QtC.Qt.AlignRight | QtC.Qt.AlignTrailing | QtC.Qt.AlignVCenter) widget.setMinimum(min_val) diff --git a/examples/example_classes/gui_structure.py b/examples/example_classes/gui_structure.py index 8226e42..239c922 100644 --- a/examples/example_classes/gui_structure.py +++ b/examples/example_classes/gui_structure.py @@ -34,7 +34,7 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): label="a", default_value=2, minimal_value=0, - maximal_value=200, + maximal_value=30000, category=self.category_inputs, ) self.int_a.set_tool_tip("This is an explanation\nfor the value a") @@ -112,7 +112,16 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): label=["b", "b"], default_value=100, minimal_value=0, - maximal_value=1000, + maximal_value=100_000, + decimal_number=2, + category=self.sub_category, + ) + + self.negative_float = els.FloatBox( + label="negative_float", + default_value=-1000, + minimal_value=-100_000, + maximal_value=-100, decimal_number=2, category=self.sub_category, ) @@ -218,7 +227,7 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): entries=["entry 1", "entry 2", "entry 3"], ) self.hint_flex = els.Hint( - hint="wrong length of flexible option", + hint=["wrong length of flexible option", "Falscher Wert in flexible option"], category=self.category_inputs, warning=True, ) diff --git a/requirements.txt b/requirements.txt index a902a2b..24565ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ matplotlib>=3.5.2 numpy>=1.23.1 -PySide6>=6.5.2 +PySide6>=6.6.0 pandas>=1.4.3 black>=23.1.0 \ No newline at end of file diff --git a/requirements_docs.txt b/requirements_docs.txt index 6efe475..22ed6c3 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -5,7 +5,8 @@ matplotlib>=3.5.2 numpy>=1.23.1 pandas>=1.4.3 pygfunction>=2.2.1 -PySide6>=6.3.1 +PySide6>=6.5.3 scipy>=1.8.1 numpydoc >= 1.2.0 -sphinx_design >= 0.3.0 \ No newline at end of file +sphinx_design >= 0.3.0 +sphinx-rtd-theme>=1.3.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index b2697b2..eb30367 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,6 +13,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 License :: OSI Approved :: BSD License Operating System :: OS Independent @@ -23,7 +24,7 @@ include_package_data = True install_requires = matplotlib>=3.5.2 numpy>=1.23.1 - PySide6>=6.4.1 + PySide6>=6.6.0 [options.extras_require] GUI = pyside6>=6.4.1 diff --git a/tests/gui_structure_for_tests.py b/tests/gui_structure_for_tests.py index 1262867..408bcdc 100644 --- a/tests/gui_structure_for_tests.py +++ b/tests/gui_structure_for_tests.py @@ -46,7 +46,7 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): label="a", default_value=2, minimal_value=0, - maximal_value=300, + maximal_value=3000, category=self.category_inputs, ) @@ -64,7 +64,7 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): label="b", default_value=100, minimal_value=0, - maximal_value=1000, + maximal_value=100_000, decimal_number=2, category=self.sub_category, ) @@ -101,6 +101,15 @@ def __init__(self, default_parent: QtW.QWidget, translations: Translations): category=self.sub_category, ) + self.negative_float = els.FloatBox( + label="negative_float", + default_value=-1000, + minimal_value=-100_000, + maximal_value=-100, + decimal_number=2, + category=self.sub_category, + ) + self.int_units = els.IntBoxWithUnits( label="IntBoxWithUnits", default_value=2, diff --git a/tests/test_gui_structure_elements/test_float_box.py b/tests/test_gui_structure_elements/test_float_box.py index e1e456a..13983c2 100644 --- a/tests/test_gui_structure_elements/test_float_box.py +++ b/tests/test_gui_structure_elements/test_float_box.py @@ -1,5 +1,5 @@ import numpy as np -import PySide6.QtGui as QtG +import PySide6.QtCore as QtC import pytest from ScenarioGUI.gui_classes.gui_structure_classes.functions import ConditionalVisibilityWarning @@ -19,6 +19,7 @@ def test_float_box(qtbot): # init gui window main_window = start_tests(qtbot) float_b = main_window.gui_structure.float_b + negative_float = main_window.gui_structure.negative_float assert np.isclose(float_b.get_value(), float_b.default_value) float_b.set_value(float_b.default_value + 50) assert np.isclose(float_b.default_value + 50, float_b.get_value()) @@ -55,9 +56,93 @@ def test_float_box(qtbot): assert not main_window.gui_structure.int_a.is_hidden() float_b.set_value(220) assert not main_window.gui_structure.int_a.is_hidden() + float_b.widget.setLocale(QtC.QLocale(QtC.QLocale.German, QtC.QLocale.Germany)) # test validation + res = float_b.widget.validate("100,02", 7) + assert np.isclose(float(res[1].replace(",", ".")), 100.02) + assert res[2] == 7 res = float_b.widget.validate("100,020", 5) assert np.isclose(float(res[1].replace(",", ".")), 100.02) + assert res[2] == 5 + res = float_b.widget.validate("10.000,020", 8) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_000.02) + assert res[2] == 8 + res = float_b.widget.validate("10.000,00", 4) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_000) + assert res[2] == 4 + res = float_b.widget.validate("103.000,20", 3) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_300.02) + assert res[2] == 4 + res = float_b.widget.validate("103.000", 7) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_300) + assert res[2] == 8 + res = float_b.widget.validate("10.300", 5) + assert res[1] == "10.300" + assert res[2] == 5 + res = float_b.widget.validate(",20", 0) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 0.2) + assert res[2] == 0 + res = float_b.widget.validate("10.0003,20", 7) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_000.32) + assert res[2] == 8 + res = float_b.widget.validate("10.0000,20", 7) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_000.02) + assert res[2] == 8 + res = float_b.widget.validate("100.000,00", 0) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 100_000) + assert res[2] == 0 + res = float_b.widget.validate("0100.000,00", 0) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 100_000) + assert res[2] == 0 + res = float_b.widget.validate("0.100.000,00", 0) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 100_000) + assert res[2] == 0 + res = float_b.widget.validate("0000.100.000,00", 4) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 100_000) + assert res[2] == 5 + res = float_b.widget.validate("0.10.000,00", 2) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10_000) + assert res[2] == 1 + res = float_b.widget.validate("10002,0", 5) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 10002) + assert res[2] == 5 + negative_float.widget.setLocale(QtC.QLocale(QtC.QLocale.German, QtC.QLocale.Germany)) + res = negative_float.widget.validate("-20000,20", 5) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -20_000.20) + assert res[2] == 5 + res = negative_float.widget.validate("-212.000,20", 4) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -21_200.02) + assert res[2] == 5 + res = negative_float.widget.validate("-21.000,230", 10) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -21_000.23) + assert res[2] == 10 + res = negative_float.widget.validate("-21.000,230", 9) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -21_000.23) + assert res[2] == 9 + res = negative_float.widget.validate("-21.0002,23", 8) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -21_000.22) + assert res[2] == 9 + res = negative_float.widget.validate("-10.0000,23", 8) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), -10_000.02) + assert res[2] == 9 + res = negative_float.widget.validate("-10.0000,00", 4) + assert res[1] == "-100.000,00" + assert res[2] == 3 + # test validation + float_b.widget.setLocale(QtC.QLocale(QtC.QLocale.English, QtC.QLocale.UnitedStates)) + res = float_b.widget.validate("10,0003.20", 7) + assert np.isclose(float(res[1].replace(",", "")), 10_000.32) + assert res[2] == 8 + res = float_b.widget.validate(".20", 3) + assert np.isclose(float(res[1]), 0.2) + assert res[2] == 3 + res = float_b.widget.validate("100.020", 5) + assert np.isclose(float(res[1]), 100.02) + assert res[2] == 5 + res = float_b.widget.validate("10002.0", 5) + assert np.isclose(float(res[1].replace(",", "")), 10002) + assert res[2] == 5 + float_b.widget.setLocale(QtC.QLocale(QtC.QLocale.German, QtC.QLocale.Germany)) # test set text main_window.gui_structure.float_b.set_text("Hello") assert main_window.gui_structure.float_b.label.text() == "Hello" diff --git a/tests/test_gui_structure_elements/test_int_box.py b/tests/test_gui_structure_elements/test_int_box.py index 230ccb0..7804cef 100644 --- a/tests/test_gui_structure_elements/test_int_box.py +++ b/tests/test_gui_structure_elements/test_int_box.py @@ -1,4 +1,5 @@ import numpy as np +import PySide6.QtCore as QtC import pytest from ScenarioGUI.gui_classes.gui_structure_classes.functions import ConditionalVisibilityWarning @@ -44,6 +45,18 @@ def test_int_box(qtbot): assert not main_window.gui_structure.float_b.is_hidden() main_window.save_scenario() assert "int_a" in main_window.list_ds[0].to_dict() + int_a.widget.setLocale(QtC.QLocale(QtC.QLocale.German, QtC.QLocale.Germany)) + # test validation + res = int_a.widget.validate("100", 2) + assert np.isclose(float(res[1].replace(",", ".")), 100) + assert res[2] == 2 + res = int_a.widget.validate("12345", 2) + assert np.isclose(float(res[1].replace(",", ".")), 1234) + assert res[2] == 2 + res = int_a.widget.validate("1.234", 2) + assert np.isclose(float(res[1].replace(".", "").replace(",", ".")), 1234) + assert res[1] == "1.234" + assert res[2] == 2 int_a.add_link_2_show(main_window.gui_structure.hint_1, below=0)