Skip to content

Commit

Permalink
Save entries by default in the Form Dialog (#97)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
DanicaSTFC authored Dec 7, 2023
1 parent cd81441 commit 05bc80d
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 6 deletions.
18 changes: 16 additions & 2 deletions eqt/ui/FormDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
35 changes: 32 additions & 3 deletions eqt/ui/UIFormWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def createForm(self):
'verticalLayout': verticalLayout, 'groupBox': groupBox,
'groupBoxFormLayout': groupBoxFormLayout}
self.widgets = {}
self.default_widgets = {}

@property
def groupBox(self):
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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):
'''
Expand Down Expand Up @@ -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)


Expand Down
50 changes: 50 additions & 0 deletions examples/dialog_example_3_save_default.py
Original file line number Diff line number Diff line change
@@ -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_())
8 changes: 7 additions & 1 deletion examples/remove_widgets_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions examples/utilitiesForExamples.py
Original file line number Diff line number Diff line change
@@ -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')
88 changes: 88 additions & 0 deletions test/test__formUI_status_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 05bc80d

Please sign in to comment.