diff --git a/eqt/ui/FormDialog.py b/eqt/ui/FormDialog.py index 53ef76f..0853a5d 100644 --- a/eqt/ui/FormDialog.py +++ b/eqt/ui/FormDialog.py @@ -23,6 +23,7 @@ def __init__(self, parent=None, title=None): self.setWindowTitle(title) # add button box to the UI self.formWidget.uiElements['verticalLayout'].addWidget(bb) + bb.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self._onOk) bb.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self._onCancel) @property @@ -35,14 +36,27 @@ def Cancel(self): '''returns a reference to the Dialog Cancel button to connect its signals''' return self.buttonBox.button(QtWidgets.QDialogButtonBox.Cancel) + def _onOk(self): + '''saves the widget states and calls `onOk`''' + self.saveAllWidgetStates() + self.onOk() + self.close() + def _onCancel(self): - '''calls onCancel and closes the FormDialog''' + '''calls `onCancel`, closes the FormDialog and restores the previously saved states + or the default states.''' self.onCancel() self.close() + self.restoreAllSavedWidgetStates() + + def onOk(self): + '''Called when the dialog's "Ok" button is clicked. + Can be redefined to add additional functionality on "Ok"''' + pass def onCancel(self): '''Called when the dialog's "Cancel" button is clicked. - Can be redefined to add additional functionality on "Cancel"''' + Can be redefined to add additional functionality on "Cancel"''' pass @property diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 991f12c..48947f1 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -46,6 +46,7 @@ def createForm(self): 'verticalLayout': verticalLayout, 'groupBox': groupBox, 'groupBoxFormLayout': groupBoxFormLayout} self.widgets = {} + self.default_widgets = {} @property def groupBox(self): @@ -140,6 +141,7 @@ def _addWidget(self, name, qwidget, qlabel=None): # add the field field = f'{name}_field' self.widgets[field] = qwidget + self.default_widgets[field] = qwidget if qlabel is not None: # add the label @@ -152,6 +154,7 @@ def _addWidget(self, name, qwidget, qlabel=None): # save a reference to label widgets in the dictionary self.widgets[label] = qlabel + self.default_widgets[label] = qlabel field_form_role = QtWidgets.QFormLayout.FieldRole @@ -161,6 +164,28 @@ def _addWidget(self, name, qwidget, qlabel=None): formLayout.setWidget(widgetno, field_form_role, qwidget) self.num_widgets += 1 + self.populate_default_widget_states_dictionary(name) + + def populate_default_widget_states_dictionary(self, name): + ''' + Creates an attribute dictionary of default widget states. The entries are in the + format: {'value': str | bool | int, 'enabled': bool, 'visible': bool}. + This can be used to restore the default states of the widgets invoking `applyWidgetStates`. + ''' + if not hasattr(self, 'default_widget_states'): + self.default_widget_states = {} + # add the default state of the qwidget + self.default_widget_states[f'{name}_field'] = self.getWidgetState(name, 'field') + # add the default state of the qlabel + if f'{name}_label' in self.widgets.keys(): + self.default_widget_states[f'{name}_label'] = self.getWidgetState(name, 'label') + + def set_default_widget_states_visible_true(self): + ''' + Sets all of the entries 'visible' in the `default_widget_states` dictionary to be `True`. + ''' + for key in self.default_widget_states.keys(): + self.default_widget_states[key]['visible'] = True def getAllWidgetStates(self): ''' @@ -320,10 +345,14 @@ def saveAllWidgetStates(self): def restoreAllSavedWidgetStates(self): ''' - Restore all widgets in the form to the state saved by `saveAllWidgetStates()`. - If `saveAllWidgetStates()` method was not previously invoked, do nothing. + All widgets in the form are restored to the saved states. There are saved states only if + `saveAllWidgetStates` was previously invoked. If there are no previously saved states, + `default_widget_states` are used instead, after being made visible. ''' - if hasattr(self, 'widget_states'): + if not hasattr(self, 'widget_states'): + self.set_default_widget_states_visible_true() + self.applyWidgetStates(self.default_widget_states) + else: self.applyWidgetStates(self.widget_states) diff --git a/examples/dialog_example_3_save_default.py b/examples/dialog_example_3_save_default.py new file mode 100644 index 0000000..27c8cc5 --- /dev/null +++ b/examples/dialog_example_3_save_default.py @@ -0,0 +1,50 @@ +import sys + +import utilitiesForExamples as utex +from PySide2 import QtWidgets + +from eqt.ui import FormDialog + + +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.executeDialog()) + + layout = QtWidgets.QHBoxLayout() + layout.addWidget(pb) + widg = QtWidgets.QWidget() + widg.setLayout(layout) + + self.setCentralWidget(widg) + self.dialog = FormDialog(parent=self, title='Example') + self.openFormDialog() + + self.show() + + def openFormDialog(self): + utex.addWidgetsToExample(self.dialog) + # redefine the onOk and onCancel functions + self.dialog.onOk = self.accepted + self.dialog.onCancel = self.rejected + + def accepted(self): + print("States saved") + + def rejected(self): + print("States rejected") + + # open dialog function when the parent button is clicked + def executeDialog(self): + self.dialog.open() + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + + window = MainUI() + + sys.exit(app.exec_()) diff --git a/examples/remove_widgets_example.py b/examples/remove_widgets_example.py index 185f55d..d553373 100644 --- a/examples/remove_widgets_example.py +++ b/examples/remove_widgets_example.py @@ -49,12 +49,18 @@ def openFormDialog(self): # store a reference self.dialog = dialog 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("Dictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets())) + print("\nDictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets())) dialog.open() + def _onOkRedefined(self): + '''Saves the widget states.''' + self.dialog.saveAllWidgetStates() + def addWidgetsToExampleForm(self, form): # add widget 1 as QLineEdit diff --git a/examples/utilitiesForExamples.py b/examples/utilitiesForExamples.py new file mode 100644 index 0000000..19eda57 --- /dev/null +++ b/examples/utilitiesForExamples.py @@ -0,0 +1,26 @@ +from PySide2 import QtWidgets + +from eqt.ui.UISliderWidget import UISliderWidget + + +def addWidgetsToExample(form): + ''' + Adds a spanning widget and every type of widget to a form + ''' + # add a spanning widget + form.addSpanningWidget(QtWidgets.QLabel("Input Values: "), 'input_title') + # add all widgets + form.addWidget(QtWidgets.QLabel('Label'), 'Label: ', 'label') + form.addWidget(QtWidgets.QCheckBox('check me'), 'CheckBox: ', 'checkBox') + combobox_list = ['choice 1', 'choice 2'] + form.addWidget(QtWidgets.QComboBox(), 'ComboBox: ', 'comboBox') + form.getWidget('comboBox').addItems(combobox_list) + 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') + 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') + form.addWidget(QtWidgets.QLineEdit('write text here'), 'LineEdit: ', 'lineEdit') + form.addWidget(QtWidgets.QPushButton('Click me'), 'Button: ', 'button') diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 02ba8bb..4ea6480 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -3,6 +3,8 @@ from unittest import mock from PySide2 import QtWidgets +from PySide2.QtCore import Qt +from PySide2.QtTest import QTest from eqt.ui.FormDialog import FormDialog from eqt.ui.UIFormWidget import FormDockWidget, FormWidget @@ -17,6 +19,22 @@ class FormsCommonTests(metaclass=abc.ABCMeta): def setUp(self): raise NotImplementedError + @property + def exampleState(self): + # define two states for every widget + state = [{ + 'label_value': 'Test label state 0', 'checkbox_value': False, 'combobox_value': 0, + 'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10, + 'uislider_value': 10, 'radio_value': False, 'textEdit_value': 'test edit 0', + 'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0', + 'pushButton_value': False}, { + 'label_value': 'Test label state 1', 'checkbox_value': True, 'combobox_value': 1, + 'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1, + 'uislider_value': 1, 'radio_value': True, 'textEdit_value': 'test edit 1', + 'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1', + 'pushButton_value': True}] + return state + def add_every_widget(self): """Generate every widget and add it to `self.form`""" form = self.form @@ -39,6 +57,44 @@ def add_two_widgets(self): form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') form.addWidget(QtWidgets.QCheckBox('test checkbox'), 'CheckBox: ', 'checkBox') + def set_state(self, i): + """ + Applies the values saved in `self.exampleState` at position `i` to the widgets in the form. + + Parameters + ---------------- + i: int + """ + state = self.exampleState + # set the states + # QLabel + self.form.getWidget('label').setText(state[i]['label_value']) + # QCheckBox + self.form.getWidget('checkBox').setChecked(state[i]['checkbox_value']) + # QComboBox + combobox_list = ['test', 'test2'] + self.form.getWidget('comboBox').addItems(combobox_list) + self.form.getWidget('comboBox').setCurrentIndex(state[i]['combobox_value']) + # QDoubleSpinBox + self.form.getWidget('doubleSpinBox').setValue(state[i]['doubleSpinBox_value']) + # QSpinBox + self.form.getWidget('spinBox').setValue(state[i]['spinBox_value']) + # QSlider + self.form.getWidget('slider').setValue(state[i]['slider_value']) + # UISlider + self.form.getWidget('uiSliderWidget').setValue(state[i]['uislider_value']) + # QRadioButton + self.form.getWidget('radioButton').setChecked(state[i]['radio_value']) + # QTextEdit + self.form.getWidget('textEdit').setText(state[i]['textEdit_value']) + # QPlainTextEdit + self.form.getWidget('plainTextEdit').setPlainText(state[i]['plainTextEdit_value']) + # QLineEdit + self.form.getWidget('lineEdit').setText(state[i]['lineEdit_value']) + # QPushButton + self.form.getWidget('button').setCheckable(True) + self.form.getWidget('button').setChecked(state[i]['pushButton_value']) + def _test_remove_one_widget(self, name): """ Remove one widget. @@ -328,6 +384,38 @@ def setUp(self): self.add_two_widgets() self.layout = self.form.formWidget.uiElements['groupBoxFormLayout'] + def click_Ok(self): + QTest.mouseClick(self.form.Ok, Qt.LeftButton) + + def click_Cancel(self): + QTest.mouseClick(self.form.Cancel, Qt.LeftButton) + + def test_dialog_buttons_default_behaviour(self): + # create the states dictionary + self.set_state(1) + states1 = self.form.getAllWidgetStates() + self.set_state(0) + states0 = self.form.getAllWidgetStates() + # check state 0 and 1 are not saved when Cancel is pressed + self.click_Cancel() + self.assertNotEqual(states0, self.form.getAllWidgetStates()) + self.assertNotEqual(states1, self.form.getAllWidgetStates()) + # save state 0 + self.set_state(0) + self.assertEqual(states0, self.form.getAllWidgetStates()) + self.click_Ok() + self.assertEqual(states0, self.form.getAllWidgetStates()) + # save state 1 + self.set_state(1) + self.assertEqual(states1, self.form.getAllWidgetStates()) + self.click_Ok() + self.assertEqual(states1, self.form.getAllWidgetStates()) + # change to state 0 without saving + self.set_state(0) + self.assertEqual(states0, self.form.getAllWidgetStates()) + self.click_Cancel() + self.assertEqual(states1, self.form.getAllWidgetStates()) + def test_form_init_title(self): """Tests if the FormDialog is created correctly with or without the title argument.""" FormDialog()