Skip to content

Commit 05bc80d

Browse files
authored
Save entries by default in the Form Dialog (#97)
- Adds a method to `UIFormWidget` to populate the default-widget-states dictionary and a method to make default-widget-states visible. - Form dialog saves states by default when `Ok` is clicked and restores the previously-saved states or the default states when `Cancel` is clicked. - Adds example file "dialog_example_3_save_default" to demonstrate the new features. - Adds example file "utilitiesForExample" for methods recurring in the examples. - Adds `exampleState`, `set_state` and `test_dialog_buttons_default_behaviour` to the unit tests.
1 parent cd81441 commit 05bc80d

6 files changed

+219
-6
lines changed

eqt/ui/FormDialog.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(self, parent=None, title=None):
2323
self.setWindowTitle(title)
2424
# add button box to the UI
2525
self.formWidget.uiElements['verticalLayout'].addWidget(bb)
26+
bb.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self._onOk)
2627
bb.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self._onCancel)
2728

2829
@property
@@ -35,14 +36,27 @@ def Cancel(self):
3536
'''returns a reference to the Dialog Cancel button to connect its signals'''
3637
return self.buttonBox.button(QtWidgets.QDialogButtonBox.Cancel)
3738

39+
def _onOk(self):
40+
'''saves the widget states and calls `onOk`'''
41+
self.saveAllWidgetStates()
42+
self.onOk()
43+
self.close()
44+
3845
def _onCancel(self):
39-
'''calls onCancel and closes the FormDialog'''
46+
'''calls `onCancel`, closes the FormDialog and restores the previously saved states
47+
or the default states.'''
4048
self.onCancel()
4149
self.close()
50+
self.restoreAllSavedWidgetStates()
51+
52+
def onOk(self):
53+
'''Called when the dialog's "Ok" button is clicked.
54+
Can be redefined to add additional functionality on "Ok"'''
55+
pass
4256

4357
def onCancel(self):
4458
'''Called when the dialog's "Cancel" button is clicked.
45-
Can be redefined to add additional functionality on "Cancel"'''
59+
Can be redefined to add additional functionality on "Cancel"'''
4660
pass
4761

4862
@property

eqt/ui/UIFormWidget.py

+32-3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def createForm(self):
4646
'verticalLayout': verticalLayout, 'groupBox': groupBox,
4747
'groupBoxFormLayout': groupBoxFormLayout}
4848
self.widgets = {}
49+
self.default_widgets = {}
4950

5051
@property
5152
def groupBox(self):
@@ -140,6 +141,7 @@ def _addWidget(self, name, qwidget, qlabel=None):
140141
# add the field
141142
field = f'{name}_field'
142143
self.widgets[field] = qwidget
144+
self.default_widgets[field] = qwidget
143145

144146
if qlabel is not None:
145147
# add the label
@@ -152,6 +154,7 @@ def _addWidget(self, name, qwidget, qlabel=None):
152154

153155
# save a reference to label widgets in the dictionary
154156
self.widgets[label] = qlabel
157+
self.default_widgets[label] = qlabel
155158

156159
field_form_role = QtWidgets.QFormLayout.FieldRole
157160

@@ -161,6 +164,28 @@ def _addWidget(self, name, qwidget, qlabel=None):
161164

162165
formLayout.setWidget(widgetno, field_form_role, qwidget)
163166
self.num_widgets += 1
167+
self.populate_default_widget_states_dictionary(name)
168+
169+
def populate_default_widget_states_dictionary(self, name):
170+
'''
171+
Creates an attribute dictionary of default widget states. The entries are in the
172+
format: {'value': str | bool | int, 'enabled': bool, 'visible': bool}.
173+
This can be used to restore the default states of the widgets invoking `applyWidgetStates`.
174+
'''
175+
if not hasattr(self, 'default_widget_states'):
176+
self.default_widget_states = {}
177+
# add the default state of the qwidget
178+
self.default_widget_states[f'{name}_field'] = self.getWidgetState(name, 'field')
179+
# add the default state of the qlabel
180+
if f'{name}_label' in self.widgets.keys():
181+
self.default_widget_states[f'{name}_label'] = self.getWidgetState(name, 'label')
182+
183+
def set_default_widget_states_visible_true(self):
184+
'''
185+
Sets all of the entries 'visible' in the `default_widget_states` dictionary to be `True`.
186+
'''
187+
for key in self.default_widget_states.keys():
188+
self.default_widget_states[key]['visible'] = True
164189

165190
def getAllWidgetStates(self):
166191
'''
@@ -320,10 +345,14 @@ def saveAllWidgetStates(self):
320345

321346
def restoreAllSavedWidgetStates(self):
322347
'''
323-
Restore all widgets in the form to the state saved by `saveAllWidgetStates()`.
324-
If `saveAllWidgetStates()` method was not previously invoked, do nothing.
348+
All widgets in the form are restored to the saved states. There are saved states only if
349+
`saveAllWidgetStates` was previously invoked. If there are no previously saved states,
350+
`default_widget_states` are used instead, after being made visible.
325351
'''
326-
if hasattr(self, 'widget_states'):
352+
if not hasattr(self, 'widget_states'):
353+
self.set_default_widget_states_visible_true()
354+
self.applyWidgetStates(self.default_widget_states)
355+
else:
327356
self.applyWidgetStates(self.widget_states)
328357

329358

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import sys
2+
3+
import utilitiesForExamples as utex
4+
from PySide2 import QtWidgets
5+
6+
from eqt.ui import FormDialog
7+
8+
9+
class MainUI(QtWidgets.QMainWindow):
10+
def __init__(self, parent=None):
11+
QtWidgets.QMainWindow.__init__(self, parent)
12+
13+
pb = QtWidgets.QPushButton(self)
14+
pb.setText("Open Dialog with form layout")
15+
pb.clicked.connect(lambda: self.executeDialog())
16+
17+
layout = QtWidgets.QHBoxLayout()
18+
layout.addWidget(pb)
19+
widg = QtWidgets.QWidget()
20+
widg.setLayout(layout)
21+
22+
self.setCentralWidget(widg)
23+
self.dialog = FormDialog(parent=self, title='Example')
24+
self.openFormDialog()
25+
26+
self.show()
27+
28+
def openFormDialog(self):
29+
utex.addWidgetsToExample(self.dialog)
30+
# redefine the onOk and onCancel functions
31+
self.dialog.onOk = self.accepted
32+
self.dialog.onCancel = self.rejected
33+
34+
def accepted(self):
35+
print("States saved")
36+
37+
def rejected(self):
38+
print("States rejected")
39+
40+
# open dialog function when the parent button is clicked
41+
def executeDialog(self):
42+
self.dialog.open()
43+
44+
45+
if __name__ == "__main__":
46+
app = QtWidgets.QApplication(sys.argv)
47+
48+
window = MainUI()
49+
50+
sys.exit(app.exec_())

examples/remove_widgets_example.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@ def openFormDialog(self):
4949
# store a reference
5050
self.dialog = dialog
5151
self.dialog.onCancel = self.rejected
52+
# redefine `onOk`` so it does not close the dialog.
53+
self.dialog._onOk = self._onOkRedefined
5254

5355
# print dictionary of all widgets in dialog
54-
print("Dictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets()))
56+
print("\nDictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets()))
5557

5658
dialog.open()
5759

60+
def _onOkRedefined(self):
61+
'''Saves the widget states.'''
62+
self.dialog.saveAllWidgetStates()
63+
5864
def addWidgetsToExampleForm(self, form):
5965

6066
# add widget 1 as QLineEdit

examples/utilitiesForExamples.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from PySide2 import QtWidgets
2+
3+
from eqt.ui.UISliderWidget import UISliderWidget
4+
5+
6+
def addWidgetsToExample(form):
7+
'''
8+
Adds a spanning widget and every type of widget to a form
9+
'''
10+
# add a spanning widget
11+
form.addSpanningWidget(QtWidgets.QLabel("Input Values: "), 'input_title')
12+
# add all widgets
13+
form.addWidget(QtWidgets.QLabel('Label'), 'Label: ', 'label')
14+
form.addWidget(QtWidgets.QCheckBox('check me'), 'CheckBox: ', 'checkBox')
15+
combobox_list = ['choice 1', 'choice 2']
16+
form.addWidget(QtWidgets.QComboBox(), 'ComboBox: ', 'comboBox')
17+
form.getWidget('comboBox').addItems(combobox_list)
18+
form.addWidget(QtWidgets.QDoubleSpinBox(), 'DoubleSpinBox: ', 'doubleSpinBox')
19+
form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox')
20+
form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider')
21+
form.addWidget(UISliderWidget(QtWidgets.QLabel()), 'UISlider: ', 'uiSliderWidget')
22+
form.addWidget(QtWidgets.QRadioButton('select me'), 'RadioButton: ', 'radioButton')
23+
form.addWidget(QtWidgets.QTextEdit('write text here'), 'TextEdit: ', 'textEdit')
24+
form.addWidget(QtWidgets.QPlainTextEdit('write text here'), 'PlainTextEdit: ', 'plainTextEdit')
25+
form.addWidget(QtWidgets.QLineEdit('write text here'), 'LineEdit: ', 'lineEdit')
26+
form.addWidget(QtWidgets.QPushButton('Click me'), 'Button: ', 'button')

test/test__formUI_status_test.py

+88
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from unittest import mock
44

55
from PySide2 import QtWidgets
6+
from PySide2.QtCore import Qt
7+
from PySide2.QtTest import QTest
68

79
from eqt.ui.FormDialog import FormDialog
810
from eqt.ui.UIFormWidget import FormDockWidget, FormWidget
@@ -17,6 +19,22 @@ class FormsCommonTests(metaclass=abc.ABCMeta):
1719
def setUp(self):
1820
raise NotImplementedError
1921

22+
@property
23+
def exampleState(self):
24+
# define two states for every widget
25+
state = [{
26+
'label_value': 'Test label state 0', 'checkbox_value': False, 'combobox_value': 0,
27+
'doubleSpinBox_value': 10.0, 'spinBox_value': 10, 'slider_value': 10,
28+
'uislider_value': 10, 'radio_value': False, 'textEdit_value': 'test edit 0',
29+
'plainTextEdit_value': 'test plain 0', 'lineEdit_value': 'test line 0',
30+
'pushButton_value': False}, {
31+
'label_value': 'Test label state 1', 'checkbox_value': True, 'combobox_value': 1,
32+
'doubleSpinBox_value': 1.0, 'spinBox_value': 1, 'slider_value': 1,
33+
'uislider_value': 1, 'radio_value': True, 'textEdit_value': 'test edit 1',
34+
'plainTextEdit_value': 'test plain 1', 'lineEdit_value': 'test line 1',
35+
'pushButton_value': True}]
36+
return state
37+
2038
def add_every_widget(self):
2139
"""Generate every widget and add it to `self.form`"""
2240
form = self.form
@@ -39,6 +57,44 @@ def add_two_widgets(self):
3957
form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label')
4058
form.addWidget(QtWidgets.QCheckBox('test checkbox'), 'CheckBox: ', 'checkBox')
4159

60+
def set_state(self, i):
61+
"""
62+
Applies the values saved in `self.exampleState` at position `i` to the widgets in the form.
63+
64+
Parameters
65+
----------------
66+
i: int
67+
"""
68+
state = self.exampleState
69+
# set the states
70+
# QLabel
71+
self.form.getWidget('label').setText(state[i]['label_value'])
72+
# QCheckBox
73+
self.form.getWidget('checkBox').setChecked(state[i]['checkbox_value'])
74+
# QComboBox
75+
combobox_list = ['test', 'test2']
76+
self.form.getWidget('comboBox').addItems(combobox_list)
77+
self.form.getWidget('comboBox').setCurrentIndex(state[i]['combobox_value'])
78+
# QDoubleSpinBox
79+
self.form.getWidget('doubleSpinBox').setValue(state[i]['doubleSpinBox_value'])
80+
# QSpinBox
81+
self.form.getWidget('spinBox').setValue(state[i]['spinBox_value'])
82+
# QSlider
83+
self.form.getWidget('slider').setValue(state[i]['slider_value'])
84+
# UISlider
85+
self.form.getWidget('uiSliderWidget').setValue(state[i]['uislider_value'])
86+
# QRadioButton
87+
self.form.getWidget('radioButton').setChecked(state[i]['radio_value'])
88+
# QTextEdit
89+
self.form.getWidget('textEdit').setText(state[i]['textEdit_value'])
90+
# QPlainTextEdit
91+
self.form.getWidget('plainTextEdit').setPlainText(state[i]['plainTextEdit_value'])
92+
# QLineEdit
93+
self.form.getWidget('lineEdit').setText(state[i]['lineEdit_value'])
94+
# QPushButton
95+
self.form.getWidget('button').setCheckable(True)
96+
self.form.getWidget('button').setChecked(state[i]['pushButton_value'])
97+
4298
def _test_remove_one_widget(self, name):
4399
"""
44100
Remove one widget.
@@ -328,6 +384,38 @@ def setUp(self):
328384
self.add_two_widgets()
329385
self.layout = self.form.formWidget.uiElements['groupBoxFormLayout']
330386

387+
def click_Ok(self):
388+
QTest.mouseClick(self.form.Ok, Qt.LeftButton)
389+
390+
def click_Cancel(self):
391+
QTest.mouseClick(self.form.Cancel, Qt.LeftButton)
392+
393+
def test_dialog_buttons_default_behaviour(self):
394+
# create the states dictionary
395+
self.set_state(1)
396+
states1 = self.form.getAllWidgetStates()
397+
self.set_state(0)
398+
states0 = self.form.getAllWidgetStates()
399+
# check state 0 and 1 are not saved when Cancel is pressed
400+
self.click_Cancel()
401+
self.assertNotEqual(states0, self.form.getAllWidgetStates())
402+
self.assertNotEqual(states1, self.form.getAllWidgetStates())
403+
# save state 0
404+
self.set_state(0)
405+
self.assertEqual(states0, self.form.getAllWidgetStates())
406+
self.click_Ok()
407+
self.assertEqual(states0, self.form.getAllWidgetStates())
408+
# save state 1
409+
self.set_state(1)
410+
self.assertEqual(states1, self.form.getAllWidgetStates())
411+
self.click_Ok()
412+
self.assertEqual(states1, self.form.getAllWidgetStates())
413+
# change to state 0 without saving
414+
self.set_state(0)
415+
self.assertEqual(states0, self.form.getAllWidgetStates())
416+
self.click_Cancel()
417+
self.assertEqual(states1, self.form.getAllWidgetStates())
418+
331419
def test_form_init_title(self):
332420
"""Tests if the FormDialog is created correctly with or without the title argument."""
333421
FormDialog()

0 commit comments

Comments
 (0)