From 3423fb0d56b3a05a8d8ce4cb0ca5c30146c2912b Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 16 Dec 2024 15:53:47 +0000 Subject: [PATCH 01/68] Rewrite UISliderWidget class to take a QDoubleSpinBox as input --- eqt/ui/UISliderWidget.py | 31 ++++++++++++++++++----- examples/dialog_example_3_save_default.py | 4 +-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index c5722cb..e1656b0 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -6,18 +6,35 @@ class UISliderWidget(QSlider): '''Creates a Slider widget which updates a QLabel with its value (which may be scaled to a non-integer value by setting the scale_factor)''' - def __init__(self, label, scale_factor=1): + def __init__(self, dspinbox, scale_factor=1, parent=None): QSlider.__init__(self) - self.label = label + self.parent = parent self.scale_factor = scale_factor + self.setOrientation(QtCore.Qt.Horizontal) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setTickPosition(QSlider.TicksBelow) - self.valueChanged.connect(self.show_slider_value) + + self.sliderPressed.connect(self.update_dspinbox) + self.sliderMoved.connect(self.update_dspinbox) + self.sliderReleased.connect(self.update_dspinbox) + + self.dspinbox = dspinbox + self.dspinbox.setMinimum(0.00) + self.dspinbox.setMaximum(100.00) + self.dspinbox.setValue(0.00) + self.dspinbox.editingFinished.connect(self.update_slider) def get_slider_value(self): - return self.value() * self.scale_factor + return self.value() + + def get_dspinbox_value(self): + return self.dspinbox.value() + + def update_slider(self): + dspinbox_value = int(self.get_dspinbox_value()) + self.setValue(dspinbox_value) - def show_slider_value(self): - value = self.get_slider_value() - self.label.setText(str(value)) + def update_dspinbox(self): + slider_value = float(self.get_slider_value()) + self.dspinbox.setValue(slider_value) diff --git a/examples/dialog_example_3_save_default.py b/examples/dialog_example_3_save_default.py index 27c8cc5..de7398d 100644 --- a/examples/dialog_example_3_save_default.py +++ b/examples/dialog_example_3_save_default.py @@ -3,7 +3,7 @@ import utilitiesForExamples as utex from PySide2 import QtWidgets -from eqt.ui import FormDialog +from eqt.ui import UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -20,7 +20,7 @@ def __init__(self, parent=None): widg.setLayout(layout) self.setCentralWidget(widg) - self.dialog = FormDialog(parent=self, title='Example') + self.uislider = UISliderWidget() self.openFormDialog() self.show() From 5c8dca9b559c9289f0f07887d7a5193f8b470a33 Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 16 Dec 2024 15:57:07 +0000 Subject: [PATCH 02/68] Revert changes to dialog_example_3_save_default.py --- examples/dialog_example_3_save_default.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/dialog_example_3_save_default.py b/examples/dialog_example_3_save_default.py index de7398d..27c8cc5 100644 --- a/examples/dialog_example_3_save_default.py +++ b/examples/dialog_example_3_save_default.py @@ -3,7 +3,7 @@ import utilitiesForExamples as utex from PySide2 import QtWidgets -from eqt.ui import UISliderWidget +from eqt.ui import FormDialog class MainUI(QtWidgets.QMainWindow): @@ -20,7 +20,7 @@ def __init__(self, parent=None): widg.setLayout(layout) self.setCentralWidget(widg) - self.uislider = UISliderWidget() + self.dialog = FormDialog(parent=self, title='Example') self.openFormDialog() self.show() From fe05eae49fdcaa208313e5093be86d4e1d723638 Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 16 Dec 2024 15:58:56 +0000 Subject: [PATCH 03/68] Add dialog_example_uislider.py --- examples/dialog_example_uislider.py | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 examples/dialog_example_uislider.py diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py new file mode 100644 index 0000000..3343fcf --- /dev/null +++ b/examples/dialog_example_uislider.py @@ -0,0 +1,58 @@ +import sys + +from PySide2 import QtWidgets + +from eqt.ui import FormDialog, UISliderWidget + + +class MainUI(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + + pb = QtWidgets.QPushButton(self) + pb.setText("Open Dialog with form layout") + pb.clicked.connect(lambda: self.openFormDialog()) + + layout = QtWidgets.QHBoxLayout() + layout.addWidget(pb) + widg = QtWidgets.QWidget() + widg.setLayout(layout) + + self.setCentralWidget(widg) + + self.show() + + def openFormDialog(self): + dialog = FormDialog(parent=self, title='Example') + dialog.Ok.clicked.connect(lambda: self.accepted()) + + # Example on how to add elements to the FormDialog + # add input 1 as UISliderWidget and DoubleSpinBox + dspinbox = QtWidgets.QDoubleSpinBox() + uislider = UISliderWidget.UISliderWidget(dspinbox) + + # finally add to the form widget + dialog.addWidget(uislider, 'Slider 1', 'input_slider') + dialog.addWidget(dspinbox, '', 'input_dspinbox') + + # store a reference + self.dialog = dialog + self.dialog.onCancel = self.rejected + dialog.exec() + + def accepted(self): + print("accepted") + print(self.dialog.widgets['input_slider_field'].value()) + print(self.dialog.widgets['input_dspinbox_field'].value()) + self.dialog.close() + + def rejected(self): + print("rejected") + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + + window = MainUI() + + sys.exit(app.exec_()) From ca10630274b4e679d16730defc1a63aff6f8d47f Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 16 Dec 2024 19:11:17 +0000 Subject: [PATCH 04/68] Update CHANGELOG.md --- CHANGELOG.md | 1 + eqt/ui/UISliderWidget.py | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c1df6..a7fab18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Update `CONTRIBUTING.md` with detailed installation and contribution instructions (#161) - Limit Python version to <3.12 in conda recipe (#161) - Change SessionDirectorySelectionDialog `.open()` call to `.exec()` (#163, #165) +- Add `QDoubleSpinBox` support to `UISliderWidget` (#168) # Version 1.0.1 - Add NoBorderScrollArea, example and tests (#155) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index e1656b0..da12e78 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -6,7 +6,7 @@ class UISliderWidget(QSlider): '''Creates a Slider widget which updates a QLabel with its value (which may be scaled to a non-integer value by setting the scale_factor)''' - def __init__(self, dspinbox, scale_factor=1, parent=None): + def __init__(self, dspinbox, min=0.00, max=1.00, scale_factor=1, parent=None): QSlider.__init__(self) self.parent = parent self.scale_factor = scale_factor @@ -20,9 +20,6 @@ def __init__(self, dspinbox, scale_factor=1, parent=None): self.sliderReleased.connect(self.update_dspinbox) self.dspinbox = dspinbox - self.dspinbox.setMinimum(0.00) - self.dspinbox.setMaximum(100.00) - self.dspinbox.setValue(0.00) self.dspinbox.editingFinished.connect(self.update_slider) def get_slider_value(self): From 2de3c5b38b2fe17470ab677108008b922a1731b8 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 18 Dec 2024 11:00:58 +0000 Subject: [PATCH 05/68] Add minimum and maximum value parameters and logic to UISliderWidget.py --- eqt/ui/UISliderWidget.py | 28 ++++++++++++++++++++++------ examples/dialog_example_uislider.py | 7 ++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index da12e78..446641e 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -4,23 +4,36 @@ class UISliderWidget(QSlider): '''Creates a Slider widget which updates - a QLabel with its value (which may be scaled - to a non-integer value by setting the scale_factor)''' - def __init__(self, dspinbox, min=0.00, max=1.00, scale_factor=1, parent=None): + a QDoubleSpinBox with its value (which may be scaled + to a non-integer value by setting the scale_factor) + + Parameters + ---------- + dspinbox : QDoubleSpinBox + min : float + max : float + step_size : float + ''' + def __init__(self, dspinbox, minimum=0.00, maximum=1.00, step_size=1.00): QSlider.__init__(self) - self.parent = parent - self.scale_factor = scale_factor + self.setMinimum(minimum) + self.setMaximum(maximum) + self.steps = (maximum-minimum) / step_size self.setOrientation(QtCore.Qt.Horizontal) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setTickPosition(QSlider.TicksBelow) + self.setSingleStep(step_size) + self.setTickInterval(step_size) self.sliderPressed.connect(self.update_dspinbox) self.sliderMoved.connect(self.update_dspinbox) self.sliderReleased.connect(self.update_dspinbox) self.dspinbox = dspinbox - self.dspinbox.editingFinished.connect(self.update_slider) + self.dspinbox.valueChanged.connect(self.update_slider) + self.dspinbox.setMinimum(minimum) + self.dspinbox.setMaximum(maximum) def get_slider_value(self): return self.value() @@ -35,3 +48,6 @@ def update_slider(self): def update_dspinbox(self): slider_value = float(self.get_slider_value()) self.dspinbox.setValue(slider_value) + + def setMaximum(self, arg__1): + return super().setMaximum(arg__1) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 3343fcf..82e8b1c 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -29,7 +29,8 @@ def openFormDialog(self): # Example on how to add elements to the FormDialog # add input 1 as UISliderWidget and DoubleSpinBox dspinbox = QtWidgets.QDoubleSpinBox() - uislider = UISliderWidget.UISliderWidget(dspinbox) + uislider = UISliderWidget.UISliderWidget(dspinbox, minimum=0.00, maximum=10.00, + step_size=1.00) # finally add to the form widget dialog.addWidget(uislider, 'Slider 1', 'input_slider') @@ -42,8 +43,8 @@ def openFormDialog(self): def accepted(self): print("accepted") - print(self.dialog.widgets['input_slider_field'].value()) - print(self.dialog.widgets['input_dspinbox_field'].value()) + print(f"Slider Value: {self.dialog.widgets['input_slider_field'].value()}") + print(f"SpinBox Value: {self.dialog.widgets['input_dspinbox_field'].value()}") self.dialog.close() def rejected(self): From c3f39dc7d52369248f9ad9558cb8eec5c609a10b Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 18 Dec 2024 11:03:46 +0000 Subject: [PATCH 06/68] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7fab18..650f9e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Limit Python version to <3.12 in conda recipe (#161) - Change SessionDirectorySelectionDialog `.open()` call to `.exec()` (#163, #165) - Add `QDoubleSpinBox` support to `UISliderWidget` (#168) + + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` # Version 1.0.1 - Add NoBorderScrollArea, example and tests (#155) From 68e58ea1f161e5c7038662edc9edc772d1c2c8ff Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 18 Dec 2024 18:15:24 +0000 Subject: [PATCH 07/68] Add UISliderLineEditWidget class, update comments --- eqt/ui/UISliderLineEditWidget.py | 69 +++++++++++++++++++++++++++++ eqt/ui/UISliderWidget.py | 22 ++++++--- examples/dialog_example_uislider.py | 29 ++++++++---- 3 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 eqt/ui/UISliderLineEditWidget.py diff --git a/eqt/ui/UISliderLineEditWidget.py b/eqt/ui/UISliderLineEditWidget.py new file mode 100644 index 0000000..39fad74 --- /dev/null +++ b/eqt/ui/UISliderLineEditWidget.py @@ -0,0 +1,69 @@ +from PySide2 import QtCore +from PySide2.QtWidgets import QDoubleSpinBox, QGridLayout, QLabel, QSlider, QWidget + + +class UISliderLineEditWidget(QWidget): + '''Creates a QGridLayout that includes a QSlider, min/max QLabels and a QDoubleSpinBox. + The QDoubleSpinBox is updated with the value of the slider and vice versa. + + Parameters + ---------- + dspinbox : QDoubleSpinBox + min : float + max : float + step_size : float + ''' + def __init__(self, minimum=0.00, maximum=1.00, step_size=1.00): + QWidget.__init__(self) + + # Configure the QSlider + self.slider = QSlider() + self.slider.setMinimum(minimum) + self.slider.setMaximum(maximum) + self.slider.setOrientation(QtCore.Qt.Horizontal) + self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) + self.slider.setTickPosition(QSlider.TicksBelow) + self.slider.setSingleStep(step_size) + self.slider.setTickInterval(step_size) + + # Connect the QSlider to the QDoubleSpinBox + self.slider.sliderPressed.connect(self.update_dspinbox) + self.slider.sliderMoved.connect(self.update_dspinbox) + self.slider.sliderReleased.connect(self.update_dspinbox) + + # Connect the QDoubleSpinBox + self.dspinbox = QDoubleSpinBox() + self.dspinbox.valueChanged.connect(self.update_slider) + self.dspinbox.setMinimum(minimum) + self.dspinbox.setMaximum(maximum) + + # Configure the min/max QLabels + self.min_label = QLabel() + self.min_label.setText(str(self.slider.minimum())) + self.max_label = QLabel() + self.max_label.setText(str(self.slider.maximum())) + + # Configure the QGridLayout + widget_layout = QGridLayout() + widget_layout.addWidget(self.slider, 0, 0, 1, -1) + widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) + widget_layout.addWidget(self.max_label, 1, 1, QtCore.Qt.AlignRight) + widget_layout.addWidget(self.dspinbox, 2, 0, 1, -1) + + # Set the layout + self.setLayout(widget_layout) + self.show() + + def get_slider_value(self): + return self.slider.value() + + def get_dspinbox_value(self): + return self.dspinbox.value() + + def update_slider(self): + dspinbox_value = int(self.get_dspinbox_value()) + self.slider.setValue(dspinbox_value) + + def update_dspinbox(self): + slider_value = float(self.get_slider_value()) + self.dspinbox.setValue(slider_value) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 446641e..6a2dd18 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -10,31 +10,42 @@ class UISliderWidget(QSlider): Parameters ---------- dspinbox : QDoubleSpinBox + min_label : QLabel + max_label : QLabel min : float max : float step_size : float ''' - def __init__(self, dspinbox, minimum=0.00, maximum=1.00, step_size=1.00): + def __init__(self, dspinbox, min_label, max_label, minimum=0.00, maximum=1.00, step_size=1.00): QSlider.__init__(self) + + # Configure the QSlider self.setMinimum(minimum) self.setMaximum(maximum) - self.steps = (maximum-minimum) / step_size - self.setOrientation(QtCore.Qt.Horizontal) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setTickPosition(QSlider.TicksBelow) self.setSingleStep(step_size) self.setTickInterval(step_size) + # Connect the QSlider to the QDoubleSpinBox self.sliderPressed.connect(self.update_dspinbox) self.sliderMoved.connect(self.update_dspinbox) self.sliderReleased.connect(self.update_dspinbox) + # Configure the QDoubleSpinBox self.dspinbox = dspinbox - self.dspinbox.valueChanged.connect(self.update_slider) + self.dspinbox.setDecimals(3) self.dspinbox.setMinimum(minimum) self.dspinbox.setMaximum(maximum) + # Connect the QDoubleSpinBox to the QSlider + self.dspinbox.valueChanged.connect(self.update_slider) + + # Configure QLabels for minimum and maximum QSlider values + self.min_label = min_label + self.max_label = max_label + def get_slider_value(self): return self.value() @@ -48,6 +59,3 @@ def update_slider(self): def update_dspinbox(self): slider_value = float(self.get_slider_value()) self.dspinbox.setValue(slider_value) - - def setMaximum(self, arg__1): - return super().setMaximum(arg__1) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 82e8b1c..b4ac222 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -2,7 +2,7 @@ from PySide2 import QtWidgets -from eqt.ui import FormDialog, UISliderWidget +from eqt.ui import FormDialog, UISliderLineEditWidget, UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -29,12 +29,22 @@ def openFormDialog(self): # Example on how to add elements to the FormDialog # add input 1 as UISliderWidget and DoubleSpinBox dspinbox = QtWidgets.QDoubleSpinBox() - uislider = UISliderWidget.UISliderWidget(dspinbox, minimum=0.00, maximum=10.00, - step_size=1.00) + min_label = QtWidgets.QLabel() + max_label = QtWidgets.QLabel() + uislider = UISliderWidget.UISliderWidget(dspinbox, min_label, max_label, minimum=0.00, + maximum=10.00, step_size=1.00) - # finally add to the form widget - dialog.addWidget(uislider, 'Slider 1', 'input_slider') - dialog.addWidget(dspinbox, '', 'input_dspinbox') + # add to the form widget + dialog.addWidget(uislider, 'QSlider 1:', 'input_slider1') + dialog.addWidget(dspinbox, '', 'input_dspinbox1') + + # add input 2 as UISliderLineEditWidget + dspinbox = QtWidgets.QDoubleSpinBox() + uislider = UISliderLineEditWidget.UISliderLineEditWidget(minimum=0.00, maximum=10.00, + step_size=2.00) + + # add to the form widget + dialog.addWidget(uislider, 'QSlider 2:', 'input_slider2') # store a reference self.dialog = dialog @@ -43,8 +53,11 @@ def openFormDialog(self): def accepted(self): print("accepted") - print(f"Slider Value: {self.dialog.widgets['input_slider_field'].value()}") - print(f"SpinBox Value: {self.dialog.widgets['input_dspinbox_field'].value()}") + print(f"QSlider 1 Value: {self.dialog.widgets['input_slider1_field'].value()}") + print(f"QDoubleSpinBox 1 Value: {self.dialog.widgets['input_dspinbox1_field'].value()}") + + print(f"QSlider 2 Value: {self.dialog.widgets['input_slider2_field'].value()}") + self.dialog.close() def rejected(self): From d86f1f76109db622c757d76c06cdcb3befa16ada Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 19 Dec 2024 17:01:56 +0000 Subject: [PATCH 08/68] Replace QDoubleSpinBox in UISliderWidget class with QLineEdit --- eqt/ui/UISliderWidget.py | 74 ++++++++++++++++++----------- examples/dialog_example_uislider.py | 19 ++++---- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 6a2dd18..2a39886 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,61 +1,79 @@ -from PySide2 import QtCore +from PySide2 import QtCore, QtGui from PySide2.QtWidgets import QSlider class UISliderWidget(QSlider): '''Creates a Slider widget which updates - a QDoubleSpinBox with its value (which may be scaled + a QLineEdit with its value (which may be scaled to a non-integer value by setting the scale_factor) Parameters ---------- - dspinbox : QDoubleSpinBox + line_edit : QLineEdit min_label : QLabel max_label : QLabel min : float max : float step_size : float ''' - def __init__(self, dspinbox, min_label, max_label, minimum=0.00, maximum=1.00, step_size=1.00): + def __init__(self, line_edit, min_label, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, + step_size=1.0): QSlider.__init__(self) + self.line_edit = line_edit + self.min_label = min_label + self.max_label = max_label + self.minimum = minimum + self.maximum = maximum + self.scale_factor = scale_factor + self.step_size = step_size # Configure the QSlider - self.setMinimum(minimum) - self.setMaximum(maximum) + self.setRange(self.minimum, self.maximum) self.setOrientation(QtCore.Qt.Horizontal) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setTickPosition(QSlider.TicksBelow) - self.setSingleStep(step_size) - self.setTickInterval(step_size) + self.setSingleStep(self.step_size) + self.setTickInterval(self.step_size) + + # Connect the QSlider to the QLineEdit + self.sliderPressed.connect(self.update_line_edit) + self.sliderMoved.connect(self.update_line_edit) + self.sliderReleased.connect(self.update_line_edit) - # Connect the QSlider to the QDoubleSpinBox - self.sliderPressed.connect(self.update_dspinbox) - self.sliderMoved.connect(self.update_dspinbox) - self.sliderReleased.connect(self.update_dspinbox) + # Configure the Validator and QLineEdit + self.validator = QtGui.QDoubleValidator() + self.validator.setBottom(self.minimum) + self.validator.setTop(self.maximum) + self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) + self.validator.setLocale(QtCore.QLocale("en_US")) - # Configure the QDoubleSpinBox - self.dspinbox = dspinbox - self.dspinbox.setDecimals(3) - self.dspinbox.setMinimum(minimum) - self.dspinbox.setMaximum(maximum) + self.line_edit.setValidator(self.validator) + self.line_edit.setText(str(minimum)) + self.line_edit.setPlaceholderText(str(minimum)) - # Connect the QDoubleSpinBox to the QSlider - self.dspinbox.valueChanged.connect(self.update_slider) + # Connect the QLineEdit to the QSlider + self.line_edit.textEdited.connect(self.update_slider) # Configure QLabels for minimum and maximum QSlider values - self.min_label = min_label - self.max_label = max_label + # self.min_label = min_label + # self.max_label = max_label def get_slider_value(self): return self.value() - def get_dspinbox_value(self): - return self.dspinbox.value() + def get_line_edit_value(self): + return float(self.line_edit.text()) def update_slider(self): - dspinbox_value = int(self.get_dspinbox_value()) - self.setValue(dspinbox_value) + state = self.validator.validate(self.line_edit.text(), 0) + if state[0] == QtGui.QDoubleValidator.Acceptable: + line_edit_value = self.get_line_edit_value() + self.setValue(line_edit_value) + else: + self.line_edit.setText(str(self.minimum)) + line_edit_value = self.get_line_edit_value() + self.setValue(line_edit_value) - def update_dspinbox(self): - slider_value = float(self.get_slider_value()) - self.dspinbox.setValue(slider_value) + def update_line_edit(self): + slider_value = str(float(self.get_slider_value())) + self.line_edit.setText(slider_value) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index b4ac222..929ba88 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -27,21 +27,20 @@ def openFormDialog(self): dialog.Ok.clicked.connect(lambda: self.accepted()) # Example on how to add elements to the FormDialog - # add input 1 as UISliderWidget and DoubleSpinBox - dspinbox = QtWidgets.QDoubleSpinBox() + # add input 1 as UISliderWidget and QLineEdit + line_edit = QtWidgets.QLineEdit() min_label = QtWidgets.QLabel() max_label = QtWidgets.QLabel() - uislider = UISliderWidget.UISliderWidget(dspinbox, min_label, max_label, minimum=0.00, - maximum=10.00, step_size=1.00) + uislider = UISliderWidget.UISliderWidget(line_edit, min_label, max_label, minimum=0.0, + maximum=10.0) # add to the form widget dialog.addWidget(uislider, 'QSlider 1:', 'input_slider1') - dialog.addWidget(dspinbox, '', 'input_dspinbox1') + dialog.addWidget(line_edit, '', 'input_line_edit1') # add input 2 as UISliderLineEditWidget - dspinbox = QtWidgets.QDoubleSpinBox() - uislider = UISliderLineEditWidget.UISliderLineEditWidget(minimum=0.00, maximum=10.00, - step_size=2.00) + uislider = UISliderLineEditWidget.UISliderLineEditWidget(minimum=0.0, maximum=10.0, + step_size=2.0) # add to the form widget dialog.addWidget(uislider, 'QSlider 2:', 'input_slider2') @@ -54,9 +53,9 @@ def openFormDialog(self): def accepted(self): print("accepted") print(f"QSlider 1 Value: {self.dialog.widgets['input_slider1_field'].value()}") - print(f"QDoubleSpinBox 1 Value: {self.dialog.widgets['input_dspinbox1_field'].value()}") + print(f"QLineEdit 1 Value: {self.dialog.widgets['input_line_edit1_field'].text()}") - print(f"QSlider 2 Value: {self.dialog.widgets['input_slider2_field'].value()}") + # print(f"QSlider 2 Value: {self.dialog.widgets['input_slider2_field'].value()}") self.dialog.close() From d255d49ce9a74a19ecc51e7167ed8372a3ff7751 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 19 Dec 2024 17:21:43 +0000 Subject: [PATCH 09/68] Replace QDoubleSpinBox in UISliderLEditWidget class with QLineEdit --- eqt/ui/UISliderLEditWidget.py | 88 +++++++++++++++++++++++++++++ eqt/ui/UISliderLineEditWidget.py | 69 ---------------------- eqt/ui/UISliderWidget.py | 8 ++- examples/dialog_example_uislider.py | 6 +- 4 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 eqt/ui/UISliderLEditWidget.py delete mode 100644 eqt/ui/UISliderLineEditWidget.py diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py new file mode 100644 index 0000000..20974ca --- /dev/null +++ b/eqt/ui/UISliderLEditWidget.py @@ -0,0 +1,88 @@ +from PySide2 import QtCore, QtGui +from PySide2.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider, QWidget + + +class UISliderLEditWidget(QWidget): + '''Creates a QGridLayout that includes a QSlider, min/max QLabels and a QLineEdit. + The QLineEdit is updated with the value of the slider and vice versa. + + Parameters + ---------- + min : float + max : float + step_size : float + scale_factor : float + ''' + def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): + QWidget.__init__(self) + + self.minimum = minimum + self.maximum = maximum + self.scale_factor = scale_factor + self.step_size = step_size + + # Configure the QSlider + self.slider = QSlider() + self.slider.setRange(self.minimum, self.maximum) + self.slider.setOrientation(QtCore.Qt.Horizontal) + self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) + self.slider.setTickPosition(QSlider.TicksBelow) + self.slider.setSingleStep(self.step_size) + self.slider.setTickInterval(self.step_size) + + # Connect the QSlider to the QLineEdit + self.slider.sliderPressed.connect(self.update_line_edit) + self.slider.sliderMoved.connect(self.update_line_edit) + self.slider.sliderReleased.connect(self.update_line_edit) + + # Configure the QDoubleValidator and QLineEdit + self.validator = QtGui.QDoubleValidator() + self.validator.setBottom(self.minimum) + self.validator.setTop(self.maximum) + self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) + self.validator.setLocale(QtCore.QLocale("en_US")) + + self.line_edit = QLineEdit() + self.line_edit.setValidator(self.validator) + self.line_edit.setText(str(minimum)) + self.line_edit.setPlaceholderText(str(minimum)) + + # Connect the QLineEdit to the QSlider + self.line_edit.textEdited.connect(self.update_slider) + + # Configure the min/max QLabels + self.min_label = QLabel() + self.min_label.setText(str(self.minimum)) + self.max_label = QLabel() + self.max_label.setText(str(self.maximum)) + + # Configure the QGridLayout + widget_layout = QGridLayout() + widget_layout.addWidget(self.slider, 0, 0, 1, -1) + widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) + widget_layout.addWidget(self.max_label, 1, 1, QtCore.Qt.AlignRight) + widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) + + # Set the layout + self.setLayout(widget_layout) + self.show() + + def get_slider_value(self): + return self.slider.value() + + def get_line_edit_value(self): + return float(self.line_edit.text()) + + def update_slider(self): + state = self.validator.validate(self.line_edit.text(), 0) + if state[0] == QtGui.QDoubleValidator.Acceptable: + line_edit_value = self.get_line_edit_value() + self.slider.setValue(line_edit_value) + else: + self.line_edit.setText(str(self.minimum)) + line_edit_value = self.get_line_edit_value() + self.slider.setValue(line_edit_value) + + def update_line_edit(self): + slider_value = str(float(self.get_slider_value())) + self.line_edit.setText(slider_value) diff --git a/eqt/ui/UISliderLineEditWidget.py b/eqt/ui/UISliderLineEditWidget.py deleted file mode 100644 index 39fad74..0000000 --- a/eqt/ui/UISliderLineEditWidget.py +++ /dev/null @@ -1,69 +0,0 @@ -from PySide2 import QtCore -from PySide2.QtWidgets import QDoubleSpinBox, QGridLayout, QLabel, QSlider, QWidget - - -class UISliderLineEditWidget(QWidget): - '''Creates a QGridLayout that includes a QSlider, min/max QLabels and a QDoubleSpinBox. - The QDoubleSpinBox is updated with the value of the slider and vice versa. - - Parameters - ---------- - dspinbox : QDoubleSpinBox - min : float - max : float - step_size : float - ''' - def __init__(self, minimum=0.00, maximum=1.00, step_size=1.00): - QWidget.__init__(self) - - # Configure the QSlider - self.slider = QSlider() - self.slider.setMinimum(minimum) - self.slider.setMaximum(maximum) - self.slider.setOrientation(QtCore.Qt.Horizontal) - self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) - self.slider.setTickPosition(QSlider.TicksBelow) - self.slider.setSingleStep(step_size) - self.slider.setTickInterval(step_size) - - # Connect the QSlider to the QDoubleSpinBox - self.slider.sliderPressed.connect(self.update_dspinbox) - self.slider.sliderMoved.connect(self.update_dspinbox) - self.slider.sliderReleased.connect(self.update_dspinbox) - - # Connect the QDoubleSpinBox - self.dspinbox = QDoubleSpinBox() - self.dspinbox.valueChanged.connect(self.update_slider) - self.dspinbox.setMinimum(minimum) - self.dspinbox.setMaximum(maximum) - - # Configure the min/max QLabels - self.min_label = QLabel() - self.min_label.setText(str(self.slider.minimum())) - self.max_label = QLabel() - self.max_label.setText(str(self.slider.maximum())) - - # Configure the QGridLayout - widget_layout = QGridLayout() - widget_layout.addWidget(self.slider, 0, 0, 1, -1) - widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) - widget_layout.addWidget(self.max_label, 1, 1, QtCore.Qt.AlignRight) - widget_layout.addWidget(self.dspinbox, 2, 0, 1, -1) - - # Set the layout - self.setLayout(widget_layout) - self.show() - - def get_slider_value(self): - return self.slider.value() - - def get_dspinbox_value(self): - return self.dspinbox.value() - - def update_slider(self): - dspinbox_value = int(self.get_dspinbox_value()) - self.slider.setValue(dspinbox_value) - - def update_dspinbox(self): - slider_value = float(self.get_slider_value()) - self.dspinbox.setValue(slider_value) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 2a39886..f8717f5 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -12,13 +12,15 @@ class UISliderWidget(QSlider): line_edit : QLineEdit min_label : QLabel max_label : QLabel - min : float - max : float + minimum : float + maximum : float step_size : float + scale_factor : float ''' def __init__(self, line_edit, min_label, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, step_size=1.0): QSlider.__init__(self) + self.line_edit = line_edit self.min_label = min_label self.max_label = max_label @@ -40,7 +42,7 @@ def __init__(self, line_edit, min_label, max_label, minimum=0.0, maximum=1.0, sc self.sliderMoved.connect(self.update_line_edit) self.sliderReleased.connect(self.update_line_edit) - # Configure the Validator and QLineEdit + # Configure the QDoubleValidator and QLineEdit self.validator = QtGui.QDoubleValidator() self.validator.setBottom(self.minimum) self.validator.setTop(self.maximum) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 929ba88..f4e373f 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -2,7 +2,7 @@ from PySide2 import QtWidgets -from eqt.ui import FormDialog, UISliderLineEditWidget, UISliderWidget +from eqt.ui import FormDialog, UISliderLEditWidget, UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -39,8 +39,8 @@ def openFormDialog(self): dialog.addWidget(line_edit, '', 'input_line_edit1') # add input 2 as UISliderLineEditWidget - uislider = UISliderLineEditWidget.UISliderLineEditWidget(minimum=0.0, maximum=10.0, - step_size=2.0) + uislider = UISliderLEditWidget.UISliderLEditWidget(minimum=0.0, maximum=10.0, + step_size=1.0) # add to the form widget dialog.addWidget(uislider, 'QSlider 2:', 'input_slider2') From e93a971ded7a77c33a5081b61f02f75df216f6e7 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 20 Dec 2024 09:44:51 +0000 Subject: [PATCH 10/68] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff6334..cd1ee7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ +# Version x.x.x +- Add `QDoubleSpinBox` support to `UISliderWidget` (#168) + + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` + # Version 1.0.2 - Upgrade python to 3.8 in `test.yml` (#171) - Rename `/scripts` directory to `/recipe` (#161) - Update `CONTRIBUTING.md` with detailed installation and contribution instructions (#161) - Limit Python version to <3.12 in conda recipe (#161) - Change SessionDirectorySelectionDialog `.open()` call to `.exec()` (#163, #165) -- Add `QDoubleSpinBox` support to `UISliderWidget` (#168) - + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` # Version 1.0.1 - Add NoBorderScrollArea, example and tests (#155) From a375e9ba9ad7b9254fb2b54b6f2a0338b8d21b3c Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 20 Dec 2024 11:45:27 +0000 Subject: [PATCH 11/68] Add QLabel to UISliderWidget to display max slider value --- eqt/ui/UISliderLEditWidget.py | 1 + eqt/ui/UISliderWidget.py | 18 ++++++++++-------- examples/dialog_example_uislider.py | 9 +++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index 20974ca..1f22f86 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -49,6 +49,7 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): # Connect the QLineEdit to the QSlider self.line_edit.textEdited.connect(self.update_slider) + self.line_edit.returnPressed.connect(self.update_slider) # Configure the min/max QLabels self.min_label = QLabel() diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index f8717f5..66109a1 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -6,23 +6,23 @@ class UISliderWidget(QSlider): '''Creates a Slider widget which updates a QLineEdit with its value (which may be scaled to a non-integer value by setting the scale_factor) + Also accepts a QLabel that is configured to + display the maximum value of the QSlider Parameters ---------- line_edit : QLineEdit - min_label : QLabel max_label : QLabel minimum : float maximum : float step_size : float scale_factor : float ''' - def __init__(self, line_edit, min_label, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, + def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, step_size=1.0): QSlider.__init__(self) self.line_edit = line_edit - self.min_label = min_label self.max_label = max_label self.minimum = minimum self.maximum = maximum @@ -50,15 +50,17 @@ def __init__(self, line_edit, min_label, max_label, minimum=0.0, maximum=1.0, sc self.validator.setLocale(QtCore.QLocale("en_US")) self.line_edit.setValidator(self.validator) - self.line_edit.setText(str(minimum)) - self.line_edit.setPlaceholderText(str(minimum)) + self.line_edit.setText(str(self.minimum)) + self.line_edit.setPlaceholderText(str(self.minimum)) # Connect the QLineEdit to the QSlider self.line_edit.textEdited.connect(self.update_slider) + self.line_edit.returnPressed.connect(self.update_slider) - # Configure QLabels for minimum and maximum QSlider values - # self.min_label = min_label - # self.max_label = max_label + # Configure QLabel to show maximum QSlider value + self.max_label = max_label + self.max_label.setAlignment(QtCore.Qt.AlignRight) + self.max_label.setText(str(self.maximum)) def get_slider_value(self): return self.value() diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index f4e373f..4e4921b 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -29,17 +29,17 @@ def openFormDialog(self): # Example on how to add elements to the FormDialog # add input 1 as UISliderWidget and QLineEdit line_edit = QtWidgets.QLineEdit() - min_label = QtWidgets.QLabel() max_label = QtWidgets.QLabel() - uislider = UISliderWidget.UISliderWidget(line_edit, min_label, max_label, minimum=0.0, - maximum=10.0) + uislider = UISliderWidget.UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, + step_size=1.0) # add to the form widget dialog.addWidget(uislider, 'QSlider 1:', 'input_slider1') + dialog.addWidget(max_label, '', 'input_max_label1') dialog.addWidget(line_edit, '', 'input_line_edit1') # add input 2 as UISliderLineEditWidget - uislider = UISliderLEditWidget.UISliderLEditWidget(minimum=0.0, maximum=10.0, + uislider = UISliderLEditWidget.UISliderLEditWidget(minimum=0.0, maximum=100.0, step_size=1.0) # add to the form widget @@ -53,6 +53,7 @@ def openFormDialog(self): def accepted(self): print("accepted") print(f"QSlider 1 Value: {self.dialog.widgets['input_slider1_field'].value()}") + print(f"QLabel 1 Value: {self.dialog.widgets['input_max_label1_field'].text()}") print(f"QLineEdit 1 Value: {self.dialog.widgets['input_line_edit1_field'].text()}") # print(f"QSlider 2 Value: {self.dialog.widgets['input_slider2_field'].value()}") From 1313217da67d5bfc3c1c456431e5cb5d283a6160 Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 23 Dec 2024 10:37:11 +0000 Subject: [PATCH 12/68] Add tick_interval attribute to UISliderWidget class --- eqt/ui/UISliderLEditWidget.py | 7 ++++--- examples/dialog_example_uislider.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index 1f22f86..81778a0 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -20,6 +20,7 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): self.maximum = maximum self.scale_factor = scale_factor self.step_size = step_size + self.tick_interval = self.step_size * self.scale_factor # Configure the QSlider self.slider = QSlider() @@ -72,16 +73,16 @@ def get_slider_value(self): return self.slider.value() def get_line_edit_value(self): - return float(self.line_edit.text()) + return self.line_edit.text() def update_slider(self): state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = self.get_line_edit_value() + line_edit_value = float(self.get_line_edit_value()) self.slider.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = self.get_line_edit_value() + line_edit_value = float(self.get_line_edit_value()) self.slider.setValue(line_edit_value) def update_line_edit(self): diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 4e4921b..1fd8f6a 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -31,7 +31,7 @@ def openFormDialog(self): line_edit = QtWidgets.QLineEdit() max_label = QtWidgets.QLabel() uislider = UISliderWidget.UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, - step_size=1.0) + scale_factor=10.0) # add to the form widget dialog.addWidget(uislider, 'QSlider 1:', 'input_slider1') From f7517ab7ef3fd663a43ed638901c1bfa11133932 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 2 Jan 2025 12:31:28 +0000 Subject: [PATCH 13/68] Reformat method names to use camel case, add value getters/setters --- eqt/ui/UISliderLEditWidget.py | 52 +++++++++++++++++++---------- eqt/ui/UISliderWidget.py | 26 +++++++-------- examples/dialog_example_uislider.py | 15 +++++---- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index 81778a0..a66af45 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -19,8 +19,7 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): self.minimum = minimum self.maximum = maximum self.scale_factor = scale_factor - self.step_size = step_size - self.tick_interval = self.step_size * self.scale_factor + self.step_size = step_size * self.scale_factor # Configure the QSlider self.slider = QSlider() @@ -29,12 +28,12 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) self.slider.setTickPosition(QSlider.TicksBelow) self.slider.setSingleStep(self.step_size) - self.slider.setTickInterval(self.step_size) + self.slider.setTickInterval(self.maximum * 0.25) # Connect the QSlider to the QLineEdit - self.slider.sliderPressed.connect(self.update_line_edit) - self.slider.sliderMoved.connect(self.update_line_edit) - self.slider.sliderReleased.connect(self.update_line_edit) + self.slider.sliderPressed.connect(self.updateLineEdit) + self.slider.sliderMoved.connect(self.updateLineEdit) + self.slider.sliderReleased.connect(self.updateLineEdit) # Configure the QDoubleValidator and QLineEdit self.validator = QtGui.QDoubleValidator() @@ -49,42 +48,61 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): self.line_edit.setPlaceholderText(str(minimum)) # Connect the QLineEdit to the QSlider - self.line_edit.textEdited.connect(self.update_slider) - self.line_edit.returnPressed.connect(self.update_slider) + self.line_edit.textEdited.connect(self.updateSlider) + self.line_edit.returnPressed.connect(self.updateSlider) - # Configure the min/max QLabels + # Configure QLabels self.min_label = QLabel() self.min_label.setText(str(self.minimum)) + self.median_label = QLabel() + self.median_label.setText(str(self.maximum * 0.5)) self.max_label = QLabel() self.max_label.setText(str(self.maximum)) + # Configure quartile QLabels + # self.lowerq_label = QLabel() + # self.lowerq_label.setText(str(self.maximum * 0.25)) + # self.upperq_label = QLabel() + # self.upperq_label.setText(str(self.maximum * 0.75)) + # Configure the QGridLayout widget_layout = QGridLayout() widget_layout.addWidget(self.slider, 0, 0, 1, -1) widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) - widget_layout.addWidget(self.max_label, 1, 1, QtCore.Qt.AlignRight) + # widget_layout.addWidget(self.lowerq_label, 1, 1, QtCore.Qt.AlignLeft) + widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) + # widget_layout.addWidget(self.upperq_label, 1, 3, QtCore.Qt.AlignRight) + widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) # Set the layout self.setLayout(widget_layout) self.show() - def get_slider_value(self): + def getValue(self): + return float(self.line_edit.text()) + + def setValue(self, value): + self.line_edit.setText(str(value)) + + def getSliderValue(self): return self.slider.value() - def get_line_edit_value(self): + def getLineEditValue(self): return self.line_edit.text() - def update_slider(self): + def updateSlider(self): state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = float(self.get_line_edit_value()) + line_edit_value = float(self.getLineEditValue()) self.slider.setValue(line_edit_value) + self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = float(self.get_line_edit_value()) + line_edit_value = float(self.getLineEditValue()) self.slider.setValue(line_edit_value) + self.setValue(line_edit_value) - def update_line_edit(self): - slider_value = str(float(self.get_slider_value())) + def updateLineEdit(self): + slider_value = str(float(self.getSliderValue())) self.line_edit.setText(slider_value) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 66109a1..e54b2cb 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -27,7 +27,7 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.minimum = minimum self.maximum = maximum self.scale_factor = scale_factor - self.step_size = step_size + self.step_size = step_size * self.scale_factor # Configure the QSlider self.setRange(self.minimum, self.maximum) @@ -38,9 +38,9 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.setTickInterval(self.step_size) # Connect the QSlider to the QLineEdit - self.sliderPressed.connect(self.update_line_edit) - self.sliderMoved.connect(self.update_line_edit) - self.sliderReleased.connect(self.update_line_edit) + self.sliderPressed.connect(self.updateLineEdit) + self.sliderMoved.connect(self.updateLineEdit) + self.sliderReleased.connect(self.updateLineEdit) # Configure the QDoubleValidator and QLineEdit self.validator = QtGui.QDoubleValidator() @@ -54,30 +54,30 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.line_edit.setPlaceholderText(str(self.minimum)) # Connect the QLineEdit to the QSlider - self.line_edit.textEdited.connect(self.update_slider) - self.line_edit.returnPressed.connect(self.update_slider) + self.line_edit.textEdited.connect(self.updateSlider) + self.line_edit.returnPressed.connect(self.updateSlider) # Configure QLabel to show maximum QSlider value self.max_label = max_label self.max_label.setAlignment(QtCore.Qt.AlignRight) self.max_label.setText(str(self.maximum)) - def get_slider_value(self): + def getSliderValue(self): return self.value() - def get_line_edit_value(self): + def getLineEditValue(self): return float(self.line_edit.text()) - def update_slider(self): + def updateSlider(self): state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = self.get_line_edit_value() + line_edit_value = self.getLineEditValue() self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = self.get_line_edit_value() + line_edit_value = self.getLineEditValue() self.setValue(line_edit_value) - def update_line_edit(self): - slider_value = str(float(self.get_slider_value())) + def updateLineEdit(self): + slider_value = str(float(self.getSliderValue())) self.line_edit.setText(slider_value) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 1fd8f6a..08fdded 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -34,16 +34,16 @@ def openFormDialog(self): scale_factor=10.0) # add to the form widget - dialog.addWidget(uislider, 'QSlider 1:', 'input_slider1') + dialog.addWidget(uislider, 'UISlider 1:', 'input_slider1') dialog.addWidget(max_label, '', 'input_max_label1') dialog.addWidget(line_edit, '', 'input_line_edit1') # add input 2 as UISliderLineEditWidget uislider = UISliderLEditWidget.UISliderLEditWidget(minimum=0.0, maximum=100.0, - step_size=1.0) + scale_factor=10.0) # add to the form widget - dialog.addWidget(uislider, 'QSlider 2:', 'input_slider2') + dialog.addWidget(uislider, 'UISlider 2:', 'input_slider2') # store a reference self.dialog = dialog @@ -52,11 +52,12 @@ def openFormDialog(self): def accepted(self): print("accepted") - print(f"QSlider 1 Value: {self.dialog.widgets['input_slider1_field'].value()}") - print(f"QLabel 1 Value: {self.dialog.widgets['input_max_label1_field'].text()}") - print(f"QLineEdit 1 Value: {self.dialog.widgets['input_line_edit1_field'].text()}") + print(f"UISlider 1 QSlider: {self.dialog.widgets['input_slider1_field'].value()}") + print(f"QSlider 1 QLabel: {self.dialog.widgets['input_max_label1_field'].text()}") + print(f"QSlider 1 QLineEdit: {self.dialog.widgets['input_line_edit1_field'].text()}") - # print(f"QSlider 2 Value: {self.dialog.widgets['input_slider2_field'].value()}") + print(f"UISlider 2 QSlider: {self.dialog.widgets['input_slider2_field'].getSliderValue()}") + print(f"UISlider 2 QLineEdit: {self.dialog.widgets['input_slider2_field'].getValue()}") self.dialog.close() From 5c595bf09f2ec8b734112dadd7caa873c6bb636f Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 2 Jan 2025 15:28:30 +0000 Subject: [PATCH 14/68] Add UISliderLEditWidget case to getWidgetState() and applyWidgetState() --- eqt/ui/UIFormWidget.py | 6 ++++++ eqt/ui/UISliderLEditWidget.py | 6 ++++-- eqt/ui/UISliderWidget.py | 5 +++-- examples/dialog_save_state_example.py | 21 ++++++++++++++++++--- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 3e067db..10eb4c7 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -2,6 +2,7 @@ from PySide2 import QtWidgets +from .UISliderLEditWidget import UISliderLEditWidget from .UISliderWidget import UISliderWidget @@ -335,6 +336,8 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() + elif isinstance(widget, UISliderLEditWidget) or isinstance(widget, QtWidgets.QWidget): + widget_state['value'] = widget.getValue() widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -420,6 +423,9 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) + elif isinstance(widget, UISliderLEditWidget) or isinstance( + widget, QtWidgets.QWidget): + widget.setValue(value) def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index a66af45..e33a769 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -13,13 +13,15 @@ class UISliderLEditWidget(QWidget): step_size : float scale_factor : float ''' - def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): + def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0, + tick_interval=1.0): QWidget.__init__(self) self.minimum = minimum self.maximum = maximum self.scale_factor = scale_factor self.step_size = step_size * self.scale_factor + self.tick_interval = tick_interval * self.scale_factor # Configure the QSlider self.slider = QSlider() @@ -28,7 +30,7 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0): self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) self.slider.setTickPosition(QSlider.TicksBelow) self.slider.setSingleStep(self.step_size) - self.slider.setTickInterval(self.maximum * 0.25) + self.slider.setTickInterval(self.tick_interval) # Connect the QSlider to the QLineEdit self.slider.sliderPressed.connect(self.updateLineEdit) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index e54b2cb..1d5bd3f 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -19,7 +19,7 @@ class UISliderWidget(QSlider): scale_factor : float ''' def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, - step_size=1.0): + step_size=1.0, tick_interval=1.0): QSlider.__init__(self) self.line_edit = line_edit @@ -28,6 +28,7 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.maximum = maximum self.scale_factor = scale_factor self.step_size = step_size * self.scale_factor + self.tick_interval = tick_interval * self.scale_factor # Configure the QSlider self.setRange(self.minimum, self.maximum) @@ -35,7 +36,7 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setTickPosition(QSlider.TicksBelow) self.setSingleStep(self.step_size) - self.setTickInterval(self.step_size) + self.setTickInterval(self.tick_interval) # Connect the QSlider to the QLineEdit self.sliderPressed.connect(self.updateLineEdit) diff --git a/examples/dialog_save_state_example.py b/examples/dialog_save_state_example.py index d0ccf2c..420cde5 100644 --- a/examples/dialog_save_state_example.py +++ b/examples/dialog_save_state_example.py @@ -3,6 +3,7 @@ from PySide2 import QtWidgets from eqt.ui import FormDialog +from eqt.ui.UISliderLEditWidget import UISliderLEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -27,18 +28,32 @@ def __init__(self, parent=None): # ## Example on how to add elements to the dialog.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') dialog.addWidget(QtWidgets.QCheckBox('test checkbox'), 'CheckBox: ', 'checkBox') + combobox = QtWidgets.QComboBox() combobox.addItems(['test1', 'test2']) dialog.addWidget(combobox, 'ComboBox: ', 'comboBox') + dialog.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') dialog.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') dialog.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - dialog.addWidget(UISliderWidget(QtWidgets.QLabel()), 'UISliderWidget: ', 'uiSliderWidget') - dialog.addWidget(QtWidgets.QRadioButton('test 1'), 'RadioButton 1: ', 'radioButton') - dialog.addWidget(QtWidgets.QRadioButton('test 2'), 'RadioButton 2: ', 'radioButton') + + line_edit = QtWidgets.QLineEdit() + max_label = QtWidgets.QLabel() + dialog.addWidget( + UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderWidget: ', 'uiSliderWidget') + dialog.addWidget(max_label, '', 'input_max_label1') + dialog.addWidget(line_edit, '', 'input_line_edit1') + + dialog.addWidget(UISliderLEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderLEditWidget:', 'uiSliderLEditWidget') + + dialog.addWidget(QtWidgets.QRadioButton('test 1'), 'RadioButton 1: ', 'radioButton1') + dialog.addWidget(QtWidgets.QRadioButton('test 2'), 'RadioButton 2: ', 'radioButton2') dialog.addWidget(QtWidgets.QTextEdit('test'), 'TextEdit: ', 'textEdit') dialog.addWidget(QtWidgets.QPlainTextEdit('test'), 'PlainTextEdit: ', 'plainTextEdit') dialog.addWidget(QtWidgets.QLineEdit('test'), 'LineEdit: ', 'lineEdit') + button = QtWidgets.QPushButton('test') button.setCheckable(True) dialog.addWidget(button, 'Button: ', 'button') From 1545425117ba8e6701c7f17c43415a13c3548850 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 3 Jan 2025 11:55:14 +0000 Subject: [PATCH 15/68] Update examples and tests to include UISliderLEditWidget --- eqt/ui/UIFormWidget.py | 11 ++++------ eqt/ui/UISliderLEditWidget.py | 8 +++---- eqt/ui/UISliderWidget.py | 3 +++ examples/utilitiesForExamples.py | 17 +++++++++++++-- test/test__formUI_status_test.py | 37 ++++++++++++++++++++++++++------ 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 10eb4c7..2fdcd98 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -326,7 +326,9 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, QtWidgets.QComboBox): widget_state['value'] = widget.currentIndex() - elif isinstance(widget, UISliderWidget) or isinstance(widget, QtWidgets.QSlider): + elif isinstance(widget, UISliderWidget) or isinstance(widget, UISliderLEditWidget): + widget_state['value'] = widget.getValue() + elif isinstance(widget, QtWidgets.QSlider): widget_state['value'] = widget.value() elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget_state['value'] = widget.value() @@ -336,8 +338,6 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() - elif isinstance(widget, UISliderLEditWidget) or isinstance(widget, QtWidgets.QWidget): - widget_state['value'] = widget.getValue() widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -411,7 +411,7 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, QtWidgets.QComboBox): widget.setCurrentIndex(value) - elif isinstance(widget, (UISliderWidget, QtWidgets.QSlider)): + elif isinstance(widget, (UISliderWidget, UISliderLEditWidget, QtWidgets.QSlider)): widget.setValue(value) elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget.setValue(value) @@ -423,9 +423,6 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) - elif isinstance(widget, UISliderLEditWidget) or isinstance( - widget, QtWidgets.QWidget): - widget.setValue(value) def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index e33a769..6ed6fb2 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -82,7 +82,7 @@ def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0, self.show() def getValue(self): - return float(self.line_edit.text()) + return self.getLineEditValue() def setValue(self, value): self.line_edit.setText(str(value)) @@ -91,17 +91,17 @@ def getSliderValue(self): return self.slider.value() def getLineEditValue(self): - return self.line_edit.text() + return float(self.line_edit.text()) def updateSlider(self): state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = float(self.getLineEditValue()) + line_edit_value = self.getLineEditValue() self.slider.setValue(line_edit_value) self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = float(self.getLineEditValue()) + line_edit_value = self.getLineEditValue() self.slider.setValue(line_edit_value) self.setValue(line_edit_value) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 1d5bd3f..d9197dd 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -63,6 +63,9 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.max_label.setAlignment(QtCore.Qt.AlignRight) self.max_label.setText(str(self.maximum)) + def getValue(self): + return self.getLineEditValue() + def getSliderValue(self): return self.value() diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py index 4f409f5..4d64d6d 100644 --- a/examples/utilitiesForExamples.py +++ b/examples/utilitiesForExamples.py @@ -1,5 +1,6 @@ from PySide2 import QtWidgets +from eqt.ui.UISliderLEditWidget import UISliderLEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -8,7 +9,8 @@ def list_all_widgets(): 'label': QtWidgets.QLabel('test label'), 'checkBox': QtWidgets.QCheckBox('test checkbox'), 'comboBox': QtWidgets.QComboBox(), 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), - 'uiSliderWidget': UISliderWidget(QtWidgets.QLabel()), + 'uiSliderWidget': UISliderWidget(QtWidgets.QLineEdit(), QtWidgets.QLabel()), + 'uiSliderLEditWidget': UISliderLEditWidget(), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -33,7 +35,18 @@ def addWidgetsToExample(form): form.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - form.addWidget(UISliderWidget(QtWidgets.QLabel()), 'UISlider: ', 'uiSliderWidget') + + line_edit = QtWidgets.QLineEdit() + max_label = QtWidgets.QLabel() + form.addWidget( + UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderWidget: ', 'uiSliderWidget') + form.addWidget(max_label, '', 'uiSliderMaxLabel') + form.addWidget(line_edit, '', 'uiSliderLineEdit') + + form.addWidget(UISliderLEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderLEditWidget:', 'uiSliderLEditWidget') + form.addWidget(QtWidgets.QRadioButton('select me'), 'RadioButton: ', 'radioButton') form.addWidget(QtWidgets.QTextEdit('write text here'), 'TextEdit: ', 'textEdit') form.addWidget(QtWidgets.QPlainTextEdit('write text here'), 'PlainTextEdit: ', 'plainTextEdit') diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index f5600e7..5d059df 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -8,6 +8,7 @@ from eqt.ui.FormDialog import AdvancedFormDialog, FormDialog from eqt.ui.UIFormWidget import FormDockWidget, FormWidget +from eqt.ui.UISliderLEditWidget import UISliderLEditWidget from eqt.ui.UISliderWidget import UISliderWidget from . import is_ci, skip @@ -33,14 +34,16 @@ def exampleState(self): state = [{ 'label_value': 'Test label state 0', 'checkBox_value': False, 'comboBox_value': 0, 'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10, - 'uiSliderWidget_value': 10, 'radioButton_value': False, - 'textEdit_value': 'test edit 0', 'plainTextEdit_value': 'test plain 0', - 'lineEdit_value': 'test line 0', 'button_value': False}, { + 'uiSliderWidget_value': 10, 'uiSliderLEditWidget_value': 10.0, + 'radioButton_value': False, 'textEdit_value': 'test edit 0', + 'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0', + 'button_value': False}, { 'label_value': 'Test label state 1', 'checkBox_value': True, 'comboBox_value': 1, 'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1, - 'uiSliderWidget_value': 1, 'radioButton_value': True, - 'textEdit_value': 'test edit 1', 'plainTextEdit_value': 'test plain 1', - 'lineEdit_value': 'test line 1', 'button_value': True}] + 'uiSliderWidget_value': 1, 'uiSliderLEditWidget_value': 1.0, + 'radioButton_value': True, 'textEdit_value': 'test edit 1', + 'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1', + 'button_value': True}] return state @property @@ -52,7 +55,9 @@ def list_all_widgets(self): 'label': QtWidgets.QLabel('test label'), 'checkBox': QtWidgets.QCheckBox('test checkbox'), 'comboBox': combobox_widget, 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), - 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget(QtWidgets.QLabel()), + 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget( + QtWidgets.QLineEdit(), + QtWidgets.QLabel()), 'uiSliderLEditWidget': UISliderLEditWidget(), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -106,6 +111,8 @@ def set_state(self, i: int): self.form.getWidget('slider').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget').setValue(state[i]['uiSliderWidget_value']) + # UISliderLEditWidget + self.form.getWidget('uiSliderLEditWidget').setValue(state[i]['uiSliderLEditWidget_value']) # QRadioButton self.form.getWidget('radioButton').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -141,6 +148,9 @@ def set_spanning_state(self, i: int): self.form.getWidget('slider_spanning').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget_spanning').setValue(state[i]['uiSliderWidget_value']) + # UISliderLEditWidget + self.form.getWidget('uiSliderLEditWidget_spanning').setValue( + state[i]['uiSliderLEditWidget_value']) # QRadioButton self.form.getWidget('radioButton_spanning').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -374,6 +384,19 @@ def test_getWidgetState_returns_UISliderWidget_value(self): self.assertEqual( self.form.getWidgetState('uiSliderWidget_field')['value'], final_slider_value) + def test_getWidgetState_returns_UISliderLEditWidget_value(self): + """Check that the value of the UISliderLEditWidget is returned in the state""" + initial_slider_value = 0 + + self.assertEqual( + self.form.getWidgetState('uiSliderLEditWidget_field')['value'], initial_slider_value) + + final_slider_value = 1 + self.form.getWidget('UISliderLEditWidget').setValue(final_slider_value) + + self.assertEqual( + self.form.getWidgetState('uiSliderLEditWidget_field')['value'], final_slider_value) + def test_getWidgetState_returns_QLineEdit_value(self): """Check that the value of the QLineEdit is saved to the state""" initial_lineEdit_value = '' From 0545d5940807f1475170637852fdc6f9a3f96aea Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 3 Jan 2025 16:46:03 +0000 Subject: [PATCH 16/68] Update tests and change defaults for UISlider classes --- eqt/ui/UIFormWidget.py | 12 ++++++++---- eqt/ui/UISliderLEditWidget.py | 2 +- eqt/ui/UISliderWidget.py | 5 +---- test/test__formUI_status_test.py | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 2fdcd98..e8b1cb3 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -326,9 +326,7 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, QtWidgets.QComboBox): widget_state['value'] = widget.currentIndex() - elif isinstance(widget, UISliderWidget) or isinstance(widget, UISliderLEditWidget): - widget_state['value'] = widget.getValue() - elif isinstance(widget, QtWidgets.QSlider): + elif isinstance(widget, UISliderWidget) or isinstance(widget, QtWidgets.QSlider): widget_state['value'] = widget.value() elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget_state['value'] = widget.value() @@ -338,6 +336,9 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() + elif isinstance(widget, UISliderLEditWidget) or isinstance(widget, QtWidgets.QWidget): + widget_state['value'] = widget.getValue() + # isinstance(QWidget) must be last due to other QtWidgets inheriting from it widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -411,7 +412,7 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, QtWidgets.QComboBox): widget.setCurrentIndex(value) - elif isinstance(widget, (UISliderWidget, UISliderLEditWidget, QtWidgets.QSlider)): + elif isinstance(widget, (UISliderWidget, QtWidgets.QSlider)): widget.setValue(value) elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget.setValue(value) @@ -423,6 +424,9 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) + elif isinstance(widget, (UISliderLEditWidget, QtWidgets.QWidget)): + widget.setValue(value) + # isinstance(QWidget) must be last due to other QtWidgets inheriting from it def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderLEditWidget.py index 6ed6fb2..d5268c9 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderLEditWidget.py @@ -13,7 +13,7 @@ class UISliderLEditWidget(QWidget): step_size : float scale_factor : float ''' - def __init__(self, minimum=0.0, maximum=1.0, step_size=1.0, scale_factor=1.0, + def __init__(self, minimum=0.0, maximum=10.0, step_size=1.0, scale_factor=1.0, tick_interval=1.0): QWidget.__init__(self) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index d9197dd..d876a44 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -18,7 +18,7 @@ class UISliderWidget(QSlider): step_size : float scale_factor : float ''' - def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor=1.0, + def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor=1.0, step_size=1.0, tick_interval=1.0): QSlider.__init__(self) @@ -63,9 +63,6 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=1.0, scale_factor= self.max_label.setAlignment(QtCore.Qt.AlignRight) self.max_label.setText(str(self.maximum)) - def getValue(self): - return self.getLineEditValue() - def getSliderValue(self): return self.value() diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 5d059df..577e4cf 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -392,7 +392,7 @@ def test_getWidgetState_returns_UISliderLEditWidget_value(self): self.form.getWidgetState('uiSliderLEditWidget_field')['value'], initial_slider_value) final_slider_value = 1 - self.form.getWidget('UISliderLEditWidget').setValue(final_slider_value) + self.form.getWidget('uiSliderLEditWidget').setValue(final_slider_value) self.assertEqual( self.form.getWidgetState('uiSliderLEditWidget_field')['value'], final_slider_value) From 9e6f42b49fe7afd15c5d8d721a298ac91c1cc86f Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 3 Jan 2025 19:13:52 +0000 Subject: [PATCH 17/68] Replace all instances of UISliderLEditWidget with UISliderEditWidget, update docstrings --- eqt/ui/UIFormWidget.py | 6 ++-- ...erLEditWidget.py => UISliderEditWidget.py} | 5 ++-- eqt/ui/UISliderWidget.py | 1 + examples/dialog_example_uislider.py | 6 ++-- examples/dialog_save_state_example.py | 6 ++-- examples/utilitiesForExamples.py | 8 +++--- test/test_UISliderEditWidget.py | 9 ++++++ test/test__formUI_status_test.py | 28 +++++++++---------- 8 files changed, 40 insertions(+), 29 deletions(-) rename eqt/ui/{UISliderLEditWidget.py => UISliderEditWidget.py} (96%) create mode 100644 test/test_UISliderEditWidget.py diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index e8b1cb3..b68325a 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -2,7 +2,7 @@ from PySide2 import QtWidgets -from .UISliderLEditWidget import UISliderLEditWidget +from .UISliderEditWidget import UISliderEditWidget from .UISliderWidget import UISliderWidget @@ -336,7 +336,7 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() - elif isinstance(widget, UISliderLEditWidget) or isinstance(widget, QtWidgets.QWidget): + elif isinstance(widget, UISliderEditWidget) or isinstance(widget, QtWidgets.QWidget): widget_state['value'] = widget.getValue() # isinstance(QWidget) must be last due to other QtWidgets inheriting from it widget_state['enabled'] = widget.isEnabled() @@ -424,7 +424,7 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) - elif isinstance(widget, (UISliderLEditWidget, QtWidgets.QWidget)): + elif isinstance(widget, (UISliderEditWidget, QtWidgets.QWidget)): widget.setValue(value) # isinstance(QWidget) must be last due to other QtWidgets inheriting from it diff --git a/eqt/ui/UISliderLEditWidget.py b/eqt/ui/UISliderEditWidget.py similarity index 96% rename from eqt/ui/UISliderLEditWidget.py rename to eqt/ui/UISliderEditWidget.py index d5268c9..8dd2cba 100644 --- a/eqt/ui/UISliderLEditWidget.py +++ b/eqt/ui/UISliderEditWidget.py @@ -2,8 +2,8 @@ from PySide2.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider, QWidget -class UISliderLEditWidget(QWidget): - '''Creates a QGridLayout that includes a QSlider, min/max QLabels and a QLineEdit. +class UISliderEditWidget(QWidget): + '''Creates a QGridLayout that includes a QSlider, min/median/max QLabels and a QLineEdit. The QLineEdit is updated with the value of the slider and vice versa. Parameters @@ -12,6 +12,7 @@ class UISliderLEditWidget(QWidget): max : float step_size : float scale_factor : float + tick_interval : float ''' def __init__(self, minimum=0.0, maximum=10.0, step_size=1.0, scale_factor=1.0, tick_interval=1.0): diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index d876a44..1c909f7 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -17,6 +17,7 @@ class UISliderWidget(QSlider): maximum : float step_size : float scale_factor : float + tick_interval : float ''' def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor=1.0, step_size=1.0, tick_interval=1.0): diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 08fdded..ddbf1f6 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -2,7 +2,7 @@ from PySide2 import QtWidgets -from eqt.ui import FormDialog, UISliderLEditWidget, UISliderWidget +from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -39,8 +39,8 @@ def openFormDialog(self): dialog.addWidget(line_edit, '', 'input_line_edit1') # add input 2 as UISliderLineEditWidget - uislider = UISliderLEditWidget.UISliderLEditWidget(minimum=0.0, maximum=100.0, - scale_factor=10.0) + uislider = UISliderEditWidget.UISliderEditWidget(minimum=0.0, maximum=100.0, + scale_factor=10.0) # add to the form widget dialog.addWidget(uislider, 'UISlider 2:', 'input_slider2') diff --git a/examples/dialog_save_state_example.py b/examples/dialog_save_state_example.py index 420cde5..496db5c 100644 --- a/examples/dialog_save_state_example.py +++ b/examples/dialog_save_state_example.py @@ -3,7 +3,7 @@ from PySide2 import QtWidgets from eqt.ui import FormDialog -from eqt.ui.UISliderLEditWidget import UISliderLEditWidget +from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -45,8 +45,8 @@ def __init__(self, parent=None): dialog.addWidget(max_label, '', 'input_max_label1') dialog.addWidget(line_edit, '', 'input_line_edit1') - dialog.addWidget(UISliderLEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderLEditWidget:', 'uiSliderLEditWidget') + dialog.addWidget(UISliderEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderEditWidget:', 'uiSliderEditWidget') dialog.addWidget(QtWidgets.QRadioButton('test 1'), 'RadioButton 1: ', 'radioButton1') dialog.addWidget(QtWidgets.QRadioButton('test 2'), 'RadioButton 2: ', 'radioButton2') diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py index 4d64d6d..b2b5a5c 100644 --- a/examples/utilitiesForExamples.py +++ b/examples/utilitiesForExamples.py @@ -1,6 +1,6 @@ from PySide2 import QtWidgets -from eqt.ui.UISliderLEditWidget import UISliderLEditWidget +from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -10,7 +10,7 @@ def list_all_widgets(): 'comboBox': QtWidgets.QComboBox(), 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget(QtWidgets.QLineEdit(), QtWidgets.QLabel()), - 'uiSliderLEditWidget': UISliderLEditWidget(), + 'uiSliderEditWidget': UISliderEditWidget(), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -44,8 +44,8 @@ def addWidgetsToExample(form): form.addWidget(max_label, '', 'uiSliderMaxLabel') form.addWidget(line_edit, '', 'uiSliderLineEdit') - form.addWidget(UISliderLEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderLEditWidget:', 'uiSliderLEditWidget') + form.addWidget(UISliderEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), + 'UISliderEditWidget:', 'uiSliderEditWidget') form.addWidget(QtWidgets.QRadioButton('select me'), 'RadioButton: ', 'radioButton') form.addWidget(QtWidgets.QTextEdit('write text here'), 'TextEdit: ', 'textEdit') diff --git a/test/test_UISliderEditWidget.py b/test/test_UISliderEditWidget.py new file mode 100644 index 0000000..e5d92ec --- /dev/null +++ b/test/test_UISliderEditWidget.py @@ -0,0 +1,9 @@ +import unittest + + +class UISliderEditWidget(unittest.TestCase): + def setUp(self): + return super().setUp() + + def tearDown(self): + return super().tearDown() diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 577e4cf..9249dea 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -8,7 +8,7 @@ from eqt.ui.FormDialog import AdvancedFormDialog, FormDialog from eqt.ui.UIFormWidget import FormDockWidget, FormWidget -from eqt.ui.UISliderLEditWidget import UISliderLEditWidget +from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget from . import is_ci, skip @@ -34,13 +34,13 @@ def exampleState(self): state = [{ 'label_value': 'Test label state 0', 'checkBox_value': False, 'comboBox_value': 0, 'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10, - 'uiSliderWidget_value': 10, 'uiSliderLEditWidget_value': 10.0, + 'uiSliderWidget_value': 10, 'uiSliderEditWidget_value': 10.0, 'radioButton_value': False, 'textEdit_value': 'test edit 0', 'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0', 'button_value': False}, { 'label_value': 'Test label state 1', 'checkBox_value': True, 'comboBox_value': 1, 'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1, - 'uiSliderWidget_value': 1, 'uiSliderLEditWidget_value': 1.0, + 'uiSliderWidget_value': 1, 'uiSliderEditWidget_value': 1.0, 'radioButton_value': True, 'textEdit_value': 'test edit 1', 'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1', 'button_value': True}] @@ -57,7 +57,7 @@ def list_all_widgets(self): 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget( QtWidgets.QLineEdit(), - QtWidgets.QLabel()), 'uiSliderLEditWidget': UISliderLEditWidget(), + QtWidgets.QLabel()), 'uiSliderEditWidget': UISliderEditWidget(), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -111,8 +111,8 @@ def set_state(self, i: int): self.form.getWidget('slider').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget').setValue(state[i]['uiSliderWidget_value']) - # UISliderLEditWidget - self.form.getWidget('uiSliderLEditWidget').setValue(state[i]['uiSliderLEditWidget_value']) + # UISliderEditWidget + self.form.getWidget('uiSliderEditWidget').setValue(state[i]['uiSliderEditWidget_value']) # QRadioButton self.form.getWidget('radioButton').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -148,9 +148,9 @@ def set_spanning_state(self, i: int): self.form.getWidget('slider_spanning').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget_spanning').setValue(state[i]['uiSliderWidget_value']) - # UISliderLEditWidget - self.form.getWidget('uiSliderLEditWidget_spanning').setValue( - state[i]['uiSliderLEditWidget_value']) + # UISliderEditWidget + self.form.getWidget('uiSliderEditWidget_spanning').setValue( + state[i]['uiSliderEditWidget_value']) # QRadioButton self.form.getWidget('radioButton_spanning').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -384,18 +384,18 @@ def test_getWidgetState_returns_UISliderWidget_value(self): self.assertEqual( self.form.getWidgetState('uiSliderWidget_field')['value'], final_slider_value) - def test_getWidgetState_returns_UISliderLEditWidget_value(self): - """Check that the value of the UISliderLEditWidget is returned in the state""" + def test_getWidgetState_returns_UISliderEditWidget_value(self): + """Check that the value of the UISliderEditWidget is returned in the state""" initial_slider_value = 0 self.assertEqual( - self.form.getWidgetState('uiSliderLEditWidget_field')['value'], initial_slider_value) + self.form.getWidgetState('uiSliderEditWidget_field')['value'], initial_slider_value) final_slider_value = 1 - self.form.getWidget('uiSliderLEditWidget').setValue(final_slider_value) + self.form.getWidget('uiSliderEditWidget').setValue(final_slider_value) self.assertEqual( - self.form.getWidgetState('uiSliderLEditWidget_field')['value'], final_slider_value) + self.form.getWidgetState('uiSliderEditWidget_field')['value'], final_slider_value) def test_getWidgetState_returns_QLineEdit_value(self): """Check that the value of the QLineEdit is saved to the state""" From c4c9faf5d7708e15120302c991e0f0e195c18ebb Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 3 Jan 2025 19:19:20 +0000 Subject: [PATCH 18/68] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd1ee7e..fc228a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Version x.x.x -- Add `QDoubleSpinBox` support to `UISliderWidget` (#168) +- Add `QLineEdit` support to `UISliderWidget` (#168) + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` +- Add `UISliderEditWidget` class, with layout and `QLineEdit` support (#168) +- Add support for `UISliderEditWidget` and modified `UISliderWidget` to `UIFormWidget` and tests (#168) # Version 1.0.2 - Upgrade python to 3.8 in `test.yml` (#171) From a767b0dd16de0e03a09d3824fa92224e4480b594 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 3 Jan 2025 19:53:52 +0000 Subject: [PATCH 19/68] Add float support to UISliderWidget value getter and setter --- eqt/ui/UIFormWidget.py | 14 ++++++-------- eqt/ui/UISliderWidget.py | 6 ++++++ test/test_UISliderEditWidget.py | 4 ++++ test/test__formUI_status_test.py | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index b68325a..9d3fa9c 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -326,7 +326,11 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, QtWidgets.QComboBox): widget_state['value'] = widget.currentIndex() - elif isinstance(widget, UISliderWidget) or isinstance(widget, QtWidgets.QSlider): + elif isinstance(widget, UISliderWidget): + widget_state['value'] = widget.getValue() + elif isinstance(widget, UISliderEditWidget): + widget_state['value'] = widget.getValue() + elif isinstance(widget, QtWidgets.QSlider): widget_state['value'] = widget.value() elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget_state['value'] = widget.value() @@ -336,9 +340,6 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() - elif isinstance(widget, UISliderEditWidget) or isinstance(widget, QtWidgets.QWidget): - widget_state['value'] = widget.getValue() - # isinstance(QWidget) must be last due to other QtWidgets inheriting from it widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -412,7 +413,7 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, QtWidgets.QComboBox): widget.setCurrentIndex(value) - elif isinstance(widget, (UISliderWidget, QtWidgets.QSlider)): + elif isinstance(widget, (UISliderWidget, UISliderEditWidget, QtWidgets.QSlider)): widget.setValue(value) elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget.setValue(value) @@ -424,9 +425,6 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) - elif isinstance(widget, (UISliderEditWidget, QtWidgets.QWidget)): - widget.setValue(value) - # isinstance(QWidget) must be last due to other QtWidgets inheriting from it def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 1c909f7..04e9e9e 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -64,6 +64,12 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor self.max_label.setAlignment(QtCore.Qt.AlignRight) self.max_label.setText(str(self.maximum)) + def getValue(self): + return self.getLineEditValue() + + def setValue(self, value): + self.line_edit.setText(str(value)) + def getSliderValue(self): return self.value() diff --git a/test/test_UISliderEditWidget.py b/test/test_UISliderEditWidget.py index e5d92ec..28d6633 100644 --- a/test/test_UISliderEditWidget.py +++ b/test/test_UISliderEditWidget.py @@ -1,5 +1,9 @@ import unittest +# from PySide2 import QtWidgets + +# from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget + class UISliderEditWidget(unittest.TestCase): def setUp(self): diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 9249dea..2750f91 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -34,13 +34,13 @@ def exampleState(self): state = [{ 'label_value': 'Test label state 0', 'checkBox_value': False, 'comboBox_value': 0, 'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10, - 'uiSliderWidget_value': 10, 'uiSliderEditWidget_value': 10.0, + 'uiSliderWidget_value': 10.0, 'uiSliderEditWidget_value': 10.0, 'radioButton_value': False, 'textEdit_value': 'test edit 0', 'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0', 'button_value': False}, { 'label_value': 'Test label state 1', 'checkBox_value': True, 'comboBox_value': 1, 'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1, - 'uiSliderWidget_value': 1, 'uiSliderEditWidget_value': 1.0, + 'uiSliderWidget_value': 1.0, 'uiSliderEditWidget_value': 1.0, 'radioButton_value': True, 'textEdit_value': 'test edit 1', 'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1', 'button_value': True}] From 8136350fcfa4a99ef5759c3315ac6796676444cb Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 6 Jan 2025 18:14:52 +0000 Subject: [PATCH 20/68] Connect QApplication focusChanged signal to UISlider QLineEdits --- eqt/ui/UISliderEditWidget.py | 11 ++++++++--- eqt/ui/UISliderWidget.py | 11 ++++++++--- test/test_UISliderEditWidget.py | 13 ++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/eqt/ui/UISliderEditWidget.py b/eqt/ui/UISliderEditWidget.py index 8dd2cba..10d7c1d 100644 --- a/eqt/ui/UISliderEditWidget.py +++ b/eqt/ui/UISliderEditWidget.py @@ -1,5 +1,5 @@ from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider, QWidget +from PySide2.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider, QWidget class UISliderEditWidget(QWidget): @@ -51,8 +51,13 @@ def __init__(self, minimum=0.0, maximum=10.0, step_size=1.0, scale_factor=1.0, self.line_edit.setPlaceholderText(str(minimum)) # Connect the QLineEdit to the QSlider - self.line_edit.textEdited.connect(self.updateSlider) - self.line_edit.returnPressed.connect(self.updateSlider) + self.line_edit.editingFinished.connect(self.updateSlider) + + # Configure the QApplication + self.app = QApplication.instance() + + # Connect the QApplication to the QLineEdit + self.app.focusChanged.connect(self.updateSlider) # Configure QLabels self.min_label = QLabel() diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 04e9e9e..8dbe3ea 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,5 +1,5 @@ from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import QSlider +from PySide2.QtWidgets import QApplication, QSlider class UISliderWidget(QSlider): @@ -56,8 +56,13 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor self.line_edit.setPlaceholderText(str(self.minimum)) # Connect the QLineEdit to the QSlider - self.line_edit.textEdited.connect(self.updateSlider) - self.line_edit.returnPressed.connect(self.updateSlider) + self.line_edit.editingFinished.connect(self.updateSlider) + + # Configure the QApplication + self.app = QApplication.instance() + + # Connect the QApplication to the QLineEdit + self.app.focusChanged.connect(self.updateSlider) # Configure QLabel to show maximum QSlider value self.max_label = max_label diff --git a/test/test_UISliderEditWidget.py b/test/test_UISliderEditWidget.py index 28d6633..eeede83 100644 --- a/test/test_UISliderEditWidget.py +++ b/test/test_UISliderEditWidget.py @@ -1,13 +1,16 @@ import unittest -# from PySide2 import QtWidgets - # from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget +from eqt.ui.UIFormWidget import FormWidget + +# from eqt.ui.UIFormWidget import FormDockWidget + +# from PySide2 import QtWidgets class UISliderEditWidget(unittest.TestCase): def setUp(self): - return super().setUp() + self.form = FormWidget() - def tearDown(self): - return super().tearDown() + # def _test_create_default_widget(self): + # uislideredit = UISliderEditWidget() From 80e3f6a0bd2eba79cbf914b3462c1c9be874a26e Mon Sep 17 00:00:00 2001 From: jcornall Date: Tue, 7 Jan 2025 19:00:56 +0000 Subject: [PATCH 21/68] Add additional UISliderEditWidget tests --- test/test_UISliderEditWidget.py | 93 ++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/test/test_UISliderEditWidget.py b/test/test_UISliderEditWidget.py index eeede83..7f19c1e 100644 --- a/test/test_UISliderEditWidget.py +++ b/test/test_UISliderEditWidget.py @@ -1,16 +1,93 @@ import unittest -# from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget -from eqt.ui.UIFormWidget import FormWidget - -# from eqt.ui.UIFormWidget import FormDockWidget +from eqt.ui import UISliderEditWidget # from PySide2 import QtWidgets -class UISliderEditWidget(unittest.TestCase): +class TestUISliderEditWidget(unittest.TestCase): def setUp(self): - self.form = FormWidget() + self.widget = UISliderEditWidget() + + def _test_init_default_widget(self): + self.assertIsInstance(self.widget, UISliderEditWidget) + + def _test_init_default_properties(self): + self.assertEqual(self.widget.minimum, 0.0) + self.assertEqual(self.widget.maximum, 10.0) + self.assertEqual(self.widget.step_size, 1.0) + self.assertEqual(self.widget.scale_factor, 1.0) + self.assertEqual(self.widget.tick_interval, 1.0) + + def _test_widget_state(self): + self.assertTrue(self.widget.isVisible()) + self.assertTrue(self.widget.isEnabled()) + + def _test_input_custom_min_max(self): + self.widget = UISliderEditWidget(minimum=5.0, maximum=100.0) + + self.assertEqual(self.widget.minimum, 0.0) + self.assertEqual(self.widget.maximum, 1.0) + + def _test_input_negative_min_max(self): + self.widget = UISliderEditWidget(minimum=-1.0, maximum=0.0) + + self.assertEqual(self.widget.minimum, -1.0) + self.assertEqual(self.widget.maximum, 0.0) + + def _test_input_invalid_min_max(self): + ... + + def _test_input_step_size(self): + ... + + def _test_input_scale_factor(self): + ... + + def _test_input_tick_interval(self): + ... + + def _test_init_default_slider(self): + ... + + def _test_connect_slider(self): + ... + + def _test_init_default_validator(self): + ... + + def _test_connect_validator(self): + ... + + def _test_init_default_lineedit(self): + ... + + def _test_connect_lineedit(self): + ... + + def _test_init_default_labels(self): + ... + + def _test_init_default_gridlayout(self): + ... + + def _test_get_value(self): + ... + + def _test_set_value(self): + ... + + def _test_get_slider_value(self): + ... + + def _test_get_lineedit_value(self): + ... + + def _test_update_slider(self): + ... + + def _test_update_lineedit(self): + ... - # def _test_create_default_widget(self): - # uislideredit = UISliderEditWidget() + def tearDown(self): + self.form = None From d76d8a39c3a8a0490ad2d7101666820999cbbe27 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 8 Jan 2025 13:42:02 +0000 Subject: [PATCH 22/68] Replace Pyside2 imports with pyqt --- eqt/ui/UISliderWidget.py | 4 ++-- examples/dialog_example_uislider.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 8dbe3ea..a037613 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,5 +1,5 @@ -from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import QApplication, QSlider +from qtpy import QtCore, QtGui +from qtpy.QtWidgets import QApplication, QSlider class UISliderWidget(QSlider): diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index ddbf1f6..76489bb 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -1,6 +1,6 @@ import sys -from PySide2 import QtWidgets +from qtpy import QtWidgets from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget From 0d7be4fab8ccc2661b8c9e3f3dfa3a393fe2cd10 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 9 Jan 2025 17:01:10 +0000 Subject: [PATCH 23/68] Update UISliderWidget, remove UISliderEditWidget, implement scaling --- CHANGELOG.md | 9 +- eqt/ui/UIFormWidget.py | 11 +- eqt/ui/UISliderEditWidget.py | 116 ------------------ eqt/ui/UISliderWidget.py | 168 ++++++++++++++++++-------- examples/dialog_example_uislider.py | 29 +---- examples/dialog_save_state_example.py | 14 +-- examples/utilitiesForExamples.py | 18 +-- test/test_UISliderEditWidget.py | 93 -------------- test/test__formUI_status_test.py | 38 ++---- 9 files changed, 146 insertions(+), 350 deletions(-) delete mode 100644 eqt/ui/UISliderEditWidget.py delete mode 100644 test/test_UISliderEditWidget.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0792322..f954481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,10 @@ -# Version x.x.x -- Add `QLineEdit` support to `UISliderWidget` (#168) - + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` -- Add `UISliderEditWidget` class, with layout and `QLineEdit` support (#168) -- Add support for `UISliderEditWidget` and modified `UISliderWidget` to `UIFormWidget` and tests (#168) - # Version 2.0.0 - Use `qtpy` as virtual Qt binding package. GHA unit tests are run with PySide2 and PyQt5 (#146) - Add `pyqt_env.yml` and `pyside_env.yml` environment files (#146) - Update `CONTRIBUTING.md`, `README.md` and add documentation file (#146) +- Refactor `UISliderWidget` class to support `QLineEdit` and layouts (#168) + + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` +- Update existing `FormDialog` tests and add new tests for `UISliderWidget`(#168) # Version 1.0.2 - Upgrade python to 3.8 in `test.yml` (#171) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index ba7f09a..644c817 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -2,7 +2,6 @@ from qtpy import QtWidgets -from .UISliderEditWidget import UISliderEditWidget from .UISliderWidget import UISliderWidget @@ -326,10 +325,6 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, QtWidgets.QComboBox): widget_state['value'] = widget.currentIndex() - elif isinstance(widget, UISliderWidget): - widget_state['value'] = widget.getValue() - elif isinstance(widget, UISliderEditWidget): - widget_state['value'] = widget.getValue() elif isinstance(widget, QtWidgets.QSlider): widget_state['value'] = widget.value() elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): @@ -340,6 +335,8 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() + elif isinstance(widget, UISliderWidget): + widget_state['value'] = widget.getValue() widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -413,7 +410,7 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, QtWidgets.QComboBox): widget.setCurrentIndex(value) - elif isinstance(widget, (UISliderWidget, UISliderEditWidget, QtWidgets.QSlider)): + elif isinstance(widget, (QtWidgets.QSlider)): widget.setValue(value) elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget.setValue(value) @@ -425,6 +422,8 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) + elif isinstance(widget, (UISliderWidget)): + widget.setValue(value) def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderEditWidget.py b/eqt/ui/UISliderEditWidget.py deleted file mode 100644 index 10d7c1d..0000000 --- a/eqt/ui/UISliderEditWidget.py +++ /dev/null @@ -1,116 +0,0 @@ -from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider, QWidget - - -class UISliderEditWidget(QWidget): - '''Creates a QGridLayout that includes a QSlider, min/median/max QLabels and a QLineEdit. - The QLineEdit is updated with the value of the slider and vice versa. - - Parameters - ---------- - min : float - max : float - step_size : float - scale_factor : float - tick_interval : float - ''' - def __init__(self, minimum=0.0, maximum=10.0, step_size=1.0, scale_factor=1.0, - tick_interval=1.0): - QWidget.__init__(self) - - self.minimum = minimum - self.maximum = maximum - self.scale_factor = scale_factor - self.step_size = step_size * self.scale_factor - self.tick_interval = tick_interval * self.scale_factor - - # Configure the QSlider - self.slider = QSlider() - self.slider.setRange(self.minimum, self.maximum) - self.slider.setOrientation(QtCore.Qt.Horizontal) - self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) - self.slider.setTickPosition(QSlider.TicksBelow) - self.slider.setSingleStep(self.step_size) - self.slider.setTickInterval(self.tick_interval) - - # Connect the QSlider to the QLineEdit - self.slider.sliderPressed.connect(self.updateLineEdit) - self.slider.sliderMoved.connect(self.updateLineEdit) - self.slider.sliderReleased.connect(self.updateLineEdit) - - # Configure the QDoubleValidator and QLineEdit - self.validator = QtGui.QDoubleValidator() - self.validator.setBottom(self.minimum) - self.validator.setTop(self.maximum) - self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) - self.validator.setLocale(QtCore.QLocale("en_US")) - - self.line_edit = QLineEdit() - self.line_edit.setValidator(self.validator) - self.line_edit.setText(str(minimum)) - self.line_edit.setPlaceholderText(str(minimum)) - - # Connect the QLineEdit to the QSlider - self.line_edit.editingFinished.connect(self.updateSlider) - - # Configure the QApplication - self.app = QApplication.instance() - - # Connect the QApplication to the QLineEdit - self.app.focusChanged.connect(self.updateSlider) - - # Configure QLabels - self.min_label = QLabel() - self.min_label.setText(str(self.minimum)) - self.median_label = QLabel() - self.median_label.setText(str(self.maximum * 0.5)) - self.max_label = QLabel() - self.max_label.setText(str(self.maximum)) - - # Configure quartile QLabels - # self.lowerq_label = QLabel() - # self.lowerq_label.setText(str(self.maximum * 0.25)) - # self.upperq_label = QLabel() - # self.upperq_label.setText(str(self.maximum * 0.75)) - - # Configure the QGridLayout - widget_layout = QGridLayout() - widget_layout.addWidget(self.slider, 0, 0, 1, -1) - widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) - # widget_layout.addWidget(self.lowerq_label, 1, 1, QtCore.Qt.AlignLeft) - widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) - # widget_layout.addWidget(self.upperq_label, 1, 3, QtCore.Qt.AlignRight) - widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) - widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) - - # Set the layout - self.setLayout(widget_layout) - self.show() - - def getValue(self): - return self.getLineEditValue() - - def setValue(self, value): - self.line_edit.setText(str(value)) - - def getSliderValue(self): - return self.slider.value() - - def getLineEditValue(self): - return float(self.line_edit.text()) - - def updateSlider(self): - state = self.validator.validate(self.line_edit.text(), 0) - if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = self.getLineEditValue() - self.slider.setValue(line_edit_value) - self.setValue(line_edit_value) - else: - self.line_edit.setText(str(self.minimum)) - line_edit_value = self.getLineEditValue() - self.slider.setValue(line_edit_value) - self.setValue(line_edit_value) - - def updateLineEdit(self): - slider_value = str(float(self.getSliderValue())) - self.line_edit.setText(slider_value) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index a037613..9939487 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,48 +1,53 @@ -from qtpy import QtCore, QtGui -from qtpy.QtWidgets import QApplication, QSlider +from PySide2 import QtCore, QtGui +from PySide2.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider, QWidget -class UISliderWidget(QSlider): - '''Creates a Slider widget which updates - a QLineEdit with its value (which may be scaled - to a non-integer value by setting the scale_factor) - Also accepts a QLabel that is configured to - display the maximum value of the QSlider +class UISliderWidget(QWidget): + '''Creates a QGridLayout that includes a QSlider, min/median/max QLabels and a QLineEdit. + Updating a widget scales the value appropriately for the other widget, + i.e. the QLineEdit is updated with the value of the slider and vice versa. Parameters ---------- - line_edit : QLineEdit - max_label : QLabel minimum : float + - Minimum value of the QLineEdit maximum : float - step_size : float - scale_factor : float - tick_interval : float + - Maximum value of the QLineEdit + decimals : int + - Number of decimal places that the QLabels and QSlider steps can display + number_of_steps : int + - Number of steps in the QSlider + number_of_ticks : int + - Number of ticks visualised under the QSlider, determines tick interval ''' - def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor=1.0, - step_size=1.0, tick_interval=1.0): - QSlider.__init__(self) - - self.line_edit = line_edit - self.max_label = max_label - self.minimum = minimum - self.maximum = maximum - self.scale_factor = scale_factor - self.step_size = step_size * self.scale_factor - self.tick_interval = tick_interval * self.scale_factor + def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): + QWidget.__init__(self) + + self.decimals = decimals + self.minimum = round(minimum, self.decimals) + self.maximum = round(maximum, self.decimals) + self.median = round(((self.maximum - self.minimum) / 2), self.decimals) + self.minimum + self.number_of_steps = number_of_steps + self.number_of_ticks = number_of_ticks + + self.slider_minimum = 0 + self.slider_maximum = self.number_of_steps + + self.step_size = float((self.maximum - self.minimum) / self.number_of_steps) + self.tick_interval = round( + (self.slider_maximum - self.slider_minimum) / self.number_of_ticks) + # self.number_of_steps = round((self.maximum - self.minimum) / self.step_size) # Configure the QSlider - self.setRange(self.minimum, self.maximum) - self.setOrientation(QtCore.Qt.Horizontal) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setTickPosition(QSlider.TicksBelow) - self.setSingleStep(self.step_size) - self.setTickInterval(self.tick_interval) + self.slider = QSlider() + self.slider.setRange(self.slider_minimum, self.slider_maximum) + self.slider.setOrientation(QtCore.Qt.Horizontal) + self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) + self.slider.setTickPosition(QSlider.TicksBelow) + self.slider.setTickInterval(self.tick_interval) # Connect the QSlider to the QLineEdit - self.sliderPressed.connect(self.updateLineEdit) - self.sliderMoved.connect(self.updateLineEdit) - self.sliderReleased.connect(self.updateLineEdit) + self.slider.valueChanged.connect(self._updateLineEdit) # Configure the QDoubleValidator and QLineEdit self.validator = QtGui.QDoubleValidator() @@ -51,46 +56,111 @@ def __init__(self, line_edit, max_label, minimum=0.0, maximum=10.0, scale_factor self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) self.validator.setLocale(QtCore.QLocale("en_US")) + self.line_edit = QLineEdit() self.line_edit.setValidator(self.validator) self.line_edit.setText(str(self.minimum)) self.line_edit.setPlaceholderText(str(self.minimum)) # Connect the QLineEdit to the QSlider - self.line_edit.editingFinished.connect(self.updateSlider) + self.line_edit.editingFinished.connect(self._updateSlider) # Configure the QApplication self.app = QApplication.instance() # Connect the QApplication to the QLineEdit - self.app.focusChanged.connect(self.updateSlider) - - # Configure QLabel to show maximum QSlider value - self.max_label = max_label - self.max_label.setAlignment(QtCore.Qt.AlignRight) + self.app.focusChanged.connect(self._updateSlider) + + # Configure QLabels + self.min_label = QLabel() + self.min_label.setText(str(self.minimum)) + self.median_label = QLabel() + self.median_label.setText(str(self.median)) + self.max_label = QLabel() self.max_label.setText(str(self.maximum)) + # Configure the QGridLayout + widget_layout = QGridLayout() + widget_layout.addWidget(self.slider, 0, 0, 1, -1) + widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) + widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) + widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) + widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) + + # Set the layout + self.setLayout(widget_layout) + self.show() + def getValue(self): - return self.getLineEditValue() + ''' + Gets the value of the UISliderWidget, which is the same as the QLineEdit value. + This method is called by methods in the UIFormWidget class responsible for + saving widget states, maintaining the naming convention used by other QWidgets. + ''' + return self._getLineEditValue() def setValue(self, value): + ''' + Sets the value of the UISliderWidget, which is the same as the QLineEdit value. + This method is called by methods in the UIFormWidget class responsible for + applying/loading widget states, maintaining the naming convention used by other QWidgets. + + Parameters + ---------- + value : float + ''' self.line_edit.setText(str(value)) - def getSliderValue(self): - return self.value() + def _getSliderValue(self): + return self.slider.value() - def getLineEditValue(self): + def _getLineEditValue(self): return float(self.line_edit.text()) - def updateSlider(self): + def _updateSlider(self): + line_edit_value = self._getLineEditValue() state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - line_edit_value = self.getLineEditValue() + self.slider.setValue(self._scaleUp(line_edit_value)) + self.setValue(line_edit_value) + elif line_edit_value > self.maximum: + self.line_edit.setText(str(self.maximum)) + line_edit_value = self._getLineEditValue() + self.slider.setValue(self._scaleUp(line_edit_value)) self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = self.getLineEditValue() + line_edit_value = self._getLineEditValue() + self.slider.setValue(self._scaleUp(line_edit_value)) self.setValue(line_edit_value) - def updateLineEdit(self): - slider_value = str(float(self.getSliderValue())) - self.line_edit.setText(slider_value) + def _updateLineEdit(self): + slider_value = self._getSliderValue() + self.line_edit.setText(str(self._scaleDown(slider_value))) + + def _scaleUp(self, value): + ''' + Scales a value up. The method calculates the appropriate scale factor for the conversion + using the minimum and maximum values of the QSlider and QLineEdit. + Returns the upscaled value. + + Parameters + ---------- + value : float + ''' + scale_factor = (self.slider_maximum - self.slider_minimum) / (self.maximum - self.minimum) + value = self.slider_minimum + (scale_factor * (value - self.minimum)) + return int(value) + + def _scaleDown(self, value): + ''' + Scales a value down. The method calculates the appropriate scale factor for the conversion + using the minimum and maximum values of the QSlider and QLineEdit. + Returns the downscaled value, rounded as per the decimals attribute. + + Parameters + ---------- + value : integer + ''' + scale_factor = (self.maximum - self.minimum) / (self.slider_maximum - self.slider_minimum) + value = self.minimum + (scale_factor * (value - self.slider_minimum)) + return round(float(value), self.decimals) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 76489bb..093fc3c 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -2,7 +2,7 @@ from qtpy import QtWidgets -from eqt.ui import FormDialog, UISliderEditWidget, UISliderWidget +from eqt.ui import FormDialog, UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -26,24 +26,11 @@ def openFormDialog(self): dialog = FormDialog(parent=self, title='Example') dialog.Ok.clicked.connect(lambda: self.accepted()) - # Example on how to add elements to the FormDialog - # add input 1 as UISliderWidget and QLineEdit - line_edit = QtWidgets.QLineEdit() - max_label = QtWidgets.QLabel() - uislider = UISliderWidget.UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, - scale_factor=10.0) + # Create UISliderWidget + uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=10) # add to the form widget - dialog.addWidget(uislider, 'UISlider 1:', 'input_slider1') - dialog.addWidget(max_label, '', 'input_max_label1') - dialog.addWidget(line_edit, '', 'input_line_edit1') - - # add input 2 as UISliderLineEditWidget - uislider = UISliderEditWidget.UISliderEditWidget(minimum=0.0, maximum=100.0, - scale_factor=10.0) - - # add to the form widget - dialog.addWidget(uislider, 'UISlider 2:', 'input_slider2') + dialog.addWidget(uislider, 'UISlider:', 'input_slider') # store a reference self.dialog = dialog @@ -52,12 +39,8 @@ def openFormDialog(self): def accepted(self): print("accepted") - print(f"UISlider 1 QSlider: {self.dialog.widgets['input_slider1_field'].value()}") - print(f"QSlider 1 QLabel: {self.dialog.widgets['input_max_label1_field'].text()}") - print(f"QSlider 1 QLineEdit: {self.dialog.widgets['input_line_edit1_field'].text()}") - - print(f"UISlider 2 QSlider: {self.dialog.widgets['input_slider2_field'].getSliderValue()}") - print(f"UISlider 2 QLineEdit: {self.dialog.widgets['input_slider2_field'].getValue()}") + print(f"UISlider QSlider: {self.dialog.widgets['input_slider_field']._getSliderValue()}") + print(f"UISlider QLineEdit: {self.dialog.widgets['input_slider_field'].getValue()}") self.dialog.close() diff --git a/examples/dialog_save_state_example.py b/examples/dialog_save_state_example.py index 2a6337e..ee475be 100644 --- a/examples/dialog_save_state_example.py +++ b/examples/dialog_save_state_example.py @@ -3,7 +3,6 @@ from qtpy import QtWidgets from eqt.ui import FormDialog -from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -36,17 +35,8 @@ def __init__(self, parent=None): dialog.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') dialog.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') dialog.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - - line_edit = QtWidgets.QLineEdit() - max_label = QtWidgets.QLabel() - dialog.addWidget( - UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderWidget: ', 'uiSliderWidget') - dialog.addWidget(max_label, '', 'input_max_label1') - dialog.addWidget(line_edit, '', 'input_line_edit1') - - dialog.addWidget(UISliderEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderEditWidget:', 'uiSliderEditWidget') + dialog.addWidget(UISliderWidget(minimum=0.0, maximum=100.0), 'UISliderWidget: ', + 'uiSliderWidget') dialog.addWidget(QtWidgets.QRadioButton('test 1'), 'RadioButton 1: ', 'radioButton1') dialog.addWidget(QtWidgets.QRadioButton('test 2'), 'RadioButton 2: ', 'radioButton2') diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py index 51744f0..8c6efa6 100644 --- a/examples/utilitiesForExamples.py +++ b/examples/utilitiesForExamples.py @@ -1,6 +1,5 @@ from qtpy import QtWidgets -from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget @@ -9,8 +8,7 @@ def list_all_widgets(): 'label': QtWidgets.QLabel('test label'), 'checkBox': QtWidgets.QCheckBox('test checkbox'), 'comboBox': QtWidgets.QComboBox(), 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), - 'uiSliderWidget': UISliderWidget(QtWidgets.QLineEdit(), QtWidgets.QLabel()), - 'uiSliderEditWidget': UISliderEditWidget(), + 'uiSliderWidget': UISliderWidget(), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -35,18 +33,8 @@ def addWidgetsToExample(form): form.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - - line_edit = QtWidgets.QLineEdit() - max_label = QtWidgets.QLabel() - form.addWidget( - UISliderWidget(line_edit, max_label, minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderWidget: ', 'uiSliderWidget') - form.addWidget(max_label, '', 'uiSliderMaxLabel') - form.addWidget(line_edit, '', 'uiSliderLineEdit') - - form.addWidget(UISliderEditWidget(minimum=0.0, maximum=100.0, scale_factor=10.0), - 'UISliderEditWidget:', 'uiSliderEditWidget') - + form.addWidget(UISliderWidget(minimum=0.0, maximum=100.0), 'UISliderWidget: ', + 'uiSliderWidget') form.addWidget(QtWidgets.QRadioButton('select me'), 'RadioButton: ', 'radioButton') form.addWidget(QtWidgets.QTextEdit('write text here'), 'TextEdit: ', 'textEdit') form.addWidget(QtWidgets.QPlainTextEdit('write text here'), 'PlainTextEdit: ', 'plainTextEdit') diff --git a/test/test_UISliderEditWidget.py b/test/test_UISliderEditWidget.py deleted file mode 100644 index 7f19c1e..0000000 --- a/test/test_UISliderEditWidget.py +++ /dev/null @@ -1,93 +0,0 @@ -import unittest - -from eqt.ui import UISliderEditWidget - -# from PySide2 import QtWidgets - - -class TestUISliderEditWidget(unittest.TestCase): - def setUp(self): - self.widget = UISliderEditWidget() - - def _test_init_default_widget(self): - self.assertIsInstance(self.widget, UISliderEditWidget) - - def _test_init_default_properties(self): - self.assertEqual(self.widget.minimum, 0.0) - self.assertEqual(self.widget.maximum, 10.0) - self.assertEqual(self.widget.step_size, 1.0) - self.assertEqual(self.widget.scale_factor, 1.0) - self.assertEqual(self.widget.tick_interval, 1.0) - - def _test_widget_state(self): - self.assertTrue(self.widget.isVisible()) - self.assertTrue(self.widget.isEnabled()) - - def _test_input_custom_min_max(self): - self.widget = UISliderEditWidget(minimum=5.0, maximum=100.0) - - self.assertEqual(self.widget.minimum, 0.0) - self.assertEqual(self.widget.maximum, 1.0) - - def _test_input_negative_min_max(self): - self.widget = UISliderEditWidget(minimum=-1.0, maximum=0.0) - - self.assertEqual(self.widget.minimum, -1.0) - self.assertEqual(self.widget.maximum, 0.0) - - def _test_input_invalid_min_max(self): - ... - - def _test_input_step_size(self): - ... - - def _test_input_scale_factor(self): - ... - - def _test_input_tick_interval(self): - ... - - def _test_init_default_slider(self): - ... - - def _test_connect_slider(self): - ... - - def _test_init_default_validator(self): - ... - - def _test_connect_validator(self): - ... - - def _test_init_default_lineedit(self): - ... - - def _test_connect_lineedit(self): - ... - - def _test_init_default_labels(self): - ... - - def _test_init_default_gridlayout(self): - ... - - def _test_get_value(self): - ... - - def _test_set_value(self): - ... - - def _test_get_slider_value(self): - ... - - def _test_get_lineedit_value(self): - ... - - def _test_update_slider(self): - ... - - def _test_update_lineedit(self): - ... - - def tearDown(self): - self.form = None diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 4137bb0..95a4eac 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -8,7 +8,6 @@ from eqt.ui.FormDialog import AdvancedFormDialog, FormDialog from eqt.ui.UIFormWidget import FormDockWidget, FormWidget -from eqt.ui.UISliderEditWidget import UISliderEditWidget from eqt.ui.UISliderWidget import UISliderWidget from . import is_ci, skip @@ -34,16 +33,14 @@ def exampleState(self): state = [{ 'label_value': 'Test label state 0', 'checkBox_value': False, 'comboBox_value': 0, 'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10, - 'uiSliderWidget_value': 10.0, 'uiSliderEditWidget_value': 10.0, - 'radioButton_value': False, 'textEdit_value': 'test edit 0', - 'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0', - 'button_value': False}, { + 'uiSliderWidget_value': 10.0, 'radioButton_value': False, + 'textEdit_value': 'test edit 0', 'plainTextEdit_value': 'test plain 0', + 'lineEdit_value': 'test line 0', 'button_value': False}, { 'label_value': 'Test label state 1', 'checkBox_value': True, 'comboBox_value': 1, 'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1, - 'uiSliderWidget_value': 1.0, 'uiSliderEditWidget_value': 1.0, - 'radioButton_value': True, 'textEdit_value': 'test edit 1', - 'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1', - 'button_value': True}] + 'uiSliderWidget_value': 1.0, 'radioButton_value': True, + 'textEdit_value': 'test edit 1', 'plainTextEdit_value': 'test plain 1', + 'lineEdit_value': 'test line 1', 'button_value': True}] return state @property @@ -56,9 +53,8 @@ def list_all_widgets(self): 'checkBox': QtWidgets.QCheckBox('test checkbox'), 'comboBox': combobox_widget, 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget( - QtWidgets.QLineEdit(), - QtWidgets.QLabel()), 'uiSliderEditWidget': UISliderEditWidget(), - 'radioButton': QtWidgets.QRadioButton('test radio button'), + minimum=0.0, + maximum=1.0), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), 'lineEdit': QtWidgets.QLineEdit('test line edit'), @@ -111,8 +107,6 @@ def set_state(self, i: int): self.form.getWidget('slider').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget').setValue(state[i]['uiSliderWidget_value']) - # UISliderEditWidget - self.form.getWidget('uiSliderEditWidget').setValue(state[i]['uiSliderEditWidget_value']) # QRadioButton self.form.getWidget('radioButton').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -148,9 +142,6 @@ def set_spanning_state(self, i: int): self.form.getWidget('slider_spanning').setValue(state[i]['slider_value']) # UISlider self.form.getWidget('uiSliderWidget_spanning').setValue(state[i]['uiSliderWidget_value']) - # UISliderEditWidget - self.form.getWidget('uiSliderEditWidget_spanning').setValue( - state[i]['uiSliderEditWidget_value']) # QRadioButton self.form.getWidget('radioButton_spanning').setChecked(state[i]['radioButton_value']) # QTextEdit @@ -384,19 +375,6 @@ def test_getWidgetState_returns_UISliderWidget_value(self): self.assertEqual( self.form.getWidgetState('uiSliderWidget_field')['value'], final_slider_value) - def test_getWidgetState_returns_UISliderEditWidget_value(self): - """Check that the value of the UISliderEditWidget is returned in the state""" - initial_slider_value = 0 - - self.assertEqual( - self.form.getWidgetState('uiSliderEditWidget_field')['value'], initial_slider_value) - - final_slider_value = 1 - self.form.getWidget('uiSliderEditWidget').setValue(final_slider_value) - - self.assertEqual( - self.form.getWidgetState('uiSliderEditWidget_field')['value'], final_slider_value) - def test_getWidgetState_returns_QLineEdit_value(self): """Check that the value of the QLineEdit is saved to the state""" initial_lineEdit_value = '' From c0fa4493dbecd9f21c8ddb683b028ac3b6eabe5e Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 9 Jan 2025 17:47:45 +0000 Subject: [PATCH 24/68] Add minimum/maximum argument check to __init__() --- eqt/ui/UISliderWidget.py | 4 ++ examples/dialog_example_uislider.py | 3 +- recipe/eqt_env.yml | 1 + test/test_UISliderWidget.py | 95 +++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/test_UISliderWidget.py diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 9939487..e8140e8 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -23,6 +23,10 @@ class UISliderWidget(QWidget): def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): QWidget.__init__(self) + # Check that the minimum/maximum arguments are valid + if minimum >= maximum: + raise ValueError("'minimum' argument must be less than 'maximum'") + self.decimals = decimals self.minimum = round(minimum, self.decimals) self.maximum = round(maximum, self.decimals) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 093fc3c..8a70666 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -27,7 +27,8 @@ def openFormDialog(self): dialog.Ok.clicked.connect(lambda: self.accepted()) # Create UISliderWidget - uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=10) + uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=2, + number_of_steps=1000, number_of_ticks=10) # add to the form widget dialog.addWidget(uislider, 'UISlider:', 'input_slider') diff --git a/recipe/eqt_env.yml b/recipe/eqt_env.yml index f4d27f3..28bac83 100644 --- a/recipe/eqt_env.yml +++ b/recipe/eqt_env.yml @@ -6,3 +6,4 @@ dependencies: - pip - qtpy - qdarkstyle + - parameterized diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py new file mode 100644 index 0000000..1998a89 --- /dev/null +++ b/test/test_UISliderWidget.py @@ -0,0 +1,95 @@ +import unittest + +from eqt.ui import UISliderWidget + +# from parameterized import parameterized, parameterized_class + +# from PySide2 import QtWidgets + + +class TestUISliderWidget(unittest.TestCase): + def setUp(self): + self.widget = UISliderWidget() + + def _test_init_default_widget(self): + self.assertIsInstance(self.widget, UISliderWidget) + + def _test_init_default_properties(self): + self.assertEqual(self.widget.minimum, 0.0) + self.assertEqual(self.widget.maximum, 10.0) + self.assertEqual(self.widget.step_size, 1.0) + self.assertEqual(self.widget.scale_factor, 1.0) + self.assertEqual(self.widget.tick_interval, 1.0) + + def _test_widget_state(self): + self.assertTrue(self.widget.isVisible()) + self.assertTrue(self.widget.isEnabled()) + + def _test_input_custom_min_max(self): + self.widget = UISliderWidget(minimum=5.0, maximum=100.0) + + self.assertEqual(self.widget.minimum, 0.0) + self.assertEqual(self.widget.maximum, 1.0) + + def _test_input_negative_min_max(self): + self.widget = UISliderWidget(minimum=-1.0, maximum=0.0) + + self.assertEqual(self.widget.minimum, -1.0) + self.assertEqual(self.widget.maximum, 0.0) + + def _test_input_invalid_min_max(self): + ... + + def _test_input_step_size(self): + ... + + def _test_input_scale_factor(self): + ... + + def _test_input_tick_interval(self): + ... + + def _test_init_default_slider(self): + ... + + def _test_connect_slider(self): + ... + + def _test_init_default_validator(self): + ... + + def _test_connect_validator(self): + ... + + def _test_init_default_lineedit(self): + ... + + def _test_connect_lineedit(self): + ... + + def _test_init_default_labels(self): + ... + + def _test_init_default_gridlayout(self): + ... + + def _test_get_value(self): + ... + + def _test_set_value(self): + ... + + def _test_get_slider_value(self): + ... + + def _test_get_lineedit_value(self): + ... + + def _test_update_slider(self): + ... + + def _test_update_lineedit(self): + ... + + def tearDown(self): + self.form = None From 9f347aca96a33c7a42263ce1f918cddfcb51ee71 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 9 Jan 2025 18:04:09 +0000 Subject: [PATCH 25/68] Correct UISliderWidget minimum/maximum values --- test/test__formUI_status_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 95a4eac..e2a60ee 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -54,7 +54,7 @@ def list_all_widgets(self): 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), 'uiSliderWidget': UISliderWidget( minimum=0.0, - maximum=1.0), 'radioButton': QtWidgets.QRadioButton('test radio button'), + maximum=10.0), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), 'lineEdit': QtWidgets.QLineEdit('test line edit'), @@ -105,7 +105,7 @@ def set_state(self, i: int): self.form.getWidget('spinBox').setValue(state[i]['spinBox_value']) # QSlider self.form.getWidget('slider').setValue(state[i]['slider_value']) - # UISlider + # UISliderWidget self.form.getWidget('uiSliderWidget').setValue(state[i]['uiSliderWidget_value']) # QRadioButton self.form.getWidget('radioButton').setChecked(state[i]['radioButton_value']) @@ -140,7 +140,7 @@ def set_spanning_state(self, i: int): self.form.getWidget('spinBox_spanning').setValue(state[i]['spinBox_value']) # QSlider self.form.getWidget('slider_spanning').setValue(state[i]['slider_value']) - # UISlider + # UISliderWidget self.form.getWidget('uiSliderWidget_spanning').setValue(state[i]['uiSliderWidget_value']) # QRadioButton self.form.getWidget('radioButton_spanning').setChecked(state[i]['radioButton_value']) From 3114d02f7fa7bf35370ccad676bb542155422e81 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 10 Jan 2025 09:59:49 +0000 Subject: [PATCH 26/68] Correct formatting of docstrings --- eqt/ui/UISliderWidget.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index e8140e8..fa06f7e 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -95,16 +95,14 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.show() def getValue(self): - ''' - Gets the value of the UISliderWidget, which is the same as the QLineEdit value. + '''Gets the value of the UISliderWidget, which is the same as the QLineEdit value. This method is called by methods in the UIFormWidget class responsible for saving widget states, maintaining the naming convention used by other QWidgets. ''' return self._getLineEditValue() def setValue(self, value): - ''' - Sets the value of the UISliderWidget, which is the same as the QLineEdit value. + '''Sets the value of the UISliderWidget, which is the same as the QLineEdit value. This method is called by methods in the UIFormWidget class responsible for applying/loading widget states, maintaining the naming convention used by other QWidgets. @@ -142,8 +140,7 @@ def _updateLineEdit(self): self.line_edit.setText(str(self._scaleDown(slider_value))) def _scaleUp(self, value): - ''' - Scales a value up. The method calculates the appropriate scale factor for the conversion + '''Scales a value up. The method calculates the appropriate scale factor for the conversion using the minimum and maximum values of the QSlider and QLineEdit. Returns the upscaled value. @@ -156,9 +153,8 @@ def _scaleUp(self, value): return int(value) def _scaleDown(self, value): - ''' - Scales a value down. The method calculates the appropriate scale factor for the conversion - using the minimum and maximum values of the QSlider and QLineEdit. + '''Scales a value down. The method calculates the appropriate scale factor for the + conversion using the minimum and maximum values of the QSlider and QLineEdit. Returns the downscaled value, rounded as per the decimals attribute. Parameters From 162d9a05241fa3da5ad6ac13321e3df40fe15c7f Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 14:39:51 +0000 Subject: [PATCH 27/68] Add UISlider tests --- examples/dialog_example_uislider.py | 2 +- test/test_UISliderWidget.py | 217 ++++++++++++++++++---------- 2 files changed, 138 insertions(+), 81 deletions(-) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 8a70666..788796e 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -28,7 +28,7 @@ def openFormDialog(self): # Create UISliderWidget uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=2, - number_of_steps=1000, number_of_ticks=10) + number_of_steps=2000, number_of_ticks=10) # add to the form widget dialog.addWidget(uislider, 'UISlider:', 'input_slider') diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 1998a89..28e41f8 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,95 +1,152 @@ import unittest -from eqt.ui import UISliderWidget - -# from parameterized import parameterized, parameterized_class +from parameterized import parameterized +from PySide2 import QtGui +from PySide2.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider -# from PySide2 import QtWidgets +from eqt.ui import UISliderWidget class TestUISliderWidget(unittest.TestCase): def setUp(self): - self.widget = UISliderWidget() - - def _test_init_default_widget(self): - self.assertIsInstance(self.widget, UISliderWidget) - - def _test_init_default_properties(self): - self.assertEqual(self.widget.minimum, 0.0) - self.assertEqual(self.widget.maximum, 10.0) - self.assertEqual(self.widget.step_size, 1.0) - self.assertEqual(self.widget.scale_factor, 1.0) - self.assertEqual(self.widget.tick_interval, 1.0) + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) + + @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), + ("negative", -10.0, -1.0), ("float", -0.5, 0.5), + ("long", -9.99999, 9.99999)]) + def test_init_default_widget(self, _, minimum, maximum): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertIsInstance(self.widget, UISliderWidget.UISliderWidget) + + @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), + ("negative", -10.0, -1.0), ("float", -0.5, 0.5), + ("long", -9.99999, 9.99999)]) + def test_init_default_properties(self, _, minimum, maximum): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertEqual(self.widget.decimals, 2) + self.assertEqual(self.widget.number_of_steps, 2000) + self.assertEqual(self.widget.number_of_ticks, 10) + + @parameterized.expand([("standard", 0.0, 10.0, 5.0), ("positive", 1.0, 10.0, 5.5), + ("negative", -10.0, -1.0, -5.5), ("float", -0.5, 0.5, 0.0), + ("long", 1.11111, 9.99999, 5.5600000000000005)]) + def test_init_median(self, _, minimum, maximum, median): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertEqual(self.widget.median, median) def _test_widget_state(self): self.assertTrue(self.widget.isVisible()) self.assertTrue(self.widget.isEnabled()) - def _test_input_custom_min_max(self): - self.widget = UISliderWidget(minimum=5.0, maximum=100.0) - - self.assertEqual(self.widget.minimum, 0.0) - self.assertEqual(self.widget.maximum, 1.0) - - def _test_input_negative_min_max(self): - self.widget = UISliderWidget(minimum=-1.0, maximum=0.0) - - self.assertEqual(self.widget.minimum, -1.0) - self.assertEqual(self.widget.maximum, 0.0) - - def _test_input_invalid_min_max(self): - ... - - def _test_input_step_size(self): - ... - - def _test_input_scale_factor(self): - ... - - def _test_input_tick_interval(self): - ... - - def _test_init_default_slider(self): - ... - - def _test_connect_slider(self): - ... - - def _test_init_default_validator(self): - ... - - def _test_connect_validator(self): - ... - - def _test_init_default_lineedit(self): - ... - - def _test_connect_lineedit(self): - ... - - def _test_init_default_labels(self): - ... - - def _test_init_default_gridlayout(self): - ... - - def _test_get_value(self): - ... - - def _test_set_value(self): - ... - - def _test_get_slider_value(self): - ... - - def _test_get_lineedit_value(self): - ... - - def _test_update_slider(self): - ... - - def _test_update_lineedit(self): - ... + @parameterized.expand([("min_gt_max", 10.0, 0.0), ("min_eq_max", 0.0, 0.0)]) + def test_input_invalid_min_max(self, _, minimum, maximum): + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + @parameterized.expand([("positive", 10, 10), ("negative", -10, 0), ("float", 2.5, 2), + ("long", 9.99999, 9)]) + def test_input_decimals(self, _, decimals, expected): + try: + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + decimals=decimals) + self.assertEqual(self.widget.decimals, expected) + except ValueError: + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + decimals=decimals) + + @parameterized.expand([("positive", 1500, 1500, 0.0006666666666666666), + ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 0.5), + ("long", 9.99999, 9, 0.11111111111111111111111111111111)]) + def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): + try: + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + number_of_steps=number_of_steps) + self.assertEqual(self.widget.number_of_steps, expected_steps) + self.assertEqual(self.widget.step_size, expected_size) + except ValueError: + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + number_of_steps=number_of_steps) + + @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), + ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) + def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): + try: + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + number_of_ticks=number_of_ticks) + self.assertEqual(self.widget.number_of_ticks, expected_ticks) + self.assertEqual(self.widget.tick_interval, expected_interval) + except ValueError: + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + number_of_ticks=number_of_ticks) + + def test_init_default_slider(self): + self.assertIsInstance(self.widget.slider, QSlider) + + def test_init_default_validator(self): + self.assertIsInstance(self.widget.validator, QtGui.QValidator) + + def test_init_default_lineedit(self): + self.assertIsInstance(self.widget.line_edit, QLineEdit) + + def test_init_default_labels(self): + self.assertIsInstance(self.widget.min_label, QLabel) + self.assertIsInstance(self.widget.median_label, QLabel) + self.assertIsInstance(self.widget.max_label, QLabel) + + def test_init_default_gridlayout(self): + self.assertIsInstance(self.widget.widget_layout, QGridLayout) + + @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), + ("decimal", -0.5, 0.5, 0.25, 0.25), + ("long", -9.99999, 9.99999, 1.11111, 1.11111)]) + def test_get_and_set_value(self, _, minimum, maximum, value, expected): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget.setValue(value) + self.assertEqual(self.widget.getValue(), expected) + + def test_get_slider_value(self): + self.widget.slider.setValue(50) + self.assertEqual(self.widget._getSliderValue(), 50) + + def test_get_lineedit_value(self): + self.widget.line_edit.setText("5.0") + self.assertEqual(self.widget._getLineEditValue(), 5.0) + + def test_acceptable_update_slider(self): + self.widget.line_edit.setText("10.0") + self.widget._updateSlider() + self.assertEqual(self.widget._getSliderValue(), 2000) + self.assertEqual(self.widget.getValue(), 10.0) + + def test_gt_max_update_slider(self): + self.widget.line_edit.setText("11.0") + self.widget._updateSlider() + self.assertEqual(self.widget._getSliderValue(), self.widget.slider_maximum) + self.assertEqual(self.widget.getValue(), self.widget.maximum) + + def test_invalid_update_slider(self): + self.widget.line_edit.setText("-11.0") + self.widget._updateSlider() + self.assertEqual(self.widget._getSliderValue(), self.widget.slider_minimum) + self.assertEqual(self.widget.getValue(), self.widget.minimum) + + def test_update_lineedit(self): + self.widget.slider.setValue(1500) + self.widget._updateLineEdit() + self.assertEqual(self.widget._getLineEditValue(), 5.0) + + @parameterized.expand([("positive", 5.0, 1500), ("negative", -5.0, 500), ("float", 2.5, 1250), + ("long", 9.99999, 1999)]) + def test_scale_up(self, _, value, expected): + self.assertEqual(self.widget._scaleUp(value), expected) + + @parameterized.expand([("positive", 1500, 5.0), ("negative", 500, -5.0), ("float", 1250, 2.5), + ("long", 1999, 9.99)]) + def test_scale_down(self, _, value, expected): + self.assertEqual(self.widget._scaleDown(value), expected) def tearDown(self): self.form = None From da49b613d2694d79391587ad952337d9c5b8ef11 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 14:53:15 +0000 Subject: [PATCH 28/68] Add checks for non-default UISlider inputs --- eqt/ui/UISliderWidget.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index fa06f7e..d3ef0bf 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -27,12 +27,13 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of if minimum >= maximum: raise ValueError("'minimum' argument must be less than 'maximum'") - self.decimals = decimals + self._setDecimals(decimals) + self._setNumberOfSteps(number_of_steps) + self._setNumberOfTicks(number_of_ticks) + self.minimum = round(minimum, self.decimals) self.maximum = round(maximum, self.decimals) self.median = round(((self.maximum - self.minimum) / 2), self.decimals) + self.minimum - self.number_of_steps = number_of_steps - self.number_of_ticks = number_of_ticks self.slider_minimum = 0 self.slider_maximum = self.number_of_steps @@ -83,15 +84,15 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.max_label.setText(str(self.maximum)) # Configure the QGridLayout - widget_layout = QGridLayout() - widget_layout.addWidget(self.slider, 0, 0, 1, -1) - widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) - widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) - widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) - widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) + self.widget_layout = QGridLayout() + self.widget_layout.addWidget(self.slider, 0, 0, 1, -1) + self.widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) + self.widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) + self.widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) + self.widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) # Set the layout - self.setLayout(widget_layout) + self.setLayout(self.widget_layout) self.show() def getValue(self): @@ -112,6 +113,24 @@ def setValue(self, value): ''' self.line_edit.setText(str(value)) + def _setDecimals(self, decimals): + if decimals < 0: + raise ValueError("'decimals' value must be a positive integer") + else: + self.decimals = int(decimals) + + def _setNumberOfSteps(self, number_of_steps): + if number_of_steps < 0: + raise ValueError("'number_of_steps' value must be a positive integer") + else: + self.number_of_steps = int(number_of_steps) + + def _setNumberOfTicks(self, number_of_ticks): + if number_of_ticks < 0: + raise ValueError("'number_of_ticks' value must be a positive integer") + else: + self.number_of_ticks = int(number_of_ticks) + def _getSliderValue(self): return self.slider.value() From 72c14db5dba4a2fbed3313caaf580dd9ee81a71b Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 14:59:51 +0000 Subject: [PATCH 29/68] Replace references to Pyside2 with qtpy --- eqt/ui/UISliderWidget.py | 4 ++-- test/test_UISliderWidget.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index d3ef0bf..a6cf505 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -1,5 +1,5 @@ -from PySide2 import QtCore, QtGui -from PySide2.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider, QWidget +from qtpy import QtCore, QtGui +from qtpy.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider, QWidget class UISliderWidget(QWidget): diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 28e41f8..1214f33 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,8 +1,8 @@ import unittest from parameterized import parameterized -from PySide2 import QtGui -from PySide2.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider +from qtpy import QtGui +from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider from eqt.ui import UISliderWidget From a88d5dff59348c33d7bc3a563fb2504ac260f7f1 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 15:12:26 +0000 Subject: [PATCH 30/68] Add parameterized to list of optional dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3082427..c2d35ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ classifiers = [ dependencies = ["qtpy", "qdarkstyle"] [project.optional-dependencies] -dev = ["pytest>=6", "pytest-cov", "pytest-timeout"] +dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "parameterized"] [tool.flake8] max_line_length = 99 From 8c6de568f482dd358db0f49c2e5cf89899d3f745 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 15:21:41 +0000 Subject: [PATCH 31/68] Remove setUp() --- test/test_UISliderWidget.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 1214f33..919530c 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -8,9 +8,6 @@ class TestUISliderWidget(unittest.TestCase): - def setUp(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) - @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), ("negative", -10.0, -1.0), ("float", -0.5, 0.5), ("long", -9.99999, 9.99999)]) @@ -47,7 +44,7 @@ def test_input_invalid_min_max(self, _, minimum, maximum): ("long", 9.99999, 9)]) def test_input_decimals(self, _, decimals, expected): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, decimals=decimals) self.assertEqual(self.widget.decimals, expected) except ValueError: @@ -55,48 +52,53 @@ def test_input_decimals(self, _, decimals, expected): self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=decimals) - @parameterized.expand([("positive", 1500, 1500, 0.0006666666666666666), - ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 0.5), - ("long", 9.99999, 9, 0.11111111111111111111111111111111)]) + @parameterized.expand([("positive", 1500, 1500, 0.013333333333333334), + ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 10.0), + ("long", 9.99999, 9, 2.2222222222222223)]) def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, number_of_steps=number_of_steps) self.assertEqual(self.widget.number_of_steps, expected_steps) self.assertEqual(self.widget.step_size, expected_size) except ValueError: with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, number_of_steps=number_of_steps) @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, number_of_ticks=number_of_ticks) self.assertEqual(self.widget.number_of_ticks, expected_ticks) self.assertEqual(self.widget.tick_interval, expected_interval) except ValueError: with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, number_of_ticks=number_of_ticks) def test_init_default_slider(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.slider, QSlider) def test_init_default_validator(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.validator, QtGui.QValidator) def test_init_default_lineedit(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.line_edit, QLineEdit) def test_init_default_labels(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.min_label, QLabel) self.assertIsInstance(self.widget.median_label, QLabel) self.assertIsInstance(self.widget.max_label, QLabel) def test_init_default_gridlayout(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.widget_layout, QGridLayout) @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), @@ -108,32 +110,38 @@ def test_get_and_set_value(self, _, minimum, maximum, value, expected): self.assertEqual(self.widget.getValue(), expected) def test_get_slider_value(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.slider.setValue(50) self.assertEqual(self.widget._getSliderValue(), 50) def test_get_lineedit_value(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("5.0") self.assertEqual(self.widget._getLineEditValue(), 5.0) def test_acceptable_update_slider(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("10.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), 2000) self.assertEqual(self.widget.getValue(), 10.0) def test_gt_max_update_slider(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("11.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), self.widget.slider_maximum) self.assertEqual(self.widget.getValue(), self.widget.maximum) def test_invalid_update_slider(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("-11.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), self.widget.slider_minimum) self.assertEqual(self.widget.getValue(), self.widget.minimum) def test_update_lineedit(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.slider.setValue(1500) self.widget._updateLineEdit() self.assertEqual(self.widget._getLineEditValue(), 5.0) @@ -141,11 +149,13 @@ def test_update_lineedit(self): @parameterized.expand([("positive", 5.0, 1500), ("negative", -5.0, 500), ("float", 2.5, 1250), ("long", 9.99999, 1999)]) def test_scale_up(self, _, value, expected): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertEqual(self.widget._scaleUp(value), expected) @parameterized.expand([("positive", 1500, 5.0), ("negative", 500, -5.0), ("float", 1250, 2.5), ("long", 1999, 9.99)]) def test_scale_down(self, _, value, expected): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertEqual(self.widget._scaleDown(value), expected) def tearDown(self): From 706db47415744f4cff6f087aeb12845ba1c94b3d Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 15:36:08 +0000 Subject: [PATCH 32/68] Remove parameterized from optional dependencies, add skip_ci decorator to test_UISliderWidget class --- pyproject.toml | 2 +- test/test_UISliderWidget.py | 35 ++++++++++++++--------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c2d35ee..3082427 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ classifiers = [ dependencies = ["qtpy", "qdarkstyle"] [project.optional-dependencies] -dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "parameterized"] +dev = ["pytest>=6", "pytest-cov", "pytest-timeout"] [tool.flake8] max_line_length = 99 diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 919530c..1d5731e 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -6,8 +6,14 @@ from eqt.ui import UISliderWidget +from . import skip_ci + +@skip_ci class TestUISliderWidget(unittest.TestCase): + def setUp(self): + self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) + @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), ("negative", -10.0, -1.0), ("float", -0.5, 0.5), ("long", -9.99999, 9.99999)]) @@ -44,7 +50,7 @@ def test_input_invalid_min_max(self, _, minimum, maximum): ("long", 9.99999, 9)]) def test_input_decimals(self, _, decimals, expected): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=decimals) self.assertEqual(self.widget.decimals, expected) except ValueError: @@ -52,53 +58,48 @@ def test_input_decimals(self, _, decimals, expected): self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=decimals) - @parameterized.expand([("positive", 1500, 1500, 0.013333333333333334), - ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 10.0), - ("long", 9.99999, 9, 2.2222222222222223)]) + @parameterized.expand([("positive", 1500, 1500, 0.0006666666666666666), + ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 0.5), + ("long", 9.99999, 9, 0.11111111111111111111111111111111)]) def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_steps=number_of_steps) self.assertEqual(self.widget.number_of_steps, expected_steps) self.assertEqual(self.widget.step_size, expected_size) except ValueError: with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_steps=number_of_steps) @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): try: - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=number_of_ticks) self.assertEqual(self.widget.number_of_ticks, expected_ticks) self.assertEqual(self.widget.tick_interval, expected_interval) except ValueError: with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0, + self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=number_of_ticks) def test_init_default_slider(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.slider, QSlider) def test_init_default_validator(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.validator, QtGui.QValidator) def test_init_default_lineedit(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.line_edit, QLineEdit) def test_init_default_labels(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.min_label, QLabel) self.assertIsInstance(self.widget.median_label, QLabel) self.assertIsInstance(self.widget.max_label, QLabel) def test_init_default_gridlayout(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertIsInstance(self.widget.widget_layout, QGridLayout) @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), @@ -110,38 +111,32 @@ def test_get_and_set_value(self, _, minimum, maximum, value, expected): self.assertEqual(self.widget.getValue(), expected) def test_get_slider_value(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.slider.setValue(50) self.assertEqual(self.widget._getSliderValue(), 50) def test_get_lineedit_value(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("5.0") self.assertEqual(self.widget._getLineEditValue(), 5.0) def test_acceptable_update_slider(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("10.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), 2000) self.assertEqual(self.widget.getValue(), 10.0) def test_gt_max_update_slider(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("11.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), self.widget.slider_maximum) self.assertEqual(self.widget.getValue(), self.widget.maximum) def test_invalid_update_slider(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.line_edit.setText("-11.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), self.widget.slider_minimum) self.assertEqual(self.widget.getValue(), self.widget.minimum) def test_update_lineedit(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.widget.slider.setValue(1500) self.widget._updateLineEdit() self.assertEqual(self.widget._getLineEditValue(), 5.0) @@ -149,13 +144,11 @@ def test_update_lineedit(self): @parameterized.expand([("positive", 5.0, 1500), ("negative", -5.0, 500), ("float", 2.5, 1250), ("long", 9.99999, 1999)]) def test_scale_up(self, _, value, expected): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertEqual(self.widget._scaleUp(value), expected) @parameterized.expand([("positive", 1500, 5.0), ("negative", 500, -5.0), ("float", 1250, 2.5), ("long", 1999, 9.99)]) def test_scale_down(self, _, value, expected): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) self.assertEqual(self.widget._scaleDown(value), expected) def tearDown(self): From 150fdbf87b38447bab9382dda31bd2d1e5284942 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 15:38:11 +0000 Subject: [PATCH 33/68] Add parameterized to optional dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3082427..c2d35ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ classifiers = [ dependencies = ["qtpy", "qdarkstyle"] [project.optional-dependencies] -dev = ["pytest>=6", "pytest-cov", "pytest-timeout"] +dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "parameterized"] [tool.flake8] max_line_length = 99 From a975d7fd80243fb52f8a4a96022f9c0e7efe99ba Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 15:42:22 +0000 Subject: [PATCH 34/68] Add returnPressed signal to UISliderWidget --- eqt/ui/UISliderWidget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index a6cf505..058dde4 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -68,6 +68,7 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of # Connect the QLineEdit to the QSlider self.line_edit.editingFinished.connect(self._updateSlider) + self.line_edit.returnPressed.connect(self._updateSlider) # Configure the QApplication self.app = QApplication.instance() From 112eecfa50d4f9faf4d3e8f6b522a03812a171fa Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 16:27:37 +0000 Subject: [PATCH 35/68] Fix error when QLineEdit return an empty string --- eqt/ui/UISliderWidget.py | 5 ++++- test/test_UISliderWidget.py | 17 +++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 058dde4..e253054 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -136,7 +136,10 @@ def _getSliderValue(self): return self.slider.value() def _getLineEditValue(self): - return float(self.line_edit.text()) + if self.line_edit.text() == '': + return self.minimum + else: + return float(self.line_edit.text()) def _updateSlider(self): line_edit_value = self._getLineEditValue() diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 1d5731e..dd17604 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -124,17 +124,14 @@ def test_acceptable_update_slider(self): self.assertEqual(self.widget._getSliderValue(), 2000) self.assertEqual(self.widget.getValue(), 10.0) - def test_gt_max_update_slider(self): - self.widget.line_edit.setText("11.0") + @parameterized.expand([("empty", "", 0, -10.0), ("lt_min", "-11.0", 0, -10.0), + ("gt_max", "11.0", 2000, 10.0)]) + def test_invalid_update_slider(self, _, value, expected_slider_value, + expected_line_edit_value): + self.widget.line_edit.setText(value) self.widget._updateSlider() - self.assertEqual(self.widget._getSliderValue(), self.widget.slider_maximum) - self.assertEqual(self.widget.getValue(), self.widget.maximum) - - def test_invalid_update_slider(self): - self.widget.line_edit.setText("-11.0") - self.widget._updateSlider() - self.assertEqual(self.widget._getSliderValue(), self.widget.slider_minimum) - self.assertEqual(self.widget.getValue(), self.widget.minimum) + self.assertEqual(self.widget._getSliderValue(), expected_slider_value) + self.assertEqual(self.widget.getValue(), expected_line_edit_value) def test_update_lineedit(self): self.widget.slider.setValue(1500) From e0141c1400acf2c44d78107e2e98bc45563ad6e9 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 17:37:13 +0000 Subject: [PATCH 36/68] Modify UISliderWidget default number_of_ticks, add _updateSlider() call to setValue() method --- eqt/ui/UISliderWidget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index e253054..e872d10 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -20,7 +20,7 @@ class UISliderWidget(QWidget): number_of_ticks : int - Number of ticks visualised under the QSlider, determines tick interval ''' - def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): + def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=100): QWidget.__init__(self) # Check that the minimum/maximum arguments are valid @@ -113,6 +113,7 @@ def setValue(self, value): value : float ''' self.line_edit.setText(str(value)) + self._updateSlider() def _setDecimals(self, decimals): if decimals < 0: From 737f93c927e6c86c71a0a2fa4499d3632bc725cd Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 17:42:05 +0000 Subject: [PATCH 37/68] Correct recursive method call in setValue() --- eqt/ui/UISliderWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index e872d10..5e8478f 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -113,7 +113,7 @@ def setValue(self, value): value : float ''' self.line_edit.setText(str(value)) - self._updateSlider() + self.slider.setValue(self._scaleUp(self._getLineEditValue())) def _setDecimals(self, decimals): if decimals < 0: From df480fc5ba647051ad348f34c95bc2ff62eb2574 Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 17 Jan 2025 17:44:50 +0000 Subject: [PATCH 38/68] Modify UISliderWidget default number_of_ticks --- eqt/ui/UISliderWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 5e8478f..debb4bb 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -20,7 +20,7 @@ class UISliderWidget(QWidget): number_of_ticks : int - Number of ticks visualised under the QSlider, determines tick interval ''' - def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=100): + def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): QWidget.__init__(self) # Check that the minimum/maximum arguments are valid From b25d1517e5cc61f2ab6ae52c81ea87c9f55e7775 Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 20 Jan 2025 10:20:47 +0000 Subject: [PATCH 39/68] Correct expected value in parameterized unit test --- test/test_UISliderWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index dd17604..4fc9728 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -104,7 +104,7 @@ def test_init_default_gridlayout(self): @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), ("decimal", -0.5, 0.5, 0.25, 0.25), - ("long", -9.99999, 9.99999, 1.11111, 1.11111)]) + ("long", -9.99999, 9.99999, 1.11111, 1.11)]) def test_get_and_set_value(self, _, minimum, maximum, value, expected): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) self.widget.setValue(value) From c3431fd400f2012704f90661ee0d85fc025f1abb Mon Sep 17 00:00:00 2001 From: jcornall Date: Tue, 21 Jan 2025 15:29:11 +0000 Subject: [PATCH 40/68] Add additional docstrings to UISliderWidget methods and tests --- eqt/ui/UISliderWidget.py | 73 ++++++++++++---- examples/dialog_example_uislider.py | 4 +- test/test_UISliderWidget.py | 128 ++++++++++++++++++++-------- 3 files changed, 154 insertions(+), 51 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index debb4bb..9212574 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -14,7 +14,7 @@ class UISliderWidget(QWidget): maximum : float - Maximum value of the QLineEdit decimals : int - - Number of decimal places that the QLabels and QSlider steps can display + - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display number_of_steps : int - Number of steps in the QSlider number_of_ticks : int @@ -41,7 +41,6 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.step_size = float((self.maximum - self.minimum) / self.number_of_steps) self.tick_interval = round( (self.slider_maximum - self.slider_minimum) / self.number_of_ticks) - # self.number_of_steps = round((self.maximum - self.minimum) / self.step_size) # Configure the QSlider self.slider = QSlider() @@ -113,60 +112,103 @@ def setValue(self, value): value : float ''' self.line_edit.setText(str(value)) - self.slider.setValue(self._scaleUp(self._getLineEditValue())) + self.slider.setValue(self._scaleLineEditToSlider(self._getLineEditValue())) def _setDecimals(self, decimals): + '''Sets the number of decimal places that the QLabels, QLineEdit and + QSlider steps can display. Also checks that the argument provided is valid, + i.e. that it is a positive integer value - if the value is invalid, the method raises + a ValueError during object instantiation. + + Parameters + ---------- + decimals : int + ''' if decimals < 0: raise ValueError("'decimals' value must be a positive integer") else: self.decimals = int(decimals) def _setNumberOfSteps(self, number_of_steps): + '''Sets the number of steps in the QSlider. Steps are each subdivision of the + QSlider's range. Also checks that the argument provided is valid, i.e. that + it is a positive integer value - if the value is invalid, the method raises + a ValueError during object instantiation. + + Parameters + ---------- + number_of_steps : int + ''' if number_of_steps < 0: raise ValueError("'number_of_steps' value must be a positive integer") else: self.number_of_steps = int(number_of_steps) def _setNumberOfTicks(self, number_of_ticks): + '''Sets the number of ticks that the QSlider displays. Ticks are the notches + displayed underneath the QSlider. Also checks that the argument provided is + valid, i.e. that it is a positive integer value - if the value is invalid, + the method raises a ValueError during object instantiation. + + Parameters + ---------- + number_of_ticks : int + ''' if number_of_ticks < 0: raise ValueError("'number_of_ticks' value must be a positive integer") else: self.number_of_ticks = int(number_of_ticks) def _getSliderValue(self): + '''Gets the current value of the QSlider, returning either 0 or a positive integer. + ''' return self.slider.value() def _getLineEditValue(self): + '''Gets the current value of the QLineEdit. If the QLineEdit is empty, + this method returns the UISliderWidget's minimum value - otherwise, it + returns a float value between the UISliderWidget's minimum and maximum values. + ''' if self.line_edit.text() == '': return self.minimum else: return float(self.line_edit.text()) def _updateSlider(self): + '''Updates the QSlider to reflect the current value of the QLineEdit. + The method uses the state of the QValidator to check that the QLineEdit + value is valid - if it is valid, it sets the value of the QSlider to the + scaled value of the QLineEdit. Otherwise, it will update the QSlider with + either the scaled value of the QLineEdit's minimum or maximum. + ''' line_edit_value = self._getLineEditValue() state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - self.slider.setValue(self._scaleUp(line_edit_value)) + self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) self.setValue(line_edit_value) elif line_edit_value > self.maximum: self.line_edit.setText(str(self.maximum)) line_edit_value = self._getLineEditValue() - self.slider.setValue(self._scaleUp(line_edit_value)) + self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) line_edit_value = self._getLineEditValue() - self.slider.setValue(self._scaleUp(line_edit_value)) + self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) self.setValue(line_edit_value) def _updateLineEdit(self): + '''Updates the QLineEdit to reflect the current value of the QSlider. + The method sets the value of the QLineEdit to the scaled value of the QSlider. + ''' slider_value = self._getSliderValue() - self.line_edit.setText(str(self._scaleDown(slider_value))) + self.line_edit.setText(str(self._scaleSliderToLineEdit(slider_value))) - def _scaleUp(self, value): - '''Scales a value up. The method calculates the appropriate scale factor for the conversion - using the minimum and maximum values of the QSlider and QLineEdit. - Returns the upscaled value. + def _scaleLineEditToSlider(self, value): + '''Converts a QLineEdit value to a scaled QSlider value. The method calculates + the appropriate scale factor for the conversion using the minimum and maximum + values of the QSlider and QLineEdit. + Returns the scaled value. Parameters ---------- @@ -176,10 +218,11 @@ def _scaleUp(self, value): value = self.slider_minimum + (scale_factor * (value - self.minimum)) return int(value) - def _scaleDown(self, value): - '''Scales a value down. The method calculates the appropriate scale factor for the - conversion using the minimum and maximum values of the QSlider and QLineEdit. - Returns the downscaled value, rounded as per the decimals attribute. + def _scaleSliderToLineEdit(self, value): + '''Converts a QSlider value to a scaled QLineEdit value. The method calculates + the appropriate scale factor for the conversion using the minimum and maximum + values of the QSlider and QLineEdit. + Returns the scaled value, rounded as per the decimals property. Parameters ---------- diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 788796e..bafcf68 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -27,8 +27,8 @@ def openFormDialog(self): dialog.Ok.clicked.connect(lambda: self.accepted()) # Create UISliderWidget - uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=2, - number_of_steps=2000, number_of_ticks=10) + uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, + number_of_steps=10, number_of_ticks=10) # add to the form widget dialog.addWidget(uislider, 'UISlider:', 'input_slider') diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 4fc9728..e28b8a0 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -12,43 +12,68 @@ @skip_ci class TestUISliderWidget(unittest.TestCase): def setUp(self): - self.widget = UISliderWidget.UISliderWidget(minimum=-10.0, maximum=10.0) + self.test_widgets = { + "standard": {"minimum": 0.0, + "maximum": 10.0}, "positive": {"minimum": 1.0, "maximum": 10.0}, + "negative": {"minimum": -10.0, + "maximum": -1.0}, "float": {"minimum": -0.5, "maximum": 0.5}, + "long": {"minimum": 1.11111, "maximum": 9.99999}} @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), - ("negative", -10.0, -1.0), ("float", -0.5, 0.5), - ("long", -9.99999, 9.99999)]) - def test_init_default_widget(self, _, minimum, maximum): - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertIsInstance(self.widget, UISliderWidget.UISliderWidget) - - @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), - ("negative", -10.0, -1.0), ("float", -0.5, 0.5), - ("long", -9.99999, 9.99999)]) - def test_init_default_properties(self, _, minimum, maximum): - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertEqual(self.widget.decimals, 2) - self.assertEqual(self.widget.number_of_steps, 2000) - self.assertEqual(self.widget.number_of_ticks, 10) - - @parameterized.expand([("standard", 0.0, 10.0, 5.0), ("positive", 1.0, 10.0, 5.5), - ("negative", -10.0, -1.0, -5.5), ("float", -0.5, 0.5, 0.0), - ("long", 1.11111, 9.99999, 5.5600000000000005)]) - def test_init_median(self, _, minimum, maximum, median): - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertEqual(self.widget.median, median) - - def _test_widget_state(self): - self.assertTrue(self.widget.isVisible()) - self.assertTrue(self.widget.isEnabled()) + ("negative", -10.0, -1.0), ("float", -0.5, 0.5), ("long", 1.11, 10.0)]) + def test_init_widget(self, _, expected_minimum, expected_maximum): + '''Tests widget instantiation using different 'minimum'/'maximum' arguments. + ''' + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertIsInstance(widget, UISliderWidget.UISliderWidget) + self.assertEqual(widget.minimum, expected_minimum) + self.assertEqual(widget.maximum, expected_maximum) + + def test_init_default_properties(self): + '''Tests the setting of the widget's default properties. + ''' + for widget in self.test_widgets.values(): + minimum = widget.get("minimum") + maximum = widget.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertEqual(widget.decimals, 2) + self.assertEqual(widget.number_of_steps, 2000) + self.assertEqual(widget.number_of_ticks, 10) + + @parameterized.expand([("standard", 5.0), ("positive", 5.5), ("negative", -5.5), + ("float", 0.0), ("long", 5.5600000000000005)]) + def test_init_median(self, _, median): + '''Tests the calculation of the 'median' property. + ''' + for widget in self.test_widgets.values(): + widget = self.test_widgets.get(_) + self.assertEqual(widget.median, median) + + def test_widget_state(self): + '''Tests the widget default widget states. + ''' + for widget in self.test_widgets.values(): + self.assertTrue(widget.isVisible()) + self.assertTrue(widget.isEnabled()) @parameterized.expand([("min_gt_max", 10.0, 0.0), ("min_eq_max", 0.0, 0.0)]) def test_input_invalid_min_max(self, _, minimum, maximum): + '''Tests for correct widget behaviour when invalid combinations + of 'minimum'/'maximum' are supplied. + ''' with self.assertRaises(ValueError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) @parameterized.expand([("positive", 10, 10), ("negative", -10, 0), ("float", 2.5, 2), ("long", 9.99999, 9)]) def test_input_decimals(self, _, decimals, expected): + '''Tests the correct widget behaviour when different 'decimals' + arguments are supplied. + If an invalid argument is supplied, a ValueError is raised. + ''' + try: self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=decimals) @@ -61,7 +86,11 @@ def test_input_decimals(self, _, decimals, expected): @parameterized.expand([("positive", 1500, 1500, 0.0006666666666666666), ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 0.5), ("long", 9.99999, 9, 0.11111111111111111111111111111111)]) - def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): + def test_input_number_of_steps(self, name, number_of_steps, expected_steps, expected_size): + '''Tests the correct widget behaviour when different 'number_of_steps' + arguments are supplied. + If an invalid argument is supplied, a ValueError is raised. + ''' try: self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_steps=number_of_steps) @@ -74,51 +103,74 @@ def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expecte @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) - def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): + def test_input_number_of_ticks(self, name, number_of_ticks, expected_ticks, expected_interval): + '''Tests the correct widget behaviour when different 'number_of_ticks' + arguments are supplied. + If an invalid argument is supplied, a ValueError is raised. + ''' try: self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=number_of_ticks) self.assertEqual(self.widget.number_of_ticks, expected_ticks) self.assertEqual(self.widget.tick_interval, expected_interval) + self.assertEqual(self.widget.slider.tickInterval(), expected_interval) except ValueError: with self.assertRaises(ValueError): self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, number_of_ticks=number_of_ticks) def test_init_default_slider(self): + '''Tests the instantiation of the QSlider widget. + ''' self.assertIsInstance(self.widget.slider, QSlider) def test_init_default_validator(self): + '''Tests the instantiation of the QValidator widget. + ''' self.assertIsInstance(self.widget.validator, QtGui.QValidator) def test_init_default_lineedit(self): + '''Tests the instantiation of the QLineEdit widget. + ''' self.assertIsInstance(self.widget.line_edit, QLineEdit) def test_init_default_labels(self): + '''Tests the instantiation of the QLabel widgets. + ''' self.assertIsInstance(self.widget.min_label, QLabel) self.assertIsInstance(self.widget.median_label, QLabel) self.assertIsInstance(self.widget.max_label, QLabel) def test_init_default_gridlayout(self): + '''Tests the instantiation of the QGridLayout. + ''' self.assertIsInstance(self.widget.widget_layout, QGridLayout) @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), ("decimal", -0.5, 0.5, 0.25, 0.25), ("long", -9.99999, 9.99999, 1.11111, 1.11)]) - def test_get_and_set_value(self, _, minimum, maximum, value, expected): + def test_get_and_set_value(self, name, minimum, maximum, value, expected): + '''Tests the getting and setting of the widget's 'value' property. + ''' self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) self.widget.setValue(value) self.assertEqual(self.widget.getValue(), expected) def test_get_slider_value(self): + '''Tests getting the QSlider value. + ''' self.widget.slider.setValue(50) self.assertEqual(self.widget._getSliderValue(), 50) def test_get_lineedit_value(self): + '''Tests getting the QLineEdit value. + ''' self.widget.line_edit.setText("5.0") self.assertEqual(self.widget._getLineEditValue(), 5.0) def test_acceptable_update_slider(self): + '''Tests updating the QSlider with a valid QLineEdit value. + ''' self.widget.line_edit.setText("10.0") self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), 2000) @@ -126,27 +178,35 @@ def test_acceptable_update_slider(self): @parameterized.expand([("empty", "", 0, -10.0), ("lt_min", "-11.0", 0, -10.0), ("gt_max", "11.0", 2000, 10.0)]) - def test_invalid_update_slider(self, _, value, expected_slider_value, + def test_invalid_update_slider(self, name, value, expected_slider_value, expected_line_edit_value): + '''Tests updating the QSlider with a valid QLineEdit value. + ''' self.widget.line_edit.setText(value) self.widget._updateSlider() self.assertEqual(self.widget._getSliderValue(), expected_slider_value) self.assertEqual(self.widget.getValue(), expected_line_edit_value) def test_update_lineedit(self): + '''Tests updating the QLineEdit with a valid QSlider value. + ''' self.widget.slider.setValue(1500) self.widget._updateLineEdit() self.assertEqual(self.widget._getLineEditValue(), 5.0) @parameterized.expand([("positive", 5.0, 1500), ("negative", -5.0, 500), ("float", 2.5, 1250), ("long", 9.99999, 1999)]) - def test_scale_up(self, _, value, expected): - self.assertEqual(self.widget._scaleUp(value), expected) + def test_scale_lineedit_to_slider(self, name, value, expected): + '''Tests converting a valid QLineEdit value into a scaled QSlider value. + ''' + self.assertEqual(self.widget._scaleLineEditToSlider(value), expected) @parameterized.expand([("positive", 1500, 5.0), ("negative", 500, -5.0), ("float", 1250, 2.5), ("long", 1999, 9.99)]) - def test_scale_down(self, _, value, expected): - self.assertEqual(self.widget._scaleDown(value), expected) + def test_scale_slider_to_lineedit(self, name, value, expected): + '''Tests converting a valid QSlider value into a scaled QLineEdit value. + ''' + self.assertEqual(self.widget._scaleSliderToLineEdit(value), expected) def tearDown(self): self.form = None From b062bab41249c31a3867e7fee653ef5abf1d9b3d Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 22 Jan 2025 11:23:08 +0000 Subject: [PATCH 41/68] Add additional parameterized test cases --- test/test_UISliderWidget.py | 245 ++++++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 81 deletions(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index e28b8a0..2c72839 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -22,11 +22,12 @@ def setUp(self): @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), ("negative", -10.0, -1.0), ("float", -0.5, 0.5), ("long", 1.11, 10.0)]) def test_init_widget(self, _, expected_minimum, expected_maximum): - '''Tests widget instantiation using different 'minimum'/'maximum' arguments. + '''Tests widget instantiation using 'minimum'/'maximum' arguments. ''' minimum = self.test_widgets.get(_).get("minimum") maximum = self.test_widgets.get(_).get("maximum") widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertIsInstance(widget, UISliderWidget.UISliderWidget) self.assertEqual(widget.minimum, expected_minimum) self.assertEqual(widget.maximum, expected_maximum) @@ -34,10 +35,11 @@ def test_init_widget(self, _, expected_minimum, expected_maximum): def test_init_default_properties(self): '''Tests the setting of the widget's default properties. ''' - for widget in self.test_widgets.values(): - minimum = widget.get("minimum") - maximum = widget.get("maximum") + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertEqual(widget.decimals, 2) self.assertEqual(widget.number_of_steps, 2000) self.assertEqual(widget.number_of_ticks, 10) @@ -47,14 +49,20 @@ def test_init_default_properties(self): def test_init_median(self, _, median): '''Tests the calculation of the 'median' property. ''' - for widget in self.test_widgets.values(): - widget = self.test_widgets.get(_) - self.assertEqual(widget.median, median) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertEqual(widget.median, median) def test_widget_state(self): - '''Tests the widget default widget states. + '''Tests default widget states. ''' - for widget in self.test_widgets.values(): + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.assertTrue(widget.isVisible()) self.assertTrue(widget.isEnabled()) @@ -73,140 +81,215 @@ def test_input_decimals(self, _, decimals, expected): arguments are supplied. If an invalid argument is supplied, a ValueError is raised. ''' + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") - try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - decimals=decimals) - self.assertEqual(self.widget.decimals, expected) - except ValueError: - with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - decimals=decimals) + try: + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + decimals=decimals) + self.assertEqual(widget.decimals, expected) + except ValueError: + with self.assertRaises(ValueError): + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + decimals=decimals) - @parameterized.expand([("positive", 1500, 1500, 0.0006666666666666666), - ("negative", -50, 2000, 0.0005), ("float", 2.5, 2, 0.5), - ("long", 9.99999, 9, 0.11111111111111111111111111111111)]) - def test_input_number_of_steps(self, name, number_of_steps, expected_steps, expected_size): + @parameterized.expand([("positive", 1500, 1500, 0.006), ("negative", -50, 2000, 0.0005), + ("float", 2.5, 2, 0.5), ("long", 9.99999, 9, 0.9877777777777779)]) + def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): '''Tests the correct widget behaviour when different 'number_of_steps' arguments are supplied. If an invalid argument is supplied, a ValueError is raised. ''' + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - number_of_steps=number_of_steps) - self.assertEqual(self.widget.number_of_steps, expected_steps) - self.assertEqual(self.widget.step_size, expected_size) + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) + self.assertEqual(widget.number_of_steps, expected_steps) + self.assertEqual(widget.step_size, expected_size) except ValueError: with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - number_of_steps=number_of_steps) + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) - def test_input_number_of_ticks(self, name, number_of_ticks, expected_ticks, expected_interval): + def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): '''Tests the correct widget behaviour when different 'number_of_ticks' arguments are supplied. If an invalid argument is supplied, a ValueError is raised. ''' - try: - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - number_of_ticks=number_of_ticks) - self.assertEqual(self.widget.number_of_ticks, expected_ticks) - self.assertEqual(self.widget.tick_interval, expected_interval) - self.assertEqual(self.widget.slider.tickInterval(), expected_interval) - except ValueError: - with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, - number_of_ticks=number_of_ticks) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + + try: + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_ticks=number_of_ticks) + self.assertEqual(widget.number_of_ticks, expected_ticks) + self.assertEqual(widget.tick_interval, expected_interval) + self.assertEqual(widget.slider.tickInterval(), expected_interval) + except ValueError: + with self.assertRaises(ValueError): + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_ticks=number_of_ticks) def test_init_default_slider(self): '''Tests the instantiation of the QSlider widget. ''' - self.assertIsInstance(self.widget.slider, QSlider) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertIsInstance(widget.slider, QSlider) def test_init_default_validator(self): '''Tests the instantiation of the QValidator widget. ''' - self.assertIsInstance(self.widget.validator, QtGui.QValidator) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertIsInstance(widget.validator, QtGui.QValidator) def test_init_default_lineedit(self): '''Tests the instantiation of the QLineEdit widget. ''' - self.assertIsInstance(self.widget.line_edit, QLineEdit) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertIsInstance(widget.line_edit, QLineEdit) def test_init_default_labels(self): '''Tests the instantiation of the QLabel widgets. ''' - self.assertIsInstance(self.widget.min_label, QLabel) - self.assertIsInstance(self.widget.median_label, QLabel) - self.assertIsInstance(self.widget.max_label, QLabel) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertIsInstance(widget.min_label, QLabel) + self.assertIsInstance(widget.median_label, QLabel) + self.assertIsInstance(widget.max_label, QLabel) def test_init_default_gridlayout(self): '''Tests the instantiation of the QGridLayout. ''' - self.assertIsInstance(self.widget.widget_layout, QGridLayout) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("positive", 0.0, 10.0, 5.0, 5.0), ("negative", -10.0, 0.0, -5.0, -5.0), - ("decimal", -0.5, 0.5, 0.25, 0.25), - ("long", -9.99999, 9.99999, 1.11111, 1.11)]) - def test_get_and_set_value(self, name, minimum, maximum, value, expected): + self.assertIsInstance(widget.widget_layout, QGridLayout) + + @parameterized.expand([("positive", 5.0, 5.0), ("negative", -5.0, -5.0), ("float", 0.25, 0.25), + ("long", 5.55555, 5.56)]) + def test_get_and_set_value(self, _, widget_value, expected): '''Tests the getting and setting of the widget's 'value' property. ''' - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.widget.setValue(value) - self.assertEqual(self.widget.getValue(), expected) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + widget.setValue(widget_value) + self.assertEqual(widget.getValue(), expected) def test_get_slider_value(self): '''Tests getting the QSlider value. ''' - self.widget.slider.setValue(50) - self.assertEqual(self.widget._getSliderValue(), 50) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + widget.slider.setValue(50) + self.assertEqual(widget._getSliderValue(), 50) - def test_get_lineedit_value(self): + @parameterized.expand([("positive", "5.0", 5.0), ("negative", "-5.0", -5.0), + ("float", "0.25", 0.25), ("long", "5.55555", 5.55555)]) + def test_get_lineedit_value(self, _, line_edit_value, expected): '''Tests getting the QLineEdit value. ''' - self.widget.line_edit.setText("5.0") - self.assertEqual(self.widget._getLineEditValue(), 5.0) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + widget.line_edit.setText(line_edit_value) + self.assertEqual(widget._getLineEditValue(), expected) - def test_acceptable_update_slider(self): + @parameterized.expand([("positive", "5.0", 888, 5.0), ("negative", "-5.0", 1111, -5.0), + ("float", "0.25", 1500, 0.25), ("long", "5.55555", 1000, 5.55555)]) + def test_acceptable_update_slider(self, _, line_edit_value, expected_slider_value, + expected_line_edit_value): '''Tests updating the QSlider with a valid QLineEdit value. ''' - self.widget.line_edit.setText("10.0") - self.widget._updateSlider() - self.assertEqual(self.widget._getSliderValue(), 2000) - self.assertEqual(self.widget.getValue(), 10.0) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + widget.line_edit.setText(line_edit_value) + widget._updateSlider() + self.assertEqual(widget._getSliderValue(), expected_slider_value) + self.assertEqual(widget.getValue(), expected_line_edit_value) - @parameterized.expand([("empty", "", 0, -10.0), ("lt_min", "-11.0", 0, -10.0), - ("gt_max", "11.0", 2000, 10.0)]) - def test_invalid_update_slider(self, name, value, expected_slider_value, - expected_line_edit_value): + @parameterized.expand([("empty", "", 0), ("lt_min", "-11.0", 0), ("gt_max", "11.0", 2000)]) + def test_invalid_update_slider(self, _, line_edit_value, expected_slider_value): '''Tests updating the QSlider with a valid QLineEdit value. ''' - self.widget.line_edit.setText(value) - self.widget._updateSlider() - self.assertEqual(self.widget._getSliderValue(), expected_slider_value) - self.assertEqual(self.widget.getValue(), expected_line_edit_value) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - def test_update_lineedit(self): + widget.line_edit.setText(line_edit_value) + widget._updateSlider() + self.assertEqual(widget._getSliderValue(), expected_slider_value) + if _ == "gt_max": + self.assertEqual(widget.getValue(), widget.maximum) + elif _ == "empty" or "lt_min": + self.assertEqual(widget.getValue(), widget.minimum) + + @parameterized.expand([("positive", 888, 5.0), ("negative", 1111, -5.0), ("float", 1500, 0.25), + ("long", 1000, 5.56)]) + def test_update_lineedit(self, _, slider_value, expected_line_edit_value): '''Tests updating the QLineEdit with a valid QSlider value. ''' - self.widget.slider.setValue(1500) - self.widget._updateLineEdit() - self.assertEqual(self.widget._getLineEditValue(), 5.0) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("positive", 5.0, 1500), ("negative", -5.0, 500), ("float", 2.5, 1250), + widget.slider.setValue(slider_value) + widget._updateLineEdit() + self.assertEqual(widget._getLineEditValue(), expected_line_edit_value) + + @parameterized.expand([("positive", 5.0, 888), ("negative", -5.0, 1111), ("float", 2.5, 6000), ("long", 9.99999, 1999)]) - def test_scale_lineedit_to_slider(self, name, value, expected): + def test_scale_lineedit_to_slider(self, _, line_edit_value, expected): '''Tests converting a valid QLineEdit value into a scaled QSlider value. ''' - self.assertEqual(self.widget._scaleLineEditToSlider(value), expected) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertEqual(widget._scaleLineEditToSlider(line_edit_value), expected) - @parameterized.expand([("positive", 1500, 5.0), ("negative", 500, -5.0), ("float", 1250, 2.5), - ("long", 1999, 9.99)]) - def test_scale_slider_to_lineedit(self, name, value, expected): + @parameterized.expand([("positive", 1000, 5.5), ("negative", 1000, -5.5), ("float", 1000, 0.0), + ("long", 1000, 5.56)]) + def test_scale_slider_to_lineedit(self, _, slider_value, expected): '''Tests converting a valid QSlider value into a scaled QLineEdit value. ''' - self.assertEqual(self.widget._scaleSliderToLineEdit(value), expected) + minimum = self.test_widgets.get(_).get("minimum") + maximum = self.test_widgets.get(_).get("maximum") + widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertEqual(widget._scaleSliderToLineEdit(slider_value), expected) def tearDown(self): self.form = None From edd28e0a683c299d9b2a8ddc6a86fef051fcd6e1 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 22 Jan 2025 15:29:46 +0000 Subject: [PATCH 42/68] Update docstrings --- eqt/ui/UIFormWidget.py | 8 +-- eqt/ui/UISliderWidget.py | 72 +++++++++++++------ ...MainWindowWithSessionManagement_example.py | 5 +- examples/dialog_example_uislider.py | 2 +- examples/dialog_save_state_example.py | 9 +-- examples/utilitiesForExamples.py | 6 +- test/test_UISliderWidget.py | 10 +-- 7 files changed, 72 insertions(+), 40 deletions(-) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 644c817..5152048 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -327,6 +327,8 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.currentIndex() elif isinstance(widget, QtWidgets.QSlider): widget_state['value'] = widget.value() + elif isinstance(widget, UISliderWidget): + widget_state['value'] = widget.value() elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget_state['value'] = widget.value() elif isinstance(widget, QtWidgets.QLineEdit): @@ -335,8 +337,6 @@ def getWidgetState(self, widget, role=None): widget_state['value'] = widget.isChecked() elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget_state['value'] = widget.toPlainText() - elif isinstance(widget, UISliderWidget): - widget_state['value'] = widget.getValue() widget_state['enabled'] = widget.isEnabled() widget_state['visible'] = widget.isVisible() widget_state['widget_row'] = self.getWidgetRow(name, role) @@ -412,6 +412,8 @@ def applyWidgetState(self, name, state, role=None): widget.setCurrentIndex(value) elif isinstance(widget, (QtWidgets.QSlider)): widget.setValue(value) + elif isinstance(widget, (UISliderWidget)): + widget.setValue(value) elif isinstance(widget, (QtWidgets.QDoubleSpinBox, QtWidgets.QSpinBox)): widget.setValue(value) elif isinstance(widget, QtWidgets.QPushButton): @@ -422,8 +424,6 @@ def applyWidgetState(self, name, state, role=None): widget.setChecked(value) elif isinstance(widget, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit)): widget.setPlainText(value) - elif isinstance(widget, (UISliderWidget)): - widget.setValue(value) def applyWidgetStates(self, states): ''' diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 9212574..f3cbd1a 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -3,24 +3,51 @@ class UISliderWidget(QWidget): - '''Creates a QGridLayout that includes a QSlider, min/median/max QLabels and a QLineEdit. - Updating a widget scales the value appropriately for the other widget, - i.e. the QLineEdit is updated with the value of the slider and vice versa. - - Parameters - ---------- - minimum : float - - Minimum value of the QLineEdit - maximum : float - - Maximum value of the QLineEdit - decimals : int - - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display - number_of_steps : int - - Number of steps in the QSlider - number_of_ticks : int - - Number of ticks visualised under the QSlider, determines tick interval + '''Creates a QSlider widget with an attached QLineEdit, displaying minimum, maximum + and median values. + + This class creates a QGridLayout that includes a QSlider, min/median/max QLabels and + a QLineEdit. When the QSlider value is changed, the widget converts/scales the value + and updates the QLineEdit with the new value, and vice versa. ''' def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): + '''Creates the QGridLayout and the widgets that populate it (QSlider, QLineEdit, + QLabels). + + Also sets a number of attributes that control the appearance and behaviour of + the QSlider and QLineEdit. + + 'minimum' and 'maximum' are required arguments when creating a UISliderWidget object. + An if statement checks whether the arguments are valid before they are set. + + The QGridLayout is responsible for the arrangement of the widgets within the + UISliderWidget. The QSlider and QLineEdit are configured to span the entire width of + the window. The QLabels for the min/max/median appear underneath the QSlider, arranged + to the left, center and right respectively. + + Signals from the QSlider and QLineEdit are connected to the _updateSlider() and + _updateLineEdit() methods, linking user interactions with these widgets together. + The update methods call the scaling methods (_scaleLineEditToSlider(), + _scaleSliderToLineEdit()) so that changes to one widget are accurately represented + in the other widget. + + The value() and setValue() methods provide an interface for forms and dialogs to + get and set the value of the widget. Some private methods exist (e.g. _setDecimals()) + that are responsible for validating and setting arguments. + + Parameters + ---------- + minimum : float + - Minimum value of the QLineEdit + maximum : float + - Maximum value of the QLineEdit + decimals : int + - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display + number_of_steps : int + - Number of steps in the QSlider + number_of_ticks : int + - Number of ticks visualised under the QSlider, determines tick interval + ''' QWidget.__init__(self) # Check that the minimum/maximum arguments are valid @@ -33,7 +60,7 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.minimum = round(minimum, self.decimals) self.maximum = round(maximum, self.decimals) - self.median = round(((self.maximum - self.minimum) / 2), self.decimals) + self.minimum + self.median = round((self.maximum - self.minimum) / 2 + self.minimum, self.decimals) self.slider_minimum = 0 self.slider_maximum = self.number_of_steps @@ -95,7 +122,7 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.setLayout(self.widget_layout) self.show() - def getValue(self): + def value(self): '''Gets the value of the UISliderWidget, which is the same as the QLineEdit value. This method is called by methods in the UIFormWidget class responsible for saving widget states, maintaining the naming convention used by other QWidgets. @@ -184,17 +211,20 @@ def _updateSlider(self): line_edit_value = self._getLineEditValue() state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: - self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) + scaled_value = self._scaleLineEditToSlider(line_edit_value) + self.slider.setValue(scaled_value) self.setValue(line_edit_value) elif line_edit_value > self.maximum: self.line_edit.setText(str(self.maximum)) line_edit_value = self._getLineEditValue() - self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) + scaled_value = self._scaleLineEditToSlider(line_edit_value) + self.slider.setValue(scaled_value) self.setValue(line_edit_value) else: self.line_edit.setText(str(self.minimum)) line_edit_value = self._getLineEditValue() - self.slider.setValue(self._scaleLineEditToSlider(line_edit_value)) + scaled_value = self._scaleLineEditToSlider(line_edit_value) + self.slider.setValue(scaled_value) self.setValue(line_edit_value) def _updateLineEdit(self): diff --git a/examples/MainWindowWithSessionManagement_example.py b/examples/MainWindowWithSessionManagement_example.py index 91d138d..dc0f3f8 100644 --- a/examples/MainWindowWithSessionManagement_example.py +++ b/examples/MainWindowWithSessionManagement_example.py @@ -4,9 +4,9 @@ from qtpy.QtWidgets import QApplication from eqt import __version__ +from eqt.ui import UISliderWidget from eqt.ui.MainWindowWithSessionManagement import MainWindowWithSessionManagement from eqt.ui.UIFormWidget import FormWidget -from eqt.ui.UISliderWidget import UISliderWidget class ExampleMainWindowWithSessionManagement(MainWindowWithSessionManagement): @@ -74,7 +74,8 @@ def add_every_widget_to_form(form): form.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - form.addWidget(UISliderWidget(QtWidgets.QLabel()), 'UISliderWidget: ', 'uiSliderWidget') + form.addWidget(UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5), 'UISliderWidget: ', + 'uiSliderWidget') form.addWidget(QtWidgets.QRadioButton('test'), 'RadioButton: ', 'radioButton') form.addWidget(QtWidgets.QTextEdit('test'), 'TextEdit: ', 'textEdit') form.addWidget(QtWidgets.QPlainTextEdit('test'), 'PlainTextEdit: ', 'plainTextEdit') diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index bafcf68..70c0613 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -41,7 +41,7 @@ def openFormDialog(self): def accepted(self): print("accepted") print(f"UISlider QSlider: {self.dialog.widgets['input_slider_field']._getSliderValue()}") - print(f"UISlider QLineEdit: {self.dialog.widgets['input_slider_field'].getValue()}") + print(f"UISlider QLineEdit: {self.dialog.widgets['input_slider_field'].value()}") self.dialog.close() diff --git a/examples/dialog_save_state_example.py b/examples/dialog_save_state_example.py index ee475be..3eb8ed4 100644 --- a/examples/dialog_save_state_example.py +++ b/examples/dialog_save_state_example.py @@ -2,8 +2,7 @@ from qtpy import QtWidgets -from eqt.ui import FormDialog -from eqt.ui.UISliderWidget import UISliderWidget +from eqt.ui import FormDialog, UISliderWidget class MainUI(QtWidgets.QMainWindow): @@ -35,8 +34,10 @@ def __init__(self, parent=None): dialog.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') dialog.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') dialog.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - dialog.addWidget(UISliderWidget(minimum=0.0, maximum=100.0), 'UISliderWidget: ', - 'uiSliderWidget') + dialog.addWidget( + UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, + number_of_steps=10, number_of_ticks=10), + 'UISliderWidget: ', 'uiSliderWidget') dialog.addWidget(QtWidgets.QRadioButton('test 1'), 'RadioButton 1: ', 'radioButton1') dialog.addWidget(QtWidgets.QRadioButton('test 2'), 'RadioButton 2: ', 'radioButton2') diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py index 8c6efa6..0601de7 100644 --- a/examples/utilitiesForExamples.py +++ b/examples/utilitiesForExamples.py @@ -1,6 +1,6 @@ from qtpy import QtWidgets -from eqt.ui.UISliderWidget import UISliderWidget +from eqt.ui import UISliderWidget def list_all_widgets(): @@ -8,7 +8,7 @@ def list_all_widgets(): 'label': QtWidgets.QLabel('test label'), 'checkBox': QtWidgets.QCheckBox('test checkbox'), 'comboBox': QtWidgets.QComboBox(), 'doubleSpinBox': QtWidgets.QDoubleSpinBox(), 'spinBox': QtWidgets.QSpinBox(), 'slider': QtWidgets.QSlider(), - 'uiSliderWidget': UISliderWidget(), + 'uiSliderWidget': UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5), 'radioButton': QtWidgets.QRadioButton('test radio button'), 'textEdit': QtWidgets.QTextEdit('test text edit'), 'plainTextEdit': QtWidgets.QPlainTextEdit('test plain text edit'), @@ -33,7 +33,7 @@ def addWidgetsToExample(form): form.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox') form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - form.addWidget(UISliderWidget(minimum=0.0, maximum=100.0), 'UISliderWidget: ', + form.addWidget(UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5), 'UISliderWidget: ', 'uiSliderWidget') form.addWidget(QtWidgets.QRadioButton('select me'), 'RadioButton: ', 'radioButton') form.addWidget(QtWidgets.QTextEdit('write text here'), 'TextEdit: ', 'textEdit') diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 2c72839..c30a021 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -45,7 +45,7 @@ def test_init_default_properties(self): self.assertEqual(widget.number_of_ticks, 10) @parameterized.expand([("standard", 5.0), ("positive", 5.5), ("negative", -5.5), - ("float", 0.0), ("long", 5.5600000000000005)]) + ("float", 0.0), ("long", 5.56)]) def test_init_median(self, _, median): '''Tests the calculation of the 'median' property. ''' @@ -198,7 +198,7 @@ def test_get_and_set_value(self, _, widget_value, expected): widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) widget.setValue(widget_value) - self.assertEqual(widget.getValue(), expected) + self.assertEqual(widget.value(), expected) def test_get_slider_value(self): '''Tests getting the QSlider value. @@ -237,7 +237,7 @@ def test_acceptable_update_slider(self, _, line_edit_value, expected_slider_valu widget.line_edit.setText(line_edit_value) widget._updateSlider() self.assertEqual(widget._getSliderValue(), expected_slider_value) - self.assertEqual(widget.getValue(), expected_line_edit_value) + self.assertEqual(widget.value(), expected_line_edit_value) @parameterized.expand([("empty", "", 0), ("lt_min", "-11.0", 0), ("gt_max", "11.0", 2000)]) def test_invalid_update_slider(self, _, line_edit_value, expected_slider_value): @@ -252,9 +252,9 @@ def test_invalid_update_slider(self, _, line_edit_value, expected_slider_value): widget._updateSlider() self.assertEqual(widget._getSliderValue(), expected_slider_value) if _ == "gt_max": - self.assertEqual(widget.getValue(), widget.maximum) + self.assertEqual(widget.value(), widget.maximum) elif _ == "empty" or "lt_min": - self.assertEqual(widget.getValue(), widget.minimum) + self.assertEqual(widget.value(), widget.minimum) @parameterized.expand([("positive", 888, 5.0), ("negative", 1111, -5.0), ("float", 1500, 0.25), ("long", 1000, 5.56)]) From f7b5d0595226009b2ea07ebbc5f205f18b2b9c57 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 23 Jan 2025 17:48:55 +0000 Subject: [PATCH 43/68] Update UISliderWidget docstrings, methods and tests --- eqt/ui/UISliderWidget.py | 129 ++++++++++++----------------- examples/insert_widgets_example.py | 1 + examples/remove_widgets_example.py | 1 + recipe/eqt_env.yml | 1 - test/test_UISliderWidget.py | 20 ++--- 5 files changed, 64 insertions(+), 88 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index f3cbd1a..0554393 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -7,40 +7,28 @@ class UISliderWidget(QWidget): and median values. This class creates a QGridLayout that includes a QSlider, min/median/max QLabels and - a QLineEdit. When the QSlider value is changed, the widget converts/scales the value - and updates the QLineEdit with the new value, and vice versa. + a QLineEdit. The QSlider value changes with the QLineEdit value and vice versa. + + The value() and setValue() methods provide an interface for external widgets to + get and set the value of the widget. Some private methods exist (e.g. _setDecimals()) + that are responsible for validating and setting arguments. ''' def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): '''Creates the QGridLayout and the widgets that populate it (QSlider, QLineEdit, - QLabels). - - Also sets a number of attributes that control the appearance and behaviour of - the QSlider and QLineEdit. - - 'minimum' and 'maximum' are required arguments when creating a UISliderWidget object. - An if statement checks whether the arguments are valid before they are set. - - The QGridLayout is responsible for the arrangement of the widgets within the - UISliderWidget. The QSlider and QLineEdit are configured to span the entire width of - the window. The QLabels for the min/max/median appear underneath the QSlider, arranged - to the left, center and right respectively. + QLabels). Also sets some attributes. - Signals from the QSlider and QLineEdit are connected to the _updateSlider() and - _updateLineEdit() methods, linking user interactions with these widgets together. - The update methods call the scaling methods (_scaleLineEditToSlider(), - _scaleSliderToLineEdit()) so that changes to one widget are accurately represented - in the other widget. + The arrangement of the widgets is configured. - The value() and setValue() methods provide an interface for forms and dialogs to - get and set the value of the widget. Some private methods exist (e.g. _setDecimals()) - that are responsible for validating and setting arguments. + Signals from the QSlider and QLineEdit are connected, linking user interactions + with these widgets. The update methods call the scaling methods so that value + changes are accurately represented in the other. Parameters ---------- minimum : float - - Minimum value of the QLineEdit + - Minimum value of the QLineEdit, must be less than the maximum. maximum : float - - Maximum value of the QLineEdit + - Maximum value of the QLineEdit, must be greater than the minimum. decimals : int - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display number_of_steps : int @@ -50,7 +38,6 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of ''' QWidget.__init__(self) - # Check that the minimum/maximum arguments are valid if minimum >= maximum: raise ValueError("'minimum' argument must be less than 'maximum'") @@ -65,11 +52,13 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.slider_minimum = 0 self.slider_maximum = self.number_of_steps + self.scale_factor = (self.slider_maximum - self.slider_minimum) / (self.maximum - + self.minimum) + self.step_size = float((self.maximum - self.minimum) / self.number_of_steps) self.tick_interval = round( (self.slider_maximum - self.slider_minimum) / self.number_of_ticks) - # Configure the QSlider self.slider = QSlider() self.slider.setRange(self.slider_minimum, self.slider_maximum) self.slider.setOrientation(QtCore.Qt.Horizontal) @@ -77,10 +66,8 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.slider.setTickPosition(QSlider.TicksBelow) self.slider.setTickInterval(self.tick_interval) - # Connect the QSlider to the QLineEdit self.slider.valueChanged.connect(self._updateLineEdit) - # Configure the QDoubleValidator and QLineEdit self.validator = QtGui.QDoubleValidator() self.validator.setBottom(self.minimum) self.validator.setTop(self.maximum) @@ -92,17 +79,12 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.line_edit.setText(str(self.minimum)) self.line_edit.setPlaceholderText(str(self.minimum)) - # Connect the QLineEdit to the QSlider self.line_edit.editingFinished.connect(self._updateSlider) self.line_edit.returnPressed.connect(self._updateSlider) - # Configure the QApplication self.app = QApplication.instance() - - # Connect the QApplication to the QLineEdit self.app.focusChanged.connect(self._updateSlider) - # Configure QLabels self.min_label = QLabel() self.min_label.setText(str(self.minimum)) self.median_label = QLabel() @@ -110,7 +92,6 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.max_label = QLabel() self.max_label.setText(str(self.maximum)) - # Configure the QGridLayout self.widget_layout = QGridLayout() self.widget_layout.addWidget(self.slider, 0, 0, 1, -1) self.widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) @@ -118,33 +99,28 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) self.widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) - # Set the layout self.setLayout(self.widget_layout) - self.show() def value(self): - '''Gets the value of the UISliderWidget, which is the same as the QLineEdit value. - This method is called by methods in the UIFormWidget class responsible for - saving widget states, maintaining the naming convention used by other QWidgets. + '''Defines the value of the UISliderWidget using the current float value of the QLineEdit. + This method exists to remain consistent with other QWidgets. ''' - return self._getLineEditValue() + return float(self._getLineEditValue()) def setValue(self, value): - '''Sets the value of the UISliderWidget, which is the same as the QLineEdit value. - This method is called by methods in the UIFormWidget class responsible for - applying/loading widget states, maintaining the naming convention used by other QWidgets. + '''Sets the value of the UISliderWidget using the current float value of the QLineEdit. + This method exists to remain consistent with other QWidgets. Parameters ---------- value : float ''' self.line_edit.setText(str(value)) - self.slider.setValue(self._scaleLineEditToSlider(self._getLineEditValue())) def _setDecimals(self, decimals): '''Sets the number of decimal places that the QLabels, QLineEdit and - QSlider steps can display. Also checks that the argument provided is valid, - i.e. that it is a positive integer value - if the value is invalid, the method raises + QSlider steps can display. Checks that the argument provided is valid, + i.e. that it is a positive integer value - an invalid value raises a ValueError during object instantiation. Parameters @@ -152,14 +128,16 @@ def _setDecimals(self, decimals): decimals : int ''' if decimals < 0: - raise ValueError("'decimals' value must be a positive integer") + raise ValueError("'decimals' value must be greater than 0") + elif isinstance(decimals, int) is not True: + raise TypeError("'decimals' value type must be int") else: - self.decimals = int(decimals) + self.decimals = decimals def _setNumberOfSteps(self, number_of_steps): '''Sets the number of steps in the QSlider. Steps are each subdivision of the QSlider's range. Also checks that the argument provided is valid, i.e. that - it is a positive integer value - if the value is invalid, the method raises + it is a positive integer value - an invalid value raises a ValueError during object instantiation. Parameters @@ -167,24 +145,28 @@ def _setNumberOfSteps(self, number_of_steps): number_of_steps : int ''' if number_of_steps < 0: - raise ValueError("'number_of_steps' value must be a positive integer") + raise ValueError("'number_of_steps' value must be greater than 0") + elif isinstance(number_of_steps, int) is not True: + raise TypeError("'number_of_steps' value type must be int") else: - self.number_of_steps = int(number_of_steps) + self.number_of_steps = number_of_steps def _setNumberOfTicks(self, number_of_ticks): '''Sets the number of ticks that the QSlider displays. Ticks are the notches displayed underneath the QSlider. Also checks that the argument provided is - valid, i.e. that it is a positive integer value - if the value is invalid, - the method raises a ValueError during object instantiation. + valid, i.e. that it is a positive integer value - an invalid value raises + a ValueError during object instantiation. Parameters ---------- number_of_ticks : int ''' if number_of_ticks < 0: - raise ValueError("'number_of_ticks' value must be a positive integer") + raise ValueError("'number_of_ticks' value must be greater than 0") + elif isinstance(number_of_ticks, int) is not True: + raise TypeError("'number_of_ticks' value type must be int") else: - self.number_of_ticks = int(number_of_ticks) + self.number_of_ticks = number_of_ticks def _getSliderValue(self): '''Gets the current value of the QSlider, returning either 0 or a positive integer. @@ -192,14 +174,10 @@ def _getSliderValue(self): return self.slider.value() def _getLineEditValue(self): - '''Gets the current value of the QLineEdit. If the QLineEdit is empty, - this method returns the UISliderWidget's minimum value - otherwise, it - returns a float value between the UISliderWidget's minimum and maximum values. + '''Gets the current value of the QLineEdit. Returns a string value between the + UISliderWidget's minimum and maximum values. ''' - if self.line_edit.text() == '': - return self.minimum - else: - return float(self.line_edit.text()) + return self.line_edit.text() def _updateSlider(self): '''Updates the QSlider to reflect the current value of the QLineEdit. @@ -208,7 +186,10 @@ def _updateSlider(self): scaled value of the QLineEdit. Otherwise, it will update the QSlider with either the scaled value of the QLineEdit's minimum or maximum. ''' - line_edit_value = self._getLineEditValue() + if self._getLineEditValue() == '': + self.line_edit.setText(str(self.minimum)) + + line_edit_value = float(self._getLineEditValue()) state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: scaled_value = self._scaleLineEditToSlider(line_edit_value) @@ -216,16 +197,12 @@ def _updateSlider(self): self.setValue(line_edit_value) elif line_edit_value > self.maximum: self.line_edit.setText(str(self.maximum)) - line_edit_value = self._getLineEditValue() - scaled_value = self._scaleLineEditToSlider(line_edit_value) - self.slider.setValue(scaled_value) - self.setValue(line_edit_value) + self.slider.setValue(self.slider_maximum) + self.setValue(self.maximum) else: self.line_edit.setText(str(self.minimum)) - line_edit_value = self._getLineEditValue() - scaled_value = self._scaleLineEditToSlider(line_edit_value) - self.slider.setValue(scaled_value) - self.setValue(line_edit_value) + self.slider.setValue(self.slider_minimum) + self.setValue(self.minimum) def _updateLineEdit(self): '''Updates the QLineEdit to reflect the current value of the QSlider. @@ -236,7 +213,7 @@ def _updateLineEdit(self): def _scaleLineEditToSlider(self, value): '''Converts a QLineEdit value to a scaled QSlider value. The method calculates - the appropriate scale factor for the conversion using the minimum and maximum + the scale factor for the conversion using the minimum and maximum values of the QSlider and QLineEdit. Returns the scaled value. @@ -244,13 +221,12 @@ def _scaleLineEditToSlider(self, value): ---------- value : float ''' - scale_factor = (self.slider_maximum - self.slider_minimum) / (self.maximum - self.minimum) - value = self.slider_minimum + (scale_factor * (value - self.minimum)) + value = self.slider_minimum + (self.scale_factor * (value - self.minimum)) return int(value) def _scaleSliderToLineEdit(self, value): '''Converts a QSlider value to a scaled QLineEdit value. The method calculates - the appropriate scale factor for the conversion using the minimum and maximum + the scale factor for the conversion using the minimum and maximum values of the QSlider and QLineEdit. Returns the scaled value, rounded as per the decimals property. @@ -258,6 +234,5 @@ def _scaleSliderToLineEdit(self, value): ---------- value : integer ''' - scale_factor = (self.maximum - self.minimum) / (self.slider_maximum - self.slider_minimum) - value = self.minimum + (scale_factor * (value - self.slider_minimum)) + value = self.minimum + (1 / self.scale_factor * (value - self.slider_minimum)) return round(float(value), self.decimals) diff --git a/examples/insert_widgets_example.py b/examples/insert_widgets_example.py index c5ddda1..43c5716 100644 --- a/examples/insert_widgets_example.py +++ b/examples/insert_widgets_example.py @@ -66,6 +66,7 @@ def insert_form(self, form, button): qlabel.setText("Widget inserted in row 0: ") qwidget = QtWidgets.QLineEdit(form) form.insertWidget(0, 'inserted widget', qwidget, qlabel) + # Test whether UISliderWidget can be inserted buttonspanning = QtWidgets.QPushButton(self) buttonspanning.setText("Spanning widget inserted in row 2") form.insertWidget(2, 'inserted spanning widget', buttonspanning) diff --git a/examples/remove_widgets_example.py b/examples/remove_widgets_example.py index 8c4933f..a856a1d 100644 --- a/examples/remove_widgets_example.py +++ b/examples/remove_widgets_example.py @@ -123,6 +123,7 @@ def rejected(self): print("\nDialog closed.") def remove(self, form, button, userselection=False): + # Test removing UISliderWidget if userselection is False: userselection = form.getWidget('userinput').currentText() form.getWidget('userinput').removeItem(form.getWidget('userinput').currentIndex()) diff --git a/recipe/eqt_env.yml b/recipe/eqt_env.yml index 28bac83..f4d27f3 100644 --- a/recipe/eqt_env.yml +++ b/recipe/eqt_env.yml @@ -6,4 +6,3 @@ dependencies: - pip - qtpy - qdarkstyle - - parameterized diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index c30a021..8cdfa04 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -75,7 +75,7 @@ def test_input_invalid_min_max(self, _, minimum, maximum): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) @parameterized.expand([("positive", 10, 10), ("negative", -10, 0), ("float", 2.5, 2), - ("long", 9.99999, 9)]) + ("long", 9.99999, 9), ("value", "10", 10)]) def test_input_decimals(self, _, decimals, expected): '''Tests the correct widget behaviour when different 'decimals' arguments are supplied. @@ -89,8 +89,8 @@ def test_input_decimals(self, _, decimals, expected): widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, decimals=decimals) self.assertEqual(widget.decimals, expected) - except ValueError: - with self.assertRaises(ValueError): + except (ValueError, TypeError) as error: + with self.assertRaises(error): widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, decimals=decimals) @@ -109,8 +109,8 @@ def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expecte number_of_steps=number_of_steps) self.assertEqual(widget.number_of_steps, expected_steps) self.assertEqual(widget.step_size, expected_size) - except ValueError: - with self.assertRaises(ValueError): + except (ValueError, TypeError) as error: + with self.assertRaises(error): widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, number_of_steps=number_of_steps) @@ -131,8 +131,8 @@ def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expecte self.assertEqual(widget.number_of_ticks, expected_ticks) self.assertEqual(widget.tick_interval, expected_interval) self.assertEqual(widget.slider.tickInterval(), expected_interval) - except ValueError: - with self.assertRaises(ValueError): + except (ValueError, TypeError) as error: + with self.assertRaises(error): widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, number_of_ticks=number_of_ticks) @@ -189,7 +189,7 @@ def test_init_default_gridlayout(self): self.assertIsInstance(widget.widget_layout, QGridLayout) @parameterized.expand([("positive", 5.0, 5.0), ("negative", -5.0, -5.0), ("float", 0.25, 0.25), - ("long", 5.55555, 5.56)]) + ("long", 5.55555, 5.55555)]) def test_get_and_set_value(self, _, widget_value, expected): '''Tests the getting and setting of the widget's 'value' property. ''' @@ -211,8 +211,8 @@ def test_get_slider_value(self): widget.slider.setValue(50) self.assertEqual(widget._getSliderValue(), 50) - @parameterized.expand([("positive", "5.0", 5.0), ("negative", "-5.0", -5.0), - ("float", "0.25", 0.25), ("long", "5.55555", 5.55555)]) + @parameterized.expand([("positive", "5.0", "5.0"), ("negative", "-5.0", "-5.0"), + ("float", "0.25", "0.25"), ("long", "5.55555", "5.55555")]) def test_get_lineedit_value(self, _, line_edit_value, expected): '''Tests getting the QLineEdit value. ''' From 8bf7f3e685a75ab631f2ad93ce7643d616ccd84a Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 27 Jan 2025 15:56:27 +0000 Subject: [PATCH 44/68] Add additional UISliderWidget methods, update tests, docstrings and examples --- eqt/ui/UISliderWidget.py | 169 ++++++--- examples/dialog_example_uislider.py | 48 ++- test/test_UISliderWidget.py | 557 ++++++++++++++++++---------- 3 files changed, 506 insertions(+), 268 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 0554393..73fdd12 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -38,20 +38,15 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of ''' QWidget.__init__(self) - if minimum >= maximum: - raise ValueError("'minimum' argument must be less than 'maximum'") - self._setDecimals(decimals) + self._setMinimumMaximum(minimum, maximum) + self.median = round((self.maximum - self.minimum) / 2 + self.minimum, self.decimals) + self._setNumberOfSteps(number_of_steps) self._setNumberOfTicks(number_of_ticks) - self.minimum = round(minimum, self.decimals) - self.maximum = round(maximum, self.decimals) - self.median = round((self.maximum - self.minimum) / 2 + self.minimum, self.decimals) - self.slider_minimum = 0 self.slider_maximum = self.number_of_steps - self.scale_factor = (self.slider_maximum - self.slider_minimum) / (self.maximum - self.minimum) @@ -59,45 +54,12 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self.tick_interval = round( (self.slider_maximum - self.slider_minimum) / self.number_of_ticks) - self.slider = QSlider() - self.slider.setRange(self.slider_minimum, self.slider_maximum) - self.slider.setOrientation(QtCore.Qt.Horizontal) - self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) - self.slider.setTickPosition(QSlider.TicksBelow) - self.slider.setTickInterval(self.tick_interval) - - self.slider.valueChanged.connect(self._updateLineEdit) - - self.validator = QtGui.QDoubleValidator() - self.validator.setBottom(self.minimum) - self.validator.setTop(self.maximum) - self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) - self.validator.setLocale(QtCore.QLocale("en_US")) - - self.line_edit = QLineEdit() - self.line_edit.setValidator(self.validator) - self.line_edit.setText(str(self.minimum)) - self.line_edit.setPlaceholderText(str(self.minimum)) - - self.line_edit.editingFinished.connect(self._updateSlider) - self.line_edit.returnPressed.connect(self._updateSlider) - - self.app = QApplication.instance() - self.app.focusChanged.connect(self._updateSlider) - - self.min_label = QLabel() - self.min_label.setText(str(self.minimum)) - self.median_label = QLabel() - self.median_label.setText(str(self.median)) - self.max_label = QLabel() - self.max_label.setText(str(self.maximum)) - - self.widget_layout = QGridLayout() - self.widget_layout.addWidget(self.slider, 0, 0, 1, -1) - self.widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) - self.widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) - self.widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) - self.widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) + self._setUpQSlider() + self._setUpQValidator() + self._setUpQLineEdit() + self._connectFocusChangedSignal() + self._setUpQLabels() + self._setUpQGridLayout() self.setLayout(self.widget_layout) @@ -105,10 +67,11 @@ def value(self): '''Defines the value of the UISliderWidget using the current float value of the QLineEdit. This method exists to remain consistent with other QWidgets. ''' - return float(self._getLineEditValue()) + return float(self._getQLineEditValue()) def setValue(self, value): '''Sets the value of the UISliderWidget using the current float value of the QLineEdit. + Also scales the value to set the value of the QSlider. This method exists to remain consistent with other QWidgets. Parameters @@ -117,6 +80,15 @@ def setValue(self, value): ''' self.line_edit.setText(str(value)) + def _setMinimumMaximum(self, minimum, maximum): + '''Sets the widget's minimum and maximum attributes. Checks that the minimum + ''' + if minimum >= maximum: + raise ValueError("'minimum' argument must be less than 'maximum'") + else: + self.minimum = round(minimum, self.decimals) + self.maximum = round(maximum, self.decimals) + def _setDecimals(self, decimals): '''Sets the number of decimal places that the QLabels, QLineEdit and QSlider steps can display. Checks that the argument provided is valid, @@ -127,7 +99,7 @@ def _setDecimals(self, decimals): ---------- decimals : int ''' - if decimals < 0: + if decimals <= 0: raise ValueError("'decimals' value must be greater than 0") elif isinstance(decimals, int) is not True: raise TypeError("'decimals' value type must be int") @@ -144,7 +116,7 @@ def _setNumberOfSteps(self, number_of_steps): ---------- number_of_steps : int ''' - if number_of_steps < 0: + if number_of_steps <= 0: raise ValueError("'number_of_steps' value must be greater than 0") elif isinstance(number_of_steps, int) is not True: raise TypeError("'number_of_steps' value type must be int") @@ -161,35 +133,110 @@ def _setNumberOfTicks(self, number_of_ticks): ---------- number_of_ticks : int ''' - if number_of_ticks < 0: + if number_of_ticks <= 0: raise ValueError("'number_of_ticks' value must be greater than 0") elif isinstance(number_of_ticks, int) is not True: raise TypeError("'number_of_ticks' value type must be int") else: self.number_of_ticks = number_of_ticks - def _getSliderValue(self): + def _setUpQSlider(self): + '''Creates and configures the UISlider's QSlider widget. + A signal from the QSlider is connected to the method that + updates the QLineEdit widget. + ''' + self.slider = QSlider() + self.slider.setRange(self.slider_minimum, self.slider_maximum) + self.slider.setOrientation(QtCore.Qt.Horizontal) + self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) + self.slider.setTickPosition(QSlider.TicksBelow) + self.slider.setTickInterval(self.tick_interval) + + self.slider.valueChanged.connect(self._updateQLineEdit) + + def _setUpQValidator(self): + '''Creates and configures the UISlider's QValidator widget. + The QValidator validates user input from the QLineEdit. + The locale is set to "en_US" to enforce the correct + decimal format (i.e. '3.14', instead of '3,14'). + ''' + self.validator = QtGui.QDoubleValidator() + self.validator.setBottom(self.minimum) + self.validator.setTop(self.maximum) + self.validator.setNotation(QtGui.QDoubleValidator.StandardNotation) + self.validator.setLocale(QtCore.QLocale("en_US")) + + def _setUpQLineEdit(self): + '''Creates and configures the UISlider's QLineEdit widget. + Signals from the QLineEdit are connected to the method that + updates the QSlider widget. + ''' + self.line_edit = QLineEdit() + self.line_edit.setValidator(self.validator) + self.line_edit.setText(str(self.minimum)) + self.line_edit.setPlaceholderText(str(self.minimum)) + + self.line_edit.editingFinished.connect(self._updateQSlider) + self.line_edit.returnPressed.connect(self._updateQSlider) + + def _connectFocusChangedSignal(self): + '''References the existing QApplication instance to connect + its focusChanged signal to the method that updates the + QSlider. If the focus changes, i.e. the QLineEdit loses focus, + the QSlider will be updated. + ''' + self.app = QApplication.instance() + self.app.focusChanged.connect(self._updateQSlider) + + def _setUpQLabels(self): + '''Creates and configures the UISlider's QLabel widgets. + The QLabels display the minimum, median and maximum values underneath + the QSlider. + ''' + self.min_label = QLabel() + self.min_label.setText(str(self.minimum)) + self.median_label = QLabel() + self.median_label.setText(str(self.median)) + self.max_label = QLabel() + self.max_label.setText(str(self.maximum)) + + def _setUpQGridLayout(self): + '''Creates a QGridLayout. Also adds the UISlider's widgets to the QGridLayout. + The QSlider is added to the first row of the QGridLayout and spans the entire row. + The QLabels are added to the second row and are aligned to the left, right and center + of the QSlider. + The QLineEdit is added to the third row and also spans the entire row. + ''' + self.widget_layout = QGridLayout() + self.widget_layout.addWidget(self.slider, 0, 0, 1, -1) + self.widget_layout.addWidget(self.min_label, 1, 0, QtCore.Qt.AlignLeft) + self.widget_layout.addWidget(self.median_label, 1, 1, QtCore.Qt.AlignCenter) + self.widget_layout.addWidget(self.max_label, 1, 2, QtCore.Qt.AlignRight) + self.widget_layout.addWidget(self.line_edit, 2, 0, 1, -1) + + def _getQSliderValue(self): '''Gets the current value of the QSlider, returning either 0 or a positive integer. ''' return self.slider.value() - def _getLineEditValue(self): + def _getQLineEditValue(self): '''Gets the current value of the QLineEdit. Returns a string value between the UISliderWidget's minimum and maximum values. ''' return self.line_edit.text() - def _updateSlider(self): + def _updateQSlider(self): '''Updates the QSlider to reflect the current value of the QLineEdit. The method uses the state of the QValidator to check that the QLineEdit value is valid - if it is valid, it sets the value of the QSlider to the scaled value of the QLineEdit. Otherwise, it will update the QSlider with - either the scaled value of the QLineEdit's minimum or maximum. + either the scaled value of the QLineEdit's minimum or maximum. Values + outside the range will raise a ValueError. ''' - if self._getLineEditValue() == '': + if self._getQLineEditValue() == '': self.line_edit.setText(str(self.minimum)) - line_edit_value = float(self._getLineEditValue()) + line_edit_value = float(self._getQLineEditValue()) state = self.validator.validate(self.line_edit.text(), 0) if state[0] == QtGui.QDoubleValidator.Acceptable: scaled_value = self._scaleLineEditToSlider(line_edit_value) @@ -199,16 +246,18 @@ def _updateSlider(self): self.line_edit.setText(str(self.maximum)) self.slider.setValue(self.slider_maximum) self.setValue(self.maximum) - else: + raise ValueError("range exceeded: resetting to 'maximum'") + elif line_edit_value < self.minimum: self.line_edit.setText(str(self.minimum)) self.slider.setValue(self.slider_minimum) self.setValue(self.minimum) + raise ValueError("range exceeded: resetting to 'minimum'") - def _updateLineEdit(self): + def _updateQLineEdit(self): '''Updates the QLineEdit to reflect the current value of the QSlider. The method sets the value of the QLineEdit to the scaled value of the QSlider. ''' - slider_value = self._getSliderValue() + slider_value = self._getQSliderValue() self.line_edit.setText(str(self._scaleSliderToLineEdit(slider_value))) def _scaleLineEditToSlider(self, value): diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 70c0613..718bf61 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -7,11 +7,16 @@ class MainUI(QtWidgets.QMainWindow): def __init__(self, parent=None): + '''Creates a QMainWindow and adds a QPushButton. Pressing the button opens a FormDialog + containing a UISliderWidget example. The UISliderWidget is connected to a method + that prints the current value of it's QSlider, QLineEdit, and the value of the + UISliderWidget itself (i.e. the value returned when it's value() method is called). + ''' QtWidgets.QMainWindow.__init__(self, parent) pb = QtWidgets.QPushButton(self) pb.setText("Open Dialog with form layout") - pb.clicked.connect(lambda: self.openFormDialog()) + pb.clicked.connect(lambda: self._openFormDialog()) layout = QtWidgets.QHBoxLayout() layout.addWidget(pb) @@ -19,34 +24,37 @@ def __init__(self, parent=None): widg.setLayout(layout) self.setCentralWidget(widg) - self.show() - def openFormDialog(self): - dialog = FormDialog(parent=self, title='Example') - dialog.Ok.clicked.connect(lambda: self.accepted()) - - # Create UISliderWidget + def _openFormDialog(self): + '''Creates a FormDialog and adds a UISliderWidget. Connects signals from the widget's + QSlider and QLineEdit to a method than prints the UISliderWidget's values. The values + will be printed when either; the QSlider is released, or the QLineEdit is edited. + Displays the FormDialog. + ''' + dialog = FormDialog(parent=self, title='UISliderWidget Example') uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, number_of_steps=10, number_of_ticks=10) - - # add to the form widget dialog.addWidget(uislider, 'UISlider:', 'input_slider') - # store a reference + dialog.widgets['input_slider_field'].slider.sliderReleased.connect( + lambda: self._printValues()) + dialog.widgets['input_slider_field'].line_edit.editingFinished.connect( + lambda: self._printValues()) + self.dialog = dialog - self.dialog.onCancel = self.rejected dialog.exec() - def accepted(self): - print("accepted") - print(f"UISlider QSlider: {self.dialog.widgets['input_slider_field']._getSliderValue()}") - print(f"UISlider QLineEdit: {self.dialog.widgets['input_slider_field'].value()}") - - self.dialog.close() - - def rejected(self): - print("rejected") + def _printValues(self): + '''Prints the values of the QSlider, QLineEdit and the UISliderWidget itself. + Also prints the type of each value. + ''' + slider_value = self.dialog.widgets['input_slider_field']._getQSliderValue() + line_edit_value = self.dialog.widgets['input_slider_field']._getQLineEditValue() + uislider_value = self.dialog.widgets['input_slider_field'].value() + print(f"QSlider Value: {slider_value} {type(slider_value)}\n" + + f"QLineEdit Value: {line_edit_value} {type(line_edit_value)}\n" + + f"UISliderWidget Value: {uislider_value} {type(uislider_value)}\n") if __name__ == "__main__": diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 8cdfa04..37cd64c 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,8 +1,6 @@ -import unittest - -from parameterized import parameterized from qtpy import QtGui from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider +from unittest_parametrize import ParametrizedTestCase, param, parametrize from eqt.ui import UISliderWidget @@ -10,7 +8,7 @@ @skip_ci -class TestUISliderWidget(unittest.TestCase): +class TestUISliderWidget(ParametrizedTestCase): def setUp(self): self.test_widgets = { "standard": {"minimum": 0.0, @@ -19,277 +17,460 @@ def setUp(self): "maximum": -1.0}, "float": {"minimum": -0.5, "maximum": 0.5}, "long": {"minimum": 1.11111, "maximum": 9.99999}} - @parameterized.expand([("standard", 0.0, 10.0), ("positive", 1.0, 10.0), - ("negative", -10.0, -1.0), ("float", -0.5, 0.5), ("long", 1.11, 10.0)]) - def test_init_widget(self, _, expected_minimum, expected_maximum): - '''Tests widget instantiation using 'minimum'/'maximum' arguments. + @parametrize( + "expected_minimum,expected_maximum,test_widget", + [ + param(0.0, 10.0, "standard", id="standard"), + param(1.0, 10.0, "positive", id="positive"), + param(-10.0, -1.0, "negative", id="negative"), + param(-0.5, 0.5, "float", id="float"), + param(1.11, 10.0, "long", id="long"),], + ) + def test_init(self, expected_minimum, expected_maximum, test_widget): + '''Tests widget instantiation using required arguments. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - self.assertIsInstance(widget, UISliderWidget.UISliderWidget) - self.assertEqual(widget.minimum, expected_minimum) - self.assertEqual(widget.maximum, expected_maximum) + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertIsInstance(self.widget, UISliderWidget.UISliderWidget) + self.assertEqual(self.widget.minimum, expected_minimum) + self.assertEqual(self.widget.maximum, expected_maximum) + + @parametrize( + "minimum,maximum", + [ + param(10.0, 0.0, id="min_gt_max"), + param(0.0, 0.0, id="min_eq_max"),], + ) + def test_init_invalid_min_max(self, minimum, maximum): + '''Tests widget behaviour when invalid combinations of the required arguments are supplied. + ''' + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - def test_init_default_properties(self): - '''Tests the setting of the widget's default properties. + def test_init_default_attributes(self): + '''Tests the setting of the widget's default attributes. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - self.assertEqual(widget.decimals, 2) - self.assertEqual(widget.number_of_steps, 2000) - self.assertEqual(widget.number_of_ticks, 10) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("standard", 5.0), ("positive", 5.5), ("negative", -5.5), - ("float", 0.0), ("long", 5.56)]) - def test_init_median(self, _, median): - '''Tests the calculation of the 'median' property. + self.assertEqual(self.widget.decimals, 2) + self.assertEqual(self.widget.number_of_steps, 2000) + self.assertEqual(self.widget.number_of_ticks, 10) + + @parametrize( + "median,test_widget", + [ + param(5.0, "standard", id="standard"), + param(5.5, "positive", id="positive"), + param(-5.5, "negative", id="negative"), + param(0.0, "float", id="float"), + param(5.56, "long", id="long"),], + ) + def test_init_median(self, median, test_widget): + '''Tests the calculation of the median attribute. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - self.assertEqual(widget.median, median) - - def test_widget_state(self): - '''Tests default widget states. + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertEqual(self.widget.median, median) + + @parametrize( + "decimals,expected", + [ + param(1, 1, id="gt_zero_1"), + param(10, 10, id="gt_zero_10"),], + ) + def test_input_valid_decimals(self, decimals, expected): + '''Tests the widget behaviour when valid 'decimals' arguments are supplied. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - self.assertTrue(widget.isVisible()) - self.assertTrue(widget.isEnabled()) - - @parameterized.expand([("min_gt_max", 10.0, 0.0), ("min_eq_max", 0.0, 0.0)]) - def test_input_invalid_min_max(self, _, minimum, maximum): - '''Tests for correct widget behaviour when invalid combinations - of 'minimum'/'maximum' are supplied. + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + decimals=decimals) + self.assertEqual(self.widget.decimals, expected) + + @parametrize( + "decimals", + [ + param(-1, id="negative_1"), + param(-10, id="negative_10"), + param(0, id="eq_0"),], + ) + def test_input_invalid_value_decimals(self, decimals): + '''Tests the widget behaviour when invalid 'decimals' values are supplied. ''' - with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - @parameterized.expand([("positive", 10, 10), ("negative", -10, 0), ("float", 2.5, 2), - ("long", 9.99999, 9), ("value", "10", 10)]) - def test_input_decimals(self, _, decimals, expected): - '''Tests the correct widget behaviour when different 'decimals' - arguments are supplied. - If an invalid argument is supplied, a ValueError is raised. + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + decimals=decimals) + + @parametrize( + "decimals", + [ + param(2.5, id="float"), + param("10", id="string"), + param(None, id="none"),], + ) + def test_input_invalid_type_decimals(self, decimals): + '''Tests the widget behaviour when invalid 'decimals' types are supplied. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - - try: - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - decimals=decimals) - self.assertEqual(widget.decimals, expected) - except (ValueError, TypeError) as error: - with self.assertRaises(error): - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - decimals=decimals) - - @parameterized.expand([("positive", 1500, 1500, 0.006), ("negative", -50, 2000, 0.0005), - ("float", 2.5, 2, 0.5), ("long", 9.99999, 9, 0.9877777777777779)]) - def test_input_number_of_steps(self, _, number_of_steps, expected_steps, expected_size): - '''Tests the correct widget behaviour when different 'number_of_steps' - arguments are supplied. - If an invalid argument is supplied, a ValueError is raised. + with self.assertRaises(TypeError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + decimals=decimals) + + @parametrize( + "number_of_steps,expected_steps", + [ + param(1, 1, id="gt_zero_1"), + param(10, 10, id="gt_zero_10"),], + ) + def test_input_valid_number_of_steps(self, number_of_steps, expected_steps): + '''Tests the input of valid 'number_of_steps' arguments. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - - try: - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) - self.assertEqual(widget.number_of_steps, expected_steps) - self.assertEqual(widget.step_size, expected_size) - except (ValueError, TypeError) as error: - with self.assertRaises(error): - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) - - @parameterized.expand([("positive", 5, 5, 400), ("negative", -10, 10, 0.1), - ("float", 2.5, 2, 1000), ("long", 9.99999, 9, 222)]) - def test_input_number_of_ticks(self, _, number_of_ticks, expected_ticks, expected_interval): - '''Tests the correct widget behaviour when different 'number_of_ticks' - arguments are supplied. - If an invalid argument is supplied, a ValueError is raised. + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) + self.assertEqual(self.widget.number_of_steps, expected_steps) + + @parametrize( + "number_of_steps", + [ + param(-1, id="negative_1"), + param(-10, id="negative_10"), + param(0, id="eq_0"),], + ) + def test_input_invalid_value_steps(self, number_of_steps): + '''Tests the widget behaviour when invalid 'number_of_steps' values are supplied. + ''' + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) + + @parametrize( + "number_of_steps,expected_size,test_widget", + [ + param(10, 1.0, "standard", id="standard"), + param(10, 0.9, "positive", id="positive"), + param(10, 0.9, "negative", id="negative"), + param(10, 0.1, "float", id="float"), + param(10, 0.889, "long", id="long"),], + ) + def test_calculate_step_size(self, number_of_steps, expected_size, test_widget): + '''Tests the calculation of the 'step_size' attribute. + ''' + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) + self.assertEqual(self.widget.step_size, expected_size) + + @parametrize( + "number_of_steps", + [ + param(2.5, id="float"), + param("10", id="string"), + param(None, id="none"),], + ) + def test_input_invalid_type_steps(self, number_of_steps): + '''Tests the widget behaviour when invalid 'number_of_steps' types are supplied. + ''' + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + with self.assertRaises(TypeError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_steps=number_of_steps) + + @parametrize( + "number_of_ticks,expected_ticks,expected_interval", + [ + param(1, 1, 2000, id="gt_zero_1"), + param(10, 10, 200, id="gt_zero_10"),], + ) + def test_input_valid_ticks(self, number_of_ticks, expected_ticks, expected_interval): + '''Tests the widget behaviour when valid 'number_of_ticks' arguments are supplied. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_ticks=number_of_ticks) + self.assertEqual(self.widget.number_of_ticks, expected_ticks) + self.assertEqual(self.widget.tick_interval, expected_interval) + self.assertEqual(self.widget.slider.tickInterval(), expected_interval) + + @parametrize( + "number_of_ticks", + [ + param(-1, id="negative_1"), + param(-10, id="negative_10"), + param(0, id="eq_0"),], + ) + def test_input_invalid_value_ticks(self, number_of_ticks): + '''Tests the widget behaviour when invalid 'number_of_ticks' values are supplied. + ''' + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + with self.assertRaises(ValueError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_ticks=number_of_ticks) + + @parametrize( + "number_of_ticks", + [ + param(2.5, id="float"), + param("10", id="string"), + param(None, id="none"),], + ) + def test_input_invalid_type_ticks(self, number_of_ticks): + '''Tests the widget behaviour when invalid 'number_of_ticks' types are supplied. + If an invalid argument is supplied, a TypeError is raised. + ''' + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + with self.assertRaises(TypeError): + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + number_of_ticks=number_of_ticks) - try: - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_ticks=number_of_ticks) - self.assertEqual(widget.number_of_ticks, expected_ticks) - self.assertEqual(widget.tick_interval, expected_interval) - self.assertEqual(widget.slider.tickInterval(), expected_interval) - except (ValueError, TypeError) as error: - with self.assertRaises(error): - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_ticks=number_of_ticks) - - def test_init_default_slider(self): + def test_init_default_qslider(self): '''Tests the instantiation of the QSlider widget. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertIsInstance(widget.slider, QSlider) + self.assertIsInstance(self.widget.slider, QSlider) - def test_init_default_validator(self): + def test_init_default_qvalidator(self): '''Tests the instantiation of the QValidator widget. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertIsInstance(widget.validator, QtGui.QValidator) + self.assertIsInstance(self.widget.validator, QtGui.QValidator) - def test_init_default_lineedit(self): + def test_init_default_qlineedit(self): '''Tests the instantiation of the QLineEdit widget. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertIsInstance(widget.line_edit, QLineEdit) + self.assertIsInstance(self.widget.line_edit, QLineEdit) - def test_init_default_labels(self): + def test_init_default_qlabels(self): '''Tests the instantiation of the QLabel widgets. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertIsInstance(widget.min_label, QLabel) - self.assertIsInstance(widget.median_label, QLabel) - self.assertIsInstance(widget.max_label, QLabel) + self.assertIsInstance(self.widget.min_label, QLabel) + self.assertIsInstance(self.widget.median_label, QLabel) + self.assertIsInstance(self.widget.max_label, QLabel) - def test_init_default_gridlayout(self): + def test_init_default_qgridlayout(self): '''Tests the instantiation of the QGridLayout. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - self.assertIsInstance(widget.widget_layout, QGridLayout) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("positive", 5.0, 5.0), ("negative", -5.0, -5.0), ("float", 0.25, 0.25), - ("long", 5.55555, 5.55555)]) - def test_get_and_set_value(self, _, widget_value, expected): - '''Tests the getting and setting of the widget's 'value' property. + self.assertIsInstance(self.widget.widget_layout, QGridLayout) + + @parametrize( + "widget_value,expected_value,test_widget", + [ + param(5.0, 5.0, "positive", id="positive"), + param(-5.0, -5.0, "negative", id="negative"), + param(0.25, 0.25, "float", id="float"), + param(5.55555, 5.55555, "long", id="long"),], + ) + def test_get_and_set_uislider_value(self, widget_value, expected_value, test_widget): + '''Tests the getting and setting of the UISlider value. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - widget.setValue(widget_value) - self.assertEqual(widget.value(), expected) + self.widget.setValue(widget_value) + self.assertEqual(self.widget.value(), expected_value) - def test_get_slider_value(self): - '''Tests getting the QSlider value. + def test_get_and_set_qslider_value(self): + '''Tests the getting and setting of the QSlider value. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - widget.slider.setValue(50) - self.assertEqual(widget._getSliderValue(), 50) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("positive", "5.0", "5.0"), ("negative", "-5.0", "-5.0"), - ("float", "0.25", "0.25"), ("long", "5.55555", "5.55555")]) - def test_get_lineedit_value(self, _, line_edit_value, expected): - '''Tests getting the QLineEdit value. + self.widget.slider.setValue(50) + self.assertEqual(self.widget._getQSliderValue(), 50) + + @parametrize( + "line_edit_value,expected", + [ + param("5.0", "5.0", id="positive"), + param("-5.0", "-5.0", id="negative"), + param("0.25", "0.25", id="float"), + param("5.55555", "5.55555", id="long"),], + ) + def test_get_and_set_qlineedit_value(self, line_edit_value, expected): + '''Tests the getting and setting of the QLineEdit value. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - widget.line_edit.setText(line_edit_value) - self.assertEqual(widget._getLineEditValue(), expected) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - @parameterized.expand([("positive", "5.0", 888, 5.0), ("negative", "-5.0", 1111, -5.0), - ("float", "0.25", 1500, 0.25), ("long", "5.55555", 1000, 5.55555)]) - def test_acceptable_update_slider(self, _, line_edit_value, expected_slider_value, - expected_line_edit_value): + self.widget.line_edit.setText(line_edit_value) + self.assertEqual(self.widget._getQLineEditValue(), expected) + + @parametrize( + "line_edit_value,expected_slider_value,expected_line_edit_value,test_widget", + [ + param("5.0", 888, 5.0, "positive", id="positive"), + param("-5.0", 1111, -5.0, "negative", id="negative"), + param("0.25", 1500, 0.25, "float", id="float"), + param("5.55555", 1000, 5.55555, "long", id="long"),], + ) + def test_acceptable_update_qslider(self, line_edit_value, expected_slider_value, + expected_line_edit_value, test_widget): '''Tests updating the QSlider with a valid QLineEdit value. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - widget.line_edit.setText(line_edit_value) - widget._updateSlider() - self.assertEqual(widget._getSliderValue(), expected_slider_value) - self.assertEqual(widget.value(), expected_line_edit_value) + self.widget.line_edit.setText(line_edit_value) + self.widget._updateQSlider() + self.assertEqual(self.widget._getQSliderValue(), expected_slider_value) + self.assertEqual(self.widget.value(), expected_line_edit_value) - @parameterized.expand([("empty", "", 0), ("lt_min", "-11.0", 0), ("gt_max", "11.0", 2000)]) - def test_invalid_update_slider(self, _, line_edit_value, expected_slider_value): - '''Tests updating the QSlider with a valid QLineEdit value. + def test_empty_update_qslider(self): + '''Tests updating the QSlider with an empty string QLineEdit value. ''' for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - - widget.line_edit.setText(line_edit_value) - widget._updateSlider() - self.assertEqual(widget._getSliderValue(), expected_slider_value) - if _ == "gt_max": - self.assertEqual(widget.value(), widget.maximum) - elif _ == "empty" or "lt_min": - self.assertEqual(widget.value(), widget.minimum) - - @parameterized.expand([("positive", 888, 5.0), ("negative", 1111, -5.0), ("float", 1500, 0.25), - ("long", 1000, 5.56)]) - def test_update_lineedit(self, _, slider_value, expected_line_edit_value): - '''Tests updating the QLineEdit with a valid QSlider value. + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.widget.line_edit.setText("") + self.widget._updateQSlider() + + self.assertEqual(self.widget._getQSliderValue(), self.widget.slider_minimum) + self.assertEqual(self.widget.value(), self.widget.minimum) + + def test_lt_min_update_qslider(self): + '''Tests updating the QSlider with a QLineEdit value less than the minimum. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - widget.slider.setValue(slider_value) - widget._updateLineEdit() - self.assertEqual(widget._getLineEditValue(), expected_line_edit_value) + lt_min_value = str(minimum - 1) + self.widget.line_edit.setText(lt_min_value) - @parameterized.expand([("positive", 5.0, 888), ("negative", -5.0, 1111), ("float", 2.5, 6000), - ("long", 9.99999, 1999)]) - def test_scale_lineedit_to_slider(self, _, line_edit_value, expected): - '''Tests converting a valid QLineEdit value into a scaled QSlider value. + with self.assertRaises(ValueError): + self.widget._updateQSlider() + + self.assertEqual(self.widget._getQSliderValue(), self.widget.slider_minimum) + self.assertEqual(self.widget.value(), self.widget.minimum) + + def test_gt_max_update_qslider(self): + '''Tests updating the QSlider with a QLineEdit value greater than the maximum. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + for params in self.test_widgets.values(): + minimum = params.get("minimum") + maximum = params.get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + gt_min_value = str(maximum + 1) + self.widget.line_edit.setText(gt_min_value) + + with self.assertRaises(ValueError): + self.widget._updateQSlider() - self.assertEqual(widget._scaleLineEditToSlider(line_edit_value), expected) + self.assertEqual(self.widget._getQSliderValue(), self.widget.slider_maximum) + self.assertEqual(self.widget.value(), self.widget.maximum) - @parameterized.expand([("positive", 1000, 5.5), ("negative", 1000, -5.5), ("float", 1000, 0.0), - ("long", 1000, 5.56)]) - def test_scale_slider_to_lineedit(self, _, slider_value, expected): + @parametrize( + "slider_value,expected_line_edit_value,test_widget", + [ + param(888, "5.0", "positive", id="positive"), + param(1111, "-5.0", "negative", id="negative"), + param(1500, "0.25", "float", id="float"), + param(1000, "5.56", "long", id="long"),], + ) + def test_update_qlineedit(self, slider_value, expected_line_edit_value, test_widget): + '''Tests updating the QLineEdit with a valid QSlider value. + ''' + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.widget.slider.setValue(slider_value) + self.widget._updateQLineEdit() + self.assertEqual(self.widget._getQLineEditValue(), expected_line_edit_value) + + @parametrize( + "line_edit_value,expected,test_widget", + [ + param(5.0, 888, "positive", id="positive"), + param(-5.0, 1111, "negative", id="negative"), + param(2.5, 6000, "float", id="float"), + param(9.99999, 1999, "long", id="long"),], + ) + def test_scale_lineedit_to_slider(self, line_edit_value, expected, test_widget): + '''Tests converting a valid QLineEdit value into a scaled QSlider value. + ''' + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + + self.assertEqual(self.widget._scaleLineEditToSlider(line_edit_value), expected) + + @parametrize( + "slider_value,expected,test_widget", + [ + param(1000, 5.5, "positive", id="positive"), + param(1000, -5.5, "negative", id="negative"), + param(1000, 0.0, "float", id="float"), + param(1000, 5.56, "long", id="long"),], + ) + def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): '''Tests converting a valid QSlider value into a scaled QLineEdit value. ''' - minimum = self.test_widgets.get(_).get("minimum") - maximum = self.test_widgets.get(_).get("maximum") - widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + minimum = self.test_widgets.get(test_widget).get("minimum") + maximum = self.test_widgets.get(test_widget).get("maximum") + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) - self.assertEqual(widget._scaleSliderToLineEdit(slider_value), expected) + self.assertEqual(self.widget._scaleSliderToLineEdit(slider_value), expected) def tearDown(self): self.form = None From 1775e94bdc81a1d223f06417b22be0a53ae63d1e Mon Sep 17 00:00:00 2001 From: jcornall Date: Mon, 27 Jan 2025 23:42:58 +0000 Subject: [PATCH 45/68] Add UISlider to insert_widgets_examply.py, fix onCancel() behaviour --- examples/dialog_example_uislider.py | 8 +- examples/insert_widgets_example.py | 119 +++++++++++++++++----------- pyproject.toml | 2 +- 3 files changed, 79 insertions(+), 50 deletions(-) diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 718bf61..5a9b539 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -14,12 +14,12 @@ def __init__(self, parent=None): ''' QtWidgets.QMainWindow.__init__(self, parent) - pb = QtWidgets.QPushButton(self) - pb.setText("Open Dialog with form layout") - pb.clicked.connect(lambda: self._openFormDialog()) + form_dialog_button = QtWidgets.QPushButton(self) + form_dialog_button.setText("Open FormDialog") + form_dialog_button.clicked.connect(lambda: self._openFormDialog()) layout = QtWidgets.QHBoxLayout() - layout.addWidget(pb) + layout.addWidget(form_dialog_button) widg = QtWidgets.QWidget() widg.setLayout(layout) diff --git a/examples/insert_widgets_example.py b/examples/insert_widgets_example.py index 43c5716..fc9e5b9 100644 --- a/examples/insert_widgets_example.py +++ b/examples/insert_widgets_example.py @@ -2,39 +2,51 @@ from qtpy import QtWidgets -from eqt.ui import FormDialog, UIFormWidget +from eqt.ui import FormDialog, UIFormWidget, UISliderWidget class MainUI(QtWidgets.QMainWindow): def __init__(self, parent=None): + '''Creates a QMainWindow and adds a FormDockWidget, a FormDialog, and a QPushButton. + Opening the FormDockWidget will show the form in a separate window, and clicking + on the QPushButton will also display a form separately. + The form will initially show three widget labels and fields: + a QLineEdit, a spanning QLabel, a QComboBox, and a QPushButton. + Clicking the QPushButton will add additional widgets: + a QLineEdit, a spanning QPushButton, and a UISlider. + + Opening the FormDialog adds an additional QPushButton, which will insert a + widget in the FormDialog's vertical layout. + ''' QtWidgets.QMainWindow.__init__(self, parent) - # dialog form - self.dialog = FormDialog(parent=self, title='Form Dialog example insert widget') - self.addWidgetsToExampleForm(self.dialog) - buttoninsertvertical = QtWidgets.QPushButton() - buttoninsertvertical.setText("Insert widget in vertical layout") - self.dialog.addSpanningWidget(buttoninsertvertical, 'Button insert vertical') - buttoninsertvertical.clicked.connect(lambda: self.insert_vertical()) - - # create a FormDockWidget dock = UIFormWidget.FormDockWidget(parent=self) - dock.setWindowTitle('Dock Widget Example insert widget') + dock.setWindowTitle('Dock Widget Insert Widget Example') self.addWidgetsToExampleForm(dock) - # create button for Form Dialog - pb = QtWidgets.QPushButton(self) - pb.setText("Open Form Dialog") - pb.clicked.connect(self.openFormDialog) + form_dialog_button = QtWidgets.QPushButton(self) + form_dialog_button.setText("Open FormDialog") + form_dialog_button.clicked.connect(self.openFormDialog) + + self.dialog = FormDialog(parent=self, title='Form Dialog Insert Widget Example') + self.addWidgetsToExampleForm(self.dialog) + + insert_vertical_button = QtWidgets.QPushButton() + insert_vertical_button.setText("Insert widget in vertical layout") + self.dialog.addSpanningWidget(insert_vertical_button, 'qbutton insert vertical') + insert_vertical_button.clicked.connect(lambda: self.insert_vertical()) - # create window layout - layout = QtWidgets.QHBoxLayout() - layout.addWidget(pb) + layout = QtWidgets.QVBoxLayout() layout.addWidget(dock) + layout.addWidget(form_dialog_button) widg = QtWidgets.QWidget() widg.setLayout(layout) self.setCentralWidget(widg) + print('\nDictionary of widgets before insertion in the form layout:') + for widget in dock.getWidgets(): + print(widget, dock.getWidgets()[widget]) + self.dialog.onCancel = self.onCancel self.show() @@ -42,16 +54,19 @@ def openFormDialog(self): self.dialog.open() def addWidgetsToExampleForm(self, form): - form.addWidget(QtWidgets.QLineEdit(), "Initial widget row 0: ", 'Initial widget row 0') - form.addSpanningWidget(QtWidgets.QLabel("Initial widget row 1"), 'Initial widget row 1') - # add QComboBox + form.addWidget(QtWidgets.QLineEdit(), "Initial QLineEdit Row 0:", + 'initial qlineedit row 0') + form.addSpanningWidget(QtWidgets.QLabel("Initial Spanning QLabel Row 1"), + 'initial spanning qlabel row 1') + qwidget = QtWidgets.QComboBox(form) qwidget.addItem("0") qwidget.addItem("1") - form.addWidget(qwidget, "Initial widget row 2", 'Initial widget row 2') + form.addWidget(qwidget, "Initial QComboBox Row 2:", 'initial qcombobox row 2') + buttoninsert = QtWidgets.QPushButton() buttoninsert.setText("Insert widgets") - form.addSpanningWidget(buttoninsert, 'Button insert widgets') + form.addSpanningWidget(buttoninsert, 'qbutton insert widgets') buttoninsert.clicked.connect(lambda: self.insert_form(form, buttoninsert)) def insert_vertical(self): @@ -59,43 +74,57 @@ def insert_vertical(self): 1, QtWidgets.QPushButton("Inserted widget in vertical layout")) print( "\nThe dictionary of widgets does not change after insertion in the vertical layout.") - self.dialog.getWidget('Button insert vertical').setEnabled(False) + self.dialog.getWidget('qbutton insert vertical').setEnabled(False) def insert_form(self, form, button): qlabel = QtWidgets.QLabel(form) - qlabel.setText("Widget inserted in row 0: ") + qlabel.setText("Inserted Widget Row 0:") qwidget = QtWidgets.QLineEdit(form) - form.insertWidget(0, 'inserted widget', qwidget, qlabel) - # Test whether UISliderWidget can be inserted + form.insertWidget(0, 'inserted qlineedit row 0', qwidget, qlabel) + buttonspanning = QtWidgets.QPushButton(self) - buttonspanning.setText("Spanning widget inserted in row 2") - form.insertWidget(2, 'inserted spanning widget', buttonspanning) - print('\nDictionary of widgets after insertion in the form layout:\n' + - str(form.getWidgets())) + buttonspanning.setText("Inserted Spanning Widget Row 2") + form.insertWidget(2, 'inserted spanning qpushbutton row 2', buttonspanning) + + qlabel = QtWidgets.QLabel(form) + qlabel.setText("Inserted Widget Row 4:") + uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, + number_of_steps=10, number_of_ticks=10) + form.insertWidget(4, 'inserted uislider row 4', uislider, qlabel) + + print('\nDictionary of widgets after insertion in the form layout:') + for widget in form.getWidgets(): + print(widget, form.getWidgets()[widget]) + button.setEnabled(False) def onCancel(self): - if not hasattr(self.dialog.formWidget, 'widget_states'): - if self.dialog.getWidget('Button insert vertical').isEnabled() is False: + if bool(self.dialog.formWidget.widget_states) is False: + if self.dialog.getWidget('qbutton insert vertical').isEnabled() is False: self.dialog.removeWidgetFromVerticalLayout( self.dialog.getWidgetFromVerticalLayout(1)) - - if self.dialog.getWidget('Button insert widgets').isEnabled() is False: + if self.dialog.getWidget('qbutton insert widgets').isEnabled() is False: + self.dialog.formWidget._popWidget(self.dialog.formWidget.default_widget_states, + 'inserted qlineedit row 0') self.dialog.formWidget._popWidget(self.dialog.formWidget.default_widget_states, - 'inserted widget') + 'inserted spanning qpushbutton row 2') self.dialog.formWidget._popWidget(self.dialog.formWidget.default_widget_states, - 'inserted spanning widget') - self.dialog.formWidget.removeWidget('inserted widget') - self.dialog.formWidget.removeWidget('inserted spanning widget') + 'inserted uislider row 4') + self.dialog.formWidget.removeWidget('inserted qlineedit row 0') + self.dialog.formWidget.removeWidget('inserted spanning qpushbutton row 2') + self.dialog.formWidget.removeWidget('inserted uislider row 4') else: - if self.dialog.getWidget('Button insert vertical').isEnabled( - ) != self.dialog.getWidgetStates()['Button insert vertical_field']['enabled'] is True: + if self.dialog.getWidget( + 'qbutton insert vertical').isEnabled() != self.dialog.getSavedWidgetStates( + )['qbutton insert vertical_field']['enabled'] is True: self.dialog.removeWidgetFromVerticalLayout( self.dialog.getWidgetFromVerticalLayout(1)) - if self.dialog.getWidget('Button insert widgets').isEnabled( - ) != self.dialog.getWidgetStates()['Button insert widgets_field']['enabled'] is True: - self.dialog.formWidget.removeWidget('inserted widget') - self.dialog.formWidget.removeWidget('inserted spanning widget') + if self.dialog.getWidget( + 'qbutton insert widgets').isEnabled() != self.dialog.getSavedWidgetStates( + )['qbutton insert widgets_field']['enabled'] is True: + self.dialog.formWidget.removeWidget('inserted qlineedit row 0') + self.dialog.formWidget.removeWidget('inserted spanning qpushbutton row 2') + self.dialog.formWidget.removeWidget('inserted uislider row 4') if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index c2d35ee..d321073 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ classifiers = [ dependencies = ["qtpy", "qdarkstyle"] [project.optional-dependencies] -dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "parameterized"] +dev = ["pytest>=6", "pytest-cov", "pytest-timeout", "unittest_parametrize"] [tool.flake8] max_line_length = 99 From e9e4eab464a437cba62effb6a000965abc1650a2 Mon Sep 17 00:00:00 2001 From: jcornall Date: Tue, 28 Jan 2025 15:02:37 +0000 Subject: [PATCH 46/68] Update docstrings --- examples/insert_widgets_example.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/insert_widgets_example.py b/examples/insert_widgets_example.py index fc9e5b9..0af6800 100644 --- a/examples/insert_widgets_example.py +++ b/examples/insert_widgets_example.py @@ -15,7 +15,7 @@ def __init__(self, parent=None): Clicking the QPushButton will add additional widgets: a QLineEdit, a spanning QPushButton, and a UISlider. - Opening the FormDialog adds an additional QPushButton, which will insert a + Opening the FormDialog shows an additional QPushButton, which will insert a widget in the FormDialog's vertical layout. ''' QtWidgets.QMainWindow.__init__(self, parent) @@ -54,8 +54,14 @@ def openFormDialog(self): self.dialog.open() def addWidgetsToExampleForm(self, form): + '''Creates the 'initial' widgets, i.e. a QLineEdit, a spanning QLabel and + a QComboBox. Adds them to the FormDialog. + Also adds a QPushButton that, when clicked, inserts additional widgets into + the FormDialog. + ''' form.addWidget(QtWidgets.QLineEdit(), "Initial QLineEdit Row 0:", 'initial qlineedit row 0') + form.addSpanningWidget(QtWidgets.QLabel("Initial Spanning QLabel Row 1"), 'initial spanning qlabel row 1') @@ -70,6 +76,10 @@ def addWidgetsToExampleForm(self, form): buttoninsert.clicked.connect(lambda: self.insert_form(form, buttoninsert)) def insert_vertical(self): + '''Inserts a QPushButton into the vertical layout. The widget is not added to + the dictionary of FormDialog widgets. Also sets the 'enabled' value of the button + that calls this method to 'False'. + ''' self.dialog.insertWidgetToVerticalLayout( 1, QtWidgets.QPushButton("Inserted widget in vertical layout")) print( @@ -77,6 +87,10 @@ def insert_vertical(self): self.dialog.getWidget('qbutton insert vertical').setEnabled(False) def insert_form(self, form, button): + '''Inserts additional widgets into the specified rows of the FormDialog: a QLineEdit, + a spanning QPushButton, and a UISlider. Prints the dictionary containing all widgets + in the FormDialog layout. + ''' qlabel = QtWidgets.QLabel(form) qlabel.setText("Inserted Widget Row 0:") qwidget = QtWidgets.QLineEdit(form) @@ -99,6 +113,12 @@ def insert_form(self, form, button): button.setEnabled(False) def onCancel(self): + '''Defines the behaviour of the 'cancel' button. + If the button is pressed and the FormDialog widget states have not been saved, it checks + whether the 'insert widget' buttons have been pressed. If they have, the method removes + the inserted widgets from the FormDialog and the dictionary of FormDialog widgets. + Otherwise, it only removes the widgets from the FormDialog. + ''' if bool(self.dialog.formWidget.widget_states) is False: if self.dialog.getWidget('qbutton insert vertical').isEnabled() is False: self.dialog.removeWidgetFromVerticalLayout( From 88014e997da53e290c6233c9169029d9d0a86b26 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 11:26:49 +0000 Subject: [PATCH 47/68] Add UISliderWidget to remove_widgets_example.py, update docstrings --- eqt/ui/UISliderWidget.py | 1 + examples/insert_widgets_example.py | 46 +++--- examples/remove_widgets_example.py | 238 +++++++++++++++++------------ 3 files changed, 171 insertions(+), 114 deletions(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 73fdd12..0ec2ed0 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -174,6 +174,7 @@ def _setUpQLineEdit(self): self.line_edit = QLineEdit() self.line_edit.setValidator(self.validator) self.line_edit.setText(str(self.minimum)) + self.line_edit.setClearButtonEnabled(True) self.line_edit.setPlaceholderText(str(self.minimum)) self.line_edit.editingFinished.connect(self._updateQSlider) diff --git a/examples/insert_widgets_example.py b/examples/insert_widgets_example.py index 0af6800..5929f6f 100644 --- a/examples/insert_widgets_example.py +++ b/examples/insert_widgets_example.py @@ -10,13 +10,20 @@ def __init__(self, parent=None): '''Creates a QMainWindow and adds a FormDockWidget, a FormDialog, and a QPushButton. Opening the FormDockWidget will show the form in a separate window, and clicking on the QPushButton will also display a form separately. + The form will initially show three widget labels and fields: - a QLineEdit, a spanning QLabel, a QComboBox, and a QPushButton. + - a QLineEdit field + - a spanning QLabel + - a QComboBox field + - a QPushButton field + Clicking the QPushButton will add additional widgets: - a QLineEdit, a spanning QPushButton, and a UISlider. + - a QLineEdit + - a spanning QPushButton + - a UISlider - Opening the FormDialog shows an additional QPushButton, which will insert a - widget in the FormDialog's vertical layout. + Opening the FormDialog displays the form with an additional QPushButton, which + will insert a widget in the FormDialog's vertical layout. ''' QtWidgets.QMainWindow.__init__(self, parent) @@ -28,7 +35,7 @@ def __init__(self, parent=None): form_dialog_button.setText("Open FormDialog") form_dialog_button.clicked.connect(self.openFormDialog) - self.dialog = FormDialog(parent=self, title='Form Dialog Insert Widget Example') + self.dialog = FormDialog(parent=self, title='FormDialog Insert Widget Example') self.addWidgetsToExampleForm(self.dialog) insert_vertical_button = QtWidgets.QPushButton() @@ -39,9 +46,9 @@ def __init__(self, parent=None): layout = QtWidgets.QVBoxLayout() layout.addWidget(dock) layout.addWidget(form_dialog_button) - widg = QtWidgets.QWidget() - widg.setLayout(layout) - self.setCentralWidget(widg) + widget = QtWidgets.QWidget() + widget.setLayout(layout) + self.setCentralWidget(widget) print('\nDictionary of widgets before insertion in the form layout:') for widget in dock.getWidgets(): @@ -70,18 +77,19 @@ def addWidgetsToExampleForm(self, form): qwidget.addItem("1") form.addWidget(qwidget, "Initial QComboBox Row 2:", 'initial qcombobox row 2') - buttoninsert = QtWidgets.QPushButton() - buttoninsert.setText("Insert widgets") - form.addSpanningWidget(buttoninsert, 'qbutton insert widgets') - buttoninsert.clicked.connect(lambda: self.insert_form(form, buttoninsert)) + insert_button = QtWidgets.QPushButton() + insert_button.setText("Insert widgets") + form.addSpanningWidget(insert_button, 'qbutton insert widgets') + insert_button.clicked.connect(lambda: self.insert_form(form, insert_button)) def insert_vertical(self): '''Inserts a QPushButton into the vertical layout. The widget is not added to the dictionary of FormDialog widgets. Also sets the 'enabled' value of the button that calls this method to 'False'. ''' - self.dialog.insertWidgetToVerticalLayout( - 1, QtWidgets.QPushButton("Inserted widget in vertical layout")) + vertical_button = QtWidgets.QPushButton() + vertical_button.setText("Inserted widget in vertical layout") + self.dialog.insertWidgetToVerticalLayout(1, vertical_button) print( "\nThe dictionary of widgets does not change after insertion in the vertical layout.") self.dialog.getWidget('qbutton insert vertical').setEnabled(False) @@ -93,12 +101,12 @@ def insert_form(self, form, button): ''' qlabel = QtWidgets.QLabel(form) qlabel.setText("Inserted Widget Row 0:") - qwidget = QtWidgets.QLineEdit(form) - form.insertWidget(0, 'inserted qlineedit row 0', qwidget, qlabel) + qlineedit = QtWidgets.QLineEdit(form) + form.insertWidget(0, 'inserted qlineedit row 0', qlineedit, qlabel) - buttonspanning = QtWidgets.QPushButton(self) - buttonspanning.setText("Inserted Spanning Widget Row 2") - form.insertWidget(2, 'inserted spanning qpushbutton row 2', buttonspanning) + spanning_button = QtWidgets.QPushButton(self) + spanning_button.setText("Inserted Spanning Widget Row 2") + form.insertWidget(2, 'inserted spanning qpushbutton row 2', spanning_button) qlabel = QtWidgets.QLabel(form) qlabel.setText("Inserted Widget Row 4:") diff --git a/examples/remove_widgets_example.py b/examples/remove_widgets_example.py index a856a1d..3fb5116 100644 --- a/examples/remove_widgets_example.py +++ b/examples/remove_widgets_example.py @@ -2,142 +2,190 @@ from qtpy import QtWidgets -from eqt.ui import FormDialog, UIFormWidget +from eqt.ui import FormDialog, UIFormWidget, UISliderWidget class MainUI(QtWidgets.QMainWindow): def __init__(self, parent=None): + '''Creates a QMainWindow and adds a FormDockWidget, a FormDialog, and a QPushButton. + Opening the FormDockWidget will show the form in a separate window, and clicking + on the QPushButton will also display a form separately. + + The form will initially show six labels and fields: + - x3 QLineEdit fields + - a UISlider field + - a spanning QLabel + - a QComboBox field + + Underneath these fields are some QPushButtons that will remove the specified widgets + when clicked. Users have the option to select a specific widget using the QComboBox field + and clicking the 'Remove user selected widget' QPushButton. + + Opening the FormDialog displays the form with an additional QPushButton, which + will insert a widget in the FormDialog's vertical layout. + ''' QtWidgets.QMainWindow.__init__(self, parent) - # create a FormDockWidget - dock = UIFormWidget.FormDockWidget(parent=self, title='Example remove widget') + dock = UIFormWidget.FormDockWidget(parent=self) + dock.setWindowTitle('Dock Widget Remove Widget Example') self.addWidgetsToExampleForm(dock) - # add a button to dock to remove user selected widget - buttonuser = QtWidgets.QPushButton(dock) - buttonuser.setText("Remove user selected widget") - dock.addSpanningWidget(buttonuser, 'Button Remove User') - buttonuser.clicked.connect(lambda: self.remove(dock, buttonuser)) + user_button = QtWidgets.QPushButton(dock) + user_button.setText("Remove user selected widget") + dock.addSpanningWidget(user_button, 'qpushbutton remove user') + user_button.clicked.connect(lambda: self.remove(dock, user_button)) - # create button for Form Dialog - pb = QtWidgets.QPushButton(self) - pb.setText("Open Form Dialog") - pb.clicked.connect(lambda: self.openFormDialog()) + form_dialog_button = QtWidgets.QPushButton(self) + form_dialog_button.setText("Open FormDialog") + form_dialog_button.clicked.connect(lambda: self.openFormDialog()) - # create window layout - layout = QtWidgets.QHBoxLayout() - layout.addWidget(pb) + layout = QtWidgets.QVBoxLayout() layout.addWidget(dock) - widg = QtWidgets.QWidget() - widg.setLayout(layout) - self.setCentralWidget(widg) + layout.addWidget(form_dialog_button) + widget = QtWidgets.QWidget() + widget.setLayout(layout) + self.setCentralWidget(widget) - # print dictionary of all widgets in dock - print("\nDictionary of widgets in the Form Dock Widget:\n" + str(dock.getWidgets())) + print('\nDictionary of widgets in the FormDockWidget:') + for widget in dock.getWidgets(): + print(widget, dock.getWidgets()[widget]) self.show() def openFormDialog(self): - dialog = FormDialog(parent=self, title='Example remove widget') - dialog.Ok.clicked.connect(lambda: self.remove(dialog, dialog.Ok)) - self.addWidgetsToExampleForm(dialog) - buttonremovevertical = QtWidgets.QPushButton() - buttonremovevertical.setText("Remove widget in vertical layout") - dialog.addSpanningWidget(buttonremovevertical, 'Button remove vertical') - vertical_button = QtWidgets.QPushButton("Widget in vertical layout") - buttonremovevertical.clicked.connect(lambda: self.remove_vertical(vertical_button)) - dialog.insertWidgetToVerticalLayout(1, vertical_button) - dialog.addSpanningWidget( - QtWidgets.QLabel( - "Press `Ok` to remove the user selected widget and `Cancel` to close the dialog:"), - 'ok_cancel_instructions') - - # store a reference - self.dialog = dialog + '''Creates a FormDialog and adds widgets to it. + Adds a widget in the vertical layout as well as a QPushButton to remove it. + Prints the dictionary containing all widgets in the FormDialog layout. + ''' + self.dialog = FormDialog(parent=self, title='FormDialog Remove Widget Example') + self.dialog.Ok.clicked.connect(lambda: self.remove(self.dialog, self.dialog.Ok)) + self.addWidgetsToExampleForm(self.dialog) + + remove_vertical_button = QtWidgets.QPushButton() + remove_vertical_button.setText("Remove widget in vertical layout") + self.dialog.addSpanningWidget(remove_vertical_button, 'qpushbutton remove vertical') + + vertical_button = QtWidgets.QPushButton() + vertical_button.setText("Widget in vertical layout") + self.dialog.insertWidgetToVerticalLayout(1, vertical_button) + + remove_vertical_button.clicked.connect(lambda: self.remove_vertical(vertical_button)) + + qlabel = QtWidgets.QLabel() + qlabel.setText( + "Press 'OK' to remove the user selected widget, 'Cancel' to close the FormDialog") + self.dialog.addSpanningWidget(qlabel, 'qlabel instructions') + self.dialog.onCancel = self.rejected - # redefine `onOk`` so it does not close the dialog. self.dialog._onOk = self._onOkRedefined - # print dictionary of all widgets in dialog - print("\nDictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets())) + print('\nDictionary of widgets in the FormDialog:') + for widget in self.dialog.getWidgets(): + print(widget, self.dialog.getWidgets()[widget]) - dialog.open() + self.dialog.saveAllWidgetStates() + self.dialog.open() def _onOkRedefined(self): - '''Saves the widget states.''' + '''Saves the widget states of all widgets in the FormDialog. + ''' self.dialog.saveAllWidgetStates() def addWidgetsToExampleForm(self, form): + '''Populates the FormDialog with widgets. + Three QLineEdit widgets, a UISlider widget, and a QComboBox widget are added. + The QComboBox widget allows users to select the widget they want to remove. + Two QPushButtons are also added to remove 'Widget 1' and the spanning widget specifically. + ''' + qlabel = QtWidgets.QLabel(form) + qlabel.setText("Widget 1:") + qlineedit = QtWidgets.QLineEdit(form) + qlineedit.setClearButtonEnabled(True) + form.addWidget(qlineedit, qlabel, 'widget 1') + + qlabel = QtWidgets.QLabel(form) + qlabel.setText("Widget 2:") + qlineedit = QtWidgets.QLineEdit(form) + qlineedit.setClearButtonEnabled(True) + form.addWidget(qlineedit, qlabel, 'widget 2') - # add widget 1 as QLineEdit qlabel = QtWidgets.QLabel(form) - qlabel.setText("Widget 1: ") - qwidget = QtWidgets.QLineEdit(form) - qwidget.setClearButtonEnabled(True) - form.addWidget(qwidget, qlabel, 'Widget 1') + qlabel.setText("Widget 3:") + qlineedit = QtWidgets.QLineEdit(form) + qlineedit.setClearButtonEnabled(True) + form.addWidget(qlineedit, qlabel, 'widget 3') - # add widget 2 as QLineEdit qlabel = QtWidgets.QLabel(form) - qlabel.setText("Widget 2: ") - qwidget = QtWidgets.QLineEdit(form) - qwidget.setClearButtonEnabled(True) - form.addWidget(qwidget, qlabel, 'Widget 2') + qlabel.setText("Widget 4:") + uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, + number_of_steps=10, number_of_ticks=10) + form.addWidget(uislider, qlabel, 'widget 4') - # add widget 3 as QLineEdit qlabel = QtWidgets.QLabel(form) - qlabel.setText("Widget 3: ") - qwidget = QtWidgets.QLineEdit(form) - qwidget.setClearButtonEnabled(True) - form.addWidget(qwidget, qlabel, 'Widget 3') - - # add input as QComboBox - form.addSpanningWidget(QtWidgets.QLabel("Pick the widget you want to remove:"), - 'input_title') + qlabel.setText("Select widget to remove:") + form.addSpanningWidget(qlabel, 'qlabel user input') + qlabel = QtWidgets.QLabel(form) qlabel.setText("User input: ") - qwidget = QtWidgets.QComboBox(form) - qwidget.addItem("Widget 2") - qwidget.addItem("Widget 3") - qwidget.setCurrentIndex(0) - qwidget.setEnabled(True) - form.addWidget(qwidget, qlabel, 'userinput') - - # add a button to remove widget 1 - button1 = QtWidgets.QPushButton(form) - button1.setText("Remove widget 1") - form.addSpanningWidget(button1, 'Button Remove w1') - button1.clicked.connect(lambda: self.remove(form, button1, 'Widget 1')) - - # add a button to remove spanning widget - buttonspanning = QtWidgets.QPushButton(form) - buttonspanning.setText("Remove spanning widget") - form.addSpanningWidget(buttonspanning, 'Button Remove Spanning') - buttonspanning.clicked.connect(lambda: self.remove(form, buttonspanning, 'input_title')) + qcombobox = QtWidgets.QComboBox(form) + qcombobox.addItem("widget 2") + qcombobox.addItem("widget 3") + qcombobox.addItem("widget 4") + qcombobox.setCurrentIndex(0) + qcombobox.setEnabled(True) + form.addWidget(qcombobox, qlabel, 'qcombobox user input') + + widget_1_button = QtWidgets.QPushButton(form) + widget_1_button.setText("Remove widget 1") + form.addSpanningWidget(widget_1_button, 'qpushbutton remove widget 1') + widget_1_button.clicked.connect(lambda: self.remove(form, widget_1_button, 'widget 1')) + + spanning_button = QtWidgets.QPushButton(form) + spanning_button.setText("Remove spanning widget") + form.addSpanningWidget(spanning_button, 'qpushbutton remove spanning') + spanning_button.clicked.connect( + lambda: self.remove(form, spanning_button, 'qlabel user input')) def remove_vertical(self, button): + '''Removes the widget from the FormDialog's vertical layout, then + sets the clicked QPushButton's 'enabled' value to 'False'. + ''' widget = self.dialog.removeWidgetFromVerticalLayout(button) - print(f'\nRemoved widget in the vertical layout is {widget}.') - self.dialog.getWidget('Button remove vertical').setEnabled(False) + print(f'\nRemoved widget in the vertical layout: {widget}') + self.dialog.getWidget('qpushbutton remove vertical').setEnabled(False) def rejected(self): - print("\nDialog closed.") - - def remove(self, form, button, userselection=False): - # Test removing UISliderWidget - if userselection is False: - userselection = form.getWidget('userinput').currentText() - form.getWidget('userinput').removeItem(form.getWidget('userinput').currentIndex()) - - widget = form.removeWidget(userselection) - print(f'\nRemove {userselection} returning {widget}.') - if form.getWidget('userinput').currentIndex() == -1: + print("\nFormDialog closed.") + + def remove(self, form, button, user_selection=False): + '''Removes a widget from the FormDialog. + If the user has not specified a widget to remove in the QComboBox, the current + selection will be removed. Otherwise, the widget specified will be deleted. + Prints the dictionary containing all widgets in the FormDialog layout after deletion. + ''' + if user_selection is False: + user_selection = form.getWidget('qcombobox user input').currentText() + form.getWidget('qcombobox user input').removeItem( + form.getWidget('qcombobox user input').currentIndex()) + + widget = form.getWidget(user_selection) + print(f'\nRemove {user_selection} returning {widget}.') + + if isinstance(form, FormDialog): + form.formWidget._popWidget(self.dialog.formWidget.widget_states, user_selection) + form.formWidget.removeWidget(user_selection) + elif isinstance(form, UIFormWidget.FormDockWidget): + form.removeWidget(user_selection) + + if form.getWidget('qcombobox user input').currentIndex() == -1: button.setEnabled(False) - if button == form.getWidget('Button Remove w1') or button == form.getWidget( - 'Button Remove Spanning'): + if button == form.getWidget('qpushbutton remove widget 1') or button == form.getWidget( + 'qpushbutton remove spanning'): button.setEnabled(False) - print("\nDictionary of widgets after deletion of " + userselection + ":\n" + - str(form.getWidgets())) + print(f'\nDictionary of widgets after deletion of {user_selection}:') + for widget in form.getWidgets(): + print(widget, form.getWidgets()[widget]) if __name__ == "__main__": From 1432a80c680736a234cd7344cc4311078cf5deb5 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 11:55:22 +0000 Subject: [PATCH 48/68] Modify setValue() to also update the value of the UISlider's QSlider --- eqt/ui/UISliderWidget.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 0ec2ed0..869e12e 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -71,13 +71,15 @@ def value(self): def setValue(self, value): '''Sets the value of the UISliderWidget using the current float value of the QLineEdit. - Also scales the value to set the value of the QSlider. + To avoid the updated QSlider overwriting the QLineEdit value, QSlider is + updated first with the scaled value. This method exists to remain consistent with other QWidgets. Parameters ---------- value : float ''' + self.slider.setValue(self._scaleLineEditToSlider(value)) self.line_edit.setText(str(value)) def _setMinimumMaximum(self, minimum, maximum): From bdee17a08e299e219dc7589fce4f7f0c6548c3bf Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 13:40:52 +0000 Subject: [PATCH 49/68] Remove skip_ci decorator from TestUISliderWidget, update recipe --- recipe/eqt_env.yml | 1 + test/test_UISliderWidget.py | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/recipe/eqt_env.yml b/recipe/eqt_env.yml index f4d27f3..40cd967 100644 --- a/recipe/eqt_env.yml +++ b/recipe/eqt_env.yml @@ -6,3 +6,4 @@ dependencies: - pip - qtpy - qdarkstyle + - unittest_parametrize diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 37cd64c..0b33705 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -4,10 +4,7 @@ from eqt.ui import UISliderWidget -from . import skip_ci - -@skip_ci class TestUISliderWidget(ParametrizedTestCase): def setUp(self): self.test_widgets = { From 4053cdef982457ca6d449a22c68d37040adc57b3 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 13:56:07 +0000 Subject: [PATCH 50/68] Modify tearDown() to quit the current QApplication instance after each test --- test/test_UISliderWidget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 0b33705..4e894e4 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,5 +1,5 @@ from qtpy import QtGui -from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider +from qtpy.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider from unittest_parametrize import ParametrizedTestCase, param, parametrize from eqt.ui import UISliderWidget @@ -471,3 +471,4 @@ def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): def tearDown(self): self.form = None + QApplication.instance().quit() From 546aa1bd8bd696bc5a9b5f11837835f44f83debf Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 14:18:01 +0000 Subject: [PATCH 51/68] Patch QApplication in TestUISliderWidget tests --- test/test_UISliderWidget.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 4e894e4..0cdc1ea 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,12 +1,15 @@ +from unittest import mock + from qtpy import QtGui -from qtpy.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider +from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider from unittest_parametrize import ParametrizedTestCase, param, parametrize from eqt.ui import UISliderWidget class TestUISliderWidget(ParametrizedTestCase): - def setUp(self): + @mock.patch("qtpy.QtWidgets.QApplication.instance") + def setUp(self, qapplication): self.test_widgets = { "standard": {"minimum": 0.0, "maximum": 10.0}, "positive": {"minimum": 1.0, "maximum": 10.0}, @@ -471,4 +474,3 @@ def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): def tearDown(self): self.form = None - QApplication.instance().quit() From fbd0c7cc7c3077181e1d5f8c8f9c88cf4fbe344f Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 14:24:34 +0000 Subject: [PATCH 52/68] Modify QApplication patch --- test/test_UISliderWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 0cdc1ea..222a433 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -8,7 +8,7 @@ class TestUISliderWidget(ParametrizedTestCase): - @mock.patch("qtpy.QtWidgets.QApplication.instance") + @mock.patch("qtpy.QtWidgets.QApplication") def setUp(self, qapplication): self.test_widgets = { "standard": {"minimum": 0.0, From 94fdb7f2a894a8dd30f1f8548a4f12e67a8c4329 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:02:09 +0000 Subject: [PATCH 53/68] Isolate QApplication dependency within UISliderWidget by adding is_application parameter --- CONTRIBUTING.md | 6 +- eqt/ui/UISliderWidget.py | 31 ++++++---- examples/dialog_example_uislider.py | 3 +- test/test_UISliderWidget.py | 91 ++++++++++++++++++----------- 4 files changed, 84 insertions(+), 47 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7ead0b..975b9c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,9 +48,13 @@ mamba activate eqt_env 5. Install the dependencies: ```sh -# Install test dependencies pip install .[dev] ``` +The following developer-specific dependencies will be installed: + - pytest + - pytest-cov + - pytest-timeout + - unittest_parametrize ### Merge the `main` Branch Conflicts may exist if your branch is behind the `main` branch. To resolve conflicts between branches, merge the `main` branch into your current working branch: diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 869e12e..2c320f5 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -13,7 +13,8 @@ class UISliderWidget(QWidget): get and set the value of the widget. Some private methods exist (e.g. _setDecimals()) that are responsible for validating and setting arguments. ''' - def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10): + def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of_ticks=10, + is_application=True): '''Creates the QGridLayout and the widgets that populate it (QSlider, QLineEdit, QLabels). Also sets some attributes. @@ -30,11 +31,13 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of maximum : float - Maximum value of the QLineEdit, must be greater than the minimum. decimals : int - - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display + - Number of decimal places that the QLabels, QLineEdit and QSlider steps can display. number_of_steps : int - - Number of steps in the QSlider + - Number of steps in the QSlider. number_of_ticks : int - - Number of ticks visualised under the QSlider, determines tick interval + - Number of ticks visualised under the QSlider, determines tick interval. + is_application : bool + - Whether the UISlider has a QApplication that it can reference. ''' QWidget.__init__(self) @@ -57,7 +60,7 @@ def __init__(self, minimum, maximum, decimals=2, number_of_steps=2000, number_of self._setUpQSlider() self._setUpQValidator() self._setUpQLineEdit() - self._connectFocusChangedSignal() + self._connectFocusChangedSignal(is_application) self._setUpQLabels() self._setUpQGridLayout() @@ -182,14 +185,18 @@ def _setUpQLineEdit(self): self.line_edit.editingFinished.connect(self._updateQSlider) self.line_edit.returnPressed.connect(self._updateQSlider) - def _connectFocusChangedSignal(self): - '''References the existing QApplication instance to connect - its focusChanged signal to the method that updates the - QSlider. If the focus changes, i.e. the QLineEdit loses focus, - the QSlider will be updated. + def _connectFocusChangedSignal(self, is_application): + '''If the 'is_application' attribute is True, connects the existing + QApplication instance's focusChanged signal to the method that updates + the QSlider. If the focus changes, i.e. the QLineEdit loses focus, + the inputted value is validated and the QSlider will be updated. + + If 'is_application' is False, the signal is not connected. The UISlider + will not automatically update when focus is changed while invalid + values have been entered. ''' - self.app = QApplication.instance() - self.app.focusChanged.connect(self._updateQSlider) + if is_application: + QApplication.instance().focusChanged.connect(self._updateQSlider) def _setUpQLabels(self): '''Creates and configures the UISlider's QLabel widgets. diff --git a/examples/dialog_example_uislider.py b/examples/dialog_example_uislider.py index 5a9b539..13e91cd 100644 --- a/examples/dialog_example_uislider.py +++ b/examples/dialog_example_uislider.py @@ -34,7 +34,8 @@ def _openFormDialog(self): ''' dialog = FormDialog(parent=self, title='UISliderWidget Example') uislider = UISliderWidget.UISliderWidget(minimum=-0.5, maximum=0.5, decimals=10, - number_of_steps=10, number_of_ticks=10) + number_of_steps=10, number_of_ticks=10, + is_application=True) dialog.addWidget(uislider, 'UISlider:', 'input_slider') dialog.widgets['input_slider_field'].slider.sliderReleased.connect( diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 222a433..5cb4e40 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,5 +1,3 @@ -from unittest import mock - from qtpy import QtGui from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider from unittest_parametrize import ParametrizedTestCase, param, parametrize @@ -8,8 +6,7 @@ class TestUISliderWidget(ParametrizedTestCase): - @mock.patch("qtpy.QtWidgets.QApplication") - def setUp(self, qapplication): + def setUp(self): self.test_widgets = { "standard": {"minimum": 0.0, "maximum": 10.0}, "positive": {"minimum": 1.0, "maximum": 10.0}, @@ -31,7 +28,8 @@ def test_init(self, expected_minimum, expected_maximum, test_widget): ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget, UISliderWidget.UISliderWidget) self.assertEqual(self.widget.minimum, expected_minimum) @@ -47,7 +45,8 @@ def test_init_invalid_min_max(self, minimum, maximum): '''Tests widget behaviour when invalid combinations of the required arguments are supplied. ''' with self.assertRaises(ValueError): - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) def test_init_default_attributes(self): '''Tests the setting of the widget's default attributes. @@ -55,7 +54,8 @@ def test_init_default_attributes(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertEqual(self.widget.decimals, 2) self.assertEqual(self.widget.number_of_steps, 2000) @@ -75,7 +75,8 @@ def test_init_median(self, median, test_widget): ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertEqual(self.widget.median, median) @@ -92,7 +93,7 @@ def test_input_valid_decimals(self, decimals, expected): minimum = params.get("minimum") maximum = params.get("maximum") self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - decimals=decimals) + decimals=decimals, is_application=False) self.assertEqual(self.widget.decimals, expected) @parametrize( @@ -110,7 +111,8 @@ def test_input_invalid_value_decimals(self, decimals): maximum = params.get("maximum") with self.assertRaises(ValueError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - decimals=decimals) + decimals=decimals, + is_application=False) @parametrize( "decimals", @@ -127,7 +129,8 @@ def test_input_invalid_type_decimals(self, decimals): maximum = params.get("maximum") with self.assertRaises(TypeError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - decimals=decimals) + decimals=decimals, + is_application=False) @parametrize( "number_of_steps,expected_steps", @@ -142,7 +145,8 @@ def test_input_valid_number_of_steps(self, number_of_steps, expected_steps): minimum = params.get("minimum") maximum = params.get("maximum") self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) + number_of_steps=number_of_steps, + is_application=False) self.assertEqual(self.widget.number_of_steps, expected_steps) @parametrize( @@ -160,7 +164,8 @@ def test_input_invalid_value_steps(self, number_of_steps): maximum = params.get("maximum") with self.assertRaises(ValueError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) + number_of_steps=number_of_steps, + is_application=False) @parametrize( "number_of_steps,expected_size,test_widget", @@ -177,7 +182,8 @@ def test_calculate_step_size(self, number_of_steps, expected_size, test_widget): minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) + number_of_steps=number_of_steps, + is_application=False) self.assertEqual(self.widget.step_size, expected_size) @parametrize( @@ -195,7 +201,8 @@ def test_input_invalid_type_steps(self, number_of_steps): maximum = params.get("maximum") with self.assertRaises(TypeError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_steps=number_of_steps) + number_of_steps=number_of_steps, + is_application=False) @parametrize( "number_of_ticks,expected_ticks,expected_interval", @@ -210,7 +217,8 @@ def test_input_valid_ticks(self, number_of_ticks, expected_ticks, expected_inter minimum = params.get("minimum") maximum = params.get("maximum") self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_ticks=number_of_ticks) + number_of_ticks=number_of_ticks, + is_application=False) self.assertEqual(self.widget.number_of_ticks, expected_ticks) self.assertEqual(self.widget.tick_interval, expected_interval) self.assertEqual(self.widget.slider.tickInterval(), expected_interval) @@ -230,7 +238,8 @@ def test_input_invalid_value_ticks(self, number_of_ticks): maximum = params.get("maximum") with self.assertRaises(ValueError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_ticks=number_of_ticks) + number_of_ticks=number_of_ticks, + is_application=False) @parametrize( "number_of_ticks", @@ -248,7 +257,8 @@ def test_input_invalid_type_ticks(self, number_of_ticks): maximum = params.get("maximum") with self.assertRaises(TypeError): self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, - number_of_ticks=number_of_ticks) + number_of_ticks=number_of_ticks, + is_application=False) def test_init_default_qslider(self): '''Tests the instantiation of the QSlider widget. @@ -256,7 +266,8 @@ def test_init_default_qslider(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget.slider, QSlider) @@ -266,7 +277,8 @@ def test_init_default_qvalidator(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget.validator, QtGui.QValidator) @@ -276,7 +288,8 @@ def test_init_default_qlineedit(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget.line_edit, QLineEdit) @@ -286,7 +299,8 @@ def test_init_default_qlabels(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget.min_label, QLabel) self.assertIsInstance(self.widget.median_label, QLabel) @@ -298,7 +312,8 @@ def test_init_default_qgridlayout(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertIsInstance(self.widget.widget_layout, QGridLayout) @@ -315,7 +330,8 @@ def test_get_and_set_uislider_value(self, widget_value, expected_value, test_wid ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.setValue(widget_value) self.assertEqual(self.widget.value(), expected_value) @@ -326,7 +342,8 @@ def test_get_and_set_qslider_value(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.slider.setValue(50) self.assertEqual(self.widget._getQSliderValue(), 50) @@ -345,7 +362,8 @@ def test_get_and_set_qlineedit_value(self, line_edit_value, expected): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.line_edit.setText(line_edit_value) self.assertEqual(self.widget._getQLineEditValue(), expected) @@ -364,7 +382,8 @@ def test_acceptable_update_qslider(self, line_edit_value, expected_slider_value, ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.line_edit.setText(line_edit_value) self.widget._updateQSlider() @@ -377,7 +396,8 @@ def test_empty_update_qslider(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.line_edit.setText("") self.widget._updateQSlider() @@ -391,7 +411,8 @@ def test_lt_min_update_qslider(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) lt_min_value = str(minimum - 1) self.widget.line_edit.setText(lt_min_value) @@ -408,7 +429,8 @@ def test_gt_max_update_qslider(self): for params in self.test_widgets.values(): minimum = params.get("minimum") maximum = params.get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) gt_min_value = str(maximum + 1) self.widget.line_edit.setText(gt_min_value) @@ -432,7 +454,8 @@ def test_update_qlineedit(self, slider_value, expected_line_edit_value, test_wid ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.widget.slider.setValue(slider_value) self.widget._updateQLineEdit() @@ -451,7 +474,8 @@ def test_scale_lineedit_to_slider(self, line_edit_value, expected, test_widget): ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertEqual(self.widget._scaleLineEditToSlider(line_edit_value), expected) @@ -468,7 +492,8 @@ def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): ''' minimum = self.test_widgets.get(test_widget).get("minimum") maximum = self.test_widgets.get(test_widget).get("maximum") - self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum) + self.widget = UISliderWidget.UISliderWidget(minimum=minimum, maximum=maximum, + is_application=False) self.assertEqual(self.widget._scaleSliderToLineEdit(slider_value), expected) From 4521810a7723fb447a7188c3d12ef86e0a1afb1c Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:07:17 +0000 Subject: [PATCH 54/68] Add pyqt to qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2acc3d9..5389d2f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: python: [3.8, 3.11] - qtbindings: ['PySide2', 'PyQt5'] + qtbindings: ['PySide2', 'PyQt5', 'pyqt'] steps: - uses: actions/checkout@v4 with: From 1c83b6e4ed0d3badcfb6475905ab3deaa5de760b Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:08:56 +0000 Subject: [PATCH 55/68] Correct 'pyqt' to 'qtpy' in qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5389d2f..039ee38 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: python: [3.8, 3.11] - qtbindings: ['PySide2', 'PyQt5', 'pyqt'] + qtbindings: ['PySide2', 'PyQt5', 'qtpy'] steps: - uses: actions/checkout@v4 with: From 3b641d3b7dd1e9fdecf7e769ebe88365ad7832fc Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:21:57 +0000 Subject: [PATCH 56/68] Remove qtpy from qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 039ee38..2acc3d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: python: [3.8, 3.11] - qtbindings: ['PySide2', 'PyQt5', 'qtpy'] + qtbindings: ['PySide2', 'PyQt5'] steps: - uses: actions/checkout@v4 with: From 96cb86f7425fe873a1cd04f16d08ff41f1380c9f Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:34:55 +0000 Subject: [PATCH 57/68] Test adding QApplication to setUp() and tearDown() methods --- test/test_UISliderWidget.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 5cb4e40..f4e4135 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,5 +1,7 @@ +import sys + from qtpy import QtGui -from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider +from qtpy.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider from unittest_parametrize import ParametrizedTestCase, param, parametrize from eqt.ui import UISliderWidget @@ -7,6 +9,7 @@ class TestUISliderWidget(ParametrizedTestCase): def setUp(self): + self.app = QApplication(sys.argv) self.test_widgets = { "standard": {"minimum": 0.0, "maximum": 10.0}, "positive": {"minimum": 1.0, "maximum": 10.0}, @@ -499,3 +502,4 @@ def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): def tearDown(self): self.form = None + sys.exit(self.app.exec_()) From ae3fe753d33c6c6a42c46830526b3f25c57010d3 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:39:05 +0000 Subject: [PATCH 58/68] Remove QApplication from setUp() and tearDown() methods --- test/test_UISliderWidget.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index f4e4135..5cb4e40 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -1,7 +1,5 @@ -import sys - from qtpy import QtGui -from qtpy.QtWidgets import QApplication, QGridLayout, QLabel, QLineEdit, QSlider +from qtpy.QtWidgets import QGridLayout, QLabel, QLineEdit, QSlider from unittest_parametrize import ParametrizedTestCase, param, parametrize from eqt.ui import UISliderWidget @@ -9,7 +7,6 @@ class TestUISliderWidget(ParametrizedTestCase): def setUp(self): - self.app = QApplication(sys.argv) self.test_widgets = { "standard": {"minimum": 0.0, "maximum": 10.0}, "positive": {"minimum": 1.0, "maximum": 10.0}, @@ -502,4 +499,3 @@ def test_scale_slider_to_lineedit(self, slider_value, expected, test_widget): def tearDown(self): self.form = None - sys.exit(self.app.exec_()) From e1f7199ba07a43136886979f1666e56b2a04d045 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:51:12 +0000 Subject: [PATCH 59/68] Test empty qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2acc3d9..7d8a134 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: python: [3.8, 3.11] - qtbindings: ['PySide2', 'PyQt5'] + qtbindings: [] steps: - uses: actions/checkout@v4 with: From 3a999a059c7d1813a3035b88f446e7337f5b6d62 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:53:49 +0000 Subject: [PATCH 60/68] Test adding qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d8a134..2acc3d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: python: [3.8, 3.11] - qtbindings: [] + qtbindings: ['PySide2', 'PyQt5'] steps: - uses: actions/checkout@v4 with: From 43c2f31c34466d6aacdc3ffc3a757dffc6dfbb4a Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:54:47 +0000 Subject: [PATCH 61/68] Move pip install of qtbindings --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2acc3d9..a0dfbef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,8 @@ jobs: with: python-version: ${{ matrix.python }} qt-bindings: ${{ matrix.qtbindings }} - - run: pip install -U .[dev] - run: pip install ${{ matrix.qtbindings }} + - run: pip install -U .[dev] - run: pytest deploy: needs: [test] From 604bc0809a428fdb0ade6f10bf889661a827b0c2 Mon Sep 17 00:00:00 2001 From: jcornall Date: Wed, 29 Jan 2025 16:57:34 +0000 Subject: [PATCH 62/68] Reset changes to test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0dfbef..2acc3d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,8 @@ jobs: with: python-version: ${{ matrix.python }} qt-bindings: ${{ matrix.qtbindings }} - - run: pip install ${{ matrix.qtbindings }} - run: pip install -U .[dev] + - run: pip install ${{ matrix.qtbindings }} - run: pytest deploy: needs: [test] From edc832a37dc78c5daed98deba6780bed73a1efab Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 30 Jan 2025 10:53:57 +0000 Subject: [PATCH 63/68] Add @skip_ci decorator to TestUISliderWidget class --- test/test_UISliderWidget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 5cb4e40..cbf8e46 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -4,7 +4,10 @@ from eqt.ui import UISliderWidget +from . import skip_ci + +@skip_ci class TestUISliderWidget(ParametrizedTestCase): def setUp(self): self.test_widgets = { From a0a074d0ea7c1db004e7e2c54d41e3a0a3f6aa3d Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 30 Jan 2025 12:16:11 +0000 Subject: [PATCH 64/68] Test bare minimum UISlider object instantiation with GitHub Actions --- test/test_UISliderWidget.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index cbf8e46..f2d6ca7 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -4,10 +4,7 @@ from eqt.ui import UISliderWidget -from . import skip_ci - -@skip_ci class TestUISliderWidget(ParametrizedTestCase): def setUp(self): self.test_widgets = { @@ -17,6 +14,12 @@ def setUp(self): "maximum": -1.0}, "float": {"minimum": -0.5, "maximum": 0.5}, "long": {"minimum": 1.11111, "maximum": 9.99999}} + def test_aaa_github_action(self): + self.widget_1 = UISliderWidget.UISliderWidget(minimum=0.0, maximum=10.0, + is_application=False) + self.widget_2 = UISliderWidget.UISliderWidget(minimum=0.0, maximum=10.0, + is_application=True) + @parametrize( "expected_minimum,expected_maximum,test_widget", [ From 3497399696fb999fcbe26f6edba6f03352162b75 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 30 Jan 2025 14:06:07 +0000 Subject: [PATCH 65/68] Update testing information in CONTRIBUTING.md --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 975b9c9..424cee9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,6 +67,8 @@ Before merging a pull request, all tests must pass. These can be run locally fro ```sh pytest ``` +> [!NOTE] +> For files that test the GUI elements, the `@skip_ci` decorator has been included to skip these tests when the GitHub Actions are executed after pushing/merging. Without the decorator, these GUI test files will cause the `pytest` GitHub Action to fail. ### Install and Run `pre-commit` Adhere to our styling guide by installing [`pre-commit`](https://pre-commit.com) in your local eqt environment: @@ -89,6 +91,8 @@ The [`.pre-commit-config.yaml`](./.pre-commit-config.yaml) config file indicates ## Continuous Integration GitHub Actions automatically runs a subset of the unit tests on every commit via [`test.yml`](.github/workflows/test.yml). +> [!NOTE] +> GitHub Actions does not currently support unit tests that test GUI elements. These tests should include the `@skip_ci` decorator so that they are skipped when the GitHub Actions are executed. ### Testing @@ -106,7 +110,8 @@ Runs automatically -- when an annotated tag is pushed -- after builds (above) su Publishes to [PyPI](https://pypi.org/project/eqt). -:warning: The annotated tag's `title` must be `Version ` (separated by a blank line) and the `body` must contain release notes, e.g.: +> [!WARNING] +> The annotated tag's `title` must be `Version ` (separated by a blank line) and the `body` must contain release notes, e.g.: ```sh git tag v1.33.7 -a From d535b108c65ffb53f0027256f2e852e69991c618 Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 30 Jan 2025 14:10:11 +0000 Subject: [PATCH 66/68] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f954481..cd32c75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Refactor `UISliderWidget` class to support `QLineEdit` and layouts (#168) + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` - Update existing `FormDialog` tests and add new tests for `UISliderWidget`(#168) +- Update existing examples to demonstrate the `UISliderWidget` (#168) # Version 1.0.2 - Upgrade python to 3.8 in `test.yml` (#171) From 30a8fb0cfa6d71aa32f3e75760bc4c68836e48ec Mon Sep 17 00:00:00 2001 From: jcornall Date: Thu, 30 Jan 2025 14:12:59 +0000 Subject: [PATCH 67/68] Add @skip_ci decorator to TestUISliderWidget class --- test/test_UISliderWidget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index f2d6ca7..1f77eca 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -4,7 +4,10 @@ from eqt.ui import UISliderWidget +from . import skip_ci + +@skip_ci class TestUISliderWidget(ParametrizedTestCase): def setUp(self): self.test_widgets = { From 11f0ae579f6b220b0a1bf7b98879ae0ba1871b9e Mon Sep 17 00:00:00 2001 From: jcornall Date: Fri, 31 Jan 2025 13:47:17 +0000 Subject: [PATCH 68/68] Update CHANGELOG.md, remove redundant test, correct docstring --- CHANGELOG.md | 4 +--- eqt/ui/UISliderWidget.py | 2 ++ test/test_UISliderWidget.py | 6 ------ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd32c75..3c7c2b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,8 @@ - Use `qtpy` as virtual Qt binding package. GHA unit tests are run with PySide2 and PyQt5 (#146) - Add `pyqt_env.yml` and `pyside_env.yml` environment files (#146) - Update `CONTRIBUTING.md`, `README.md` and add documentation file (#146) -- Refactor `UISliderWidget` class to support `QLineEdit` and layouts (#168) +- Refactor `UISliderWidget` class to support `QLineEdit` and layouts, update tests and examples (#168) + Breaks backwards compatability as `UISliderWidget` no longer accepts a `QLabel` -- Update existing `FormDialog` tests and add new tests for `UISliderWidget`(#168) -- Update existing examples to demonstrate the `UISliderWidget` (#168) # Version 1.0.2 - Upgrade python to 3.8 in `test.yml` (#171) diff --git a/eqt/ui/UISliderWidget.py b/eqt/ui/UISliderWidget.py index 2c320f5..335c599 100644 --- a/eqt/ui/UISliderWidget.py +++ b/eqt/ui/UISliderWidget.py @@ -87,6 +87,8 @@ def setValue(self, value): def _setMinimumMaximum(self, minimum, maximum): '''Sets the widget's minimum and maximum attributes. Checks that the minimum + is less than the maximum. If the minimum is greater than or equal to the maximum, + a ValueError is raised. ''' if minimum >= maximum: raise ValueError("'minimum' argument must be less than 'maximum'") diff --git a/test/test_UISliderWidget.py b/test/test_UISliderWidget.py index 1f77eca..cbf8e46 100644 --- a/test/test_UISliderWidget.py +++ b/test/test_UISliderWidget.py @@ -17,12 +17,6 @@ def setUp(self): "maximum": -1.0}, "float": {"minimum": -0.5, "maximum": 0.5}, "long": {"minimum": 1.11111, "maximum": 9.99999}} - def test_aaa_github_action(self): - self.widget_1 = UISliderWidget.UISliderWidget(minimum=0.0, maximum=10.0, - is_application=False) - self.widget_2 = UISliderWidget.UISliderWidget(minimum=0.0, maximum=10.0, - is_application=True) - @parametrize( "expected_minimum,expected_maximum,test_widget", [