diff --git a/eqt/ui/FormDialog.py b/eqt/ui/FormDialog.py index 510ff59..53ef76f 100644 --- a/eqt/ui/FormDialog.py +++ b/eqt/ui/FormDialog.py @@ -74,6 +74,14 @@ def addWidget(self, qwidget, qlabel=None, name=None, layout='form'): else: raise ValueError(f"layout '{layout}' unrecognised: expected 'form' or 'vertical'") + def removeWidget(self, name): + ''' + Removes a widget (and its label if present) from the layout. + Decreases the counter for the number of widgets in the layout. + Deletes the field (and label) from the dictionary. + ''' + self.formWidget.removeWidget(name) + def addSpanningWidget(self, qwidget, name=None, layout='form'): ''' Adds a spanning widget to the layout. @@ -93,6 +101,12 @@ def addSpanningWidget(self, qwidget, name=None, layout='form'): raise ValueError( f"layout {layout} is not recognised, must be set to 'form' or 'vertical'") + def getNumWidgets(self): + ''' + Returns the number of widgets in the form. + ''' + return self.formWidget.getNumWidgets() + def insertWidget(self, index, qwidget): '''inserts a widget to the vertical layout at the specific index''' self.formWidget.uiElements['verticalLayout'].insertWidget(index, qwidget) diff --git a/eqt/ui/UIFormWidget.py b/eqt/ui/UIFormWidget.py index 926a536..def2939 100644 --- a/eqt/ui/UIFormWidget.py +++ b/eqt/ui/UIFormWidget.py @@ -1,3 +1,5 @@ +import logging + from PySide2 import QtWidgets from .UISliderWidget import UISliderWidget @@ -55,6 +57,28 @@ def addSpanningWidget(self, qwidget, name): def addWidget(self, qwidget, qlabel, name): self._addWidget(name, qwidget, qlabel) + def removeWidget(self, name): + ''' + Removes a widget (and its label if present) from the layout. + Decreases the counter for the number of widgets in the layout. + Deletes the field (and label) from the dictionary. + ''' + formLayout = self.uiElements['groupBoxFormLayout'] + qwidget = self.getWidget(name, role='field') # retrieves the widget from its name + formLayout.removeRow(qwidget) # removes the whole row from the layout + self.num_widgets -= 1 # updates total number of widgets + self.getWidgets().pop(name + '_field') # removes field from the dictionary + try: + self.getWidgets().pop(name + '_label') + except KeyError: + logging.info('Widget ' + name + ' does not have a label.') + + def getNumWidgets(self): + ''' + Returns the number of widgets in the form. + ''' + return self.num_widgets + def getWidget(self, name, role='field'): '''returns the Widget by the name with which it has been added @@ -322,6 +346,23 @@ def __init__(self, parent=None, title=None): def addWidget(self, qwidget, qlabel, name): self.widget().addWidget(qwidget, qlabel, name) + def addSpanningWidget(self, qwidget, name): + self.widget().addSpanningWidget(qwidget, name) + + def removeWidget(self, name): + ''' + Removes a widget (and its label if present) from the layout. + Decreases the counter for the number of widgets in the layout. + Deletes the field (and label) from the dictionary. + ''' + self.widget().removeWidget(name) + + def getNumWidgets(self): + ''' + Returns the number of widgets in the form. + ''' + return self.widget().getNumWidgets() + def getWidget(self, name, role='field'): '''returns the Widget by the name with which it has been added diff --git a/examples/remove_widgets_example.py b/examples/remove_widgets_example.py new file mode 100644 index 0000000..aebfdf4 --- /dev/null +++ b/examples/remove_widgets_example.py @@ -0,0 +1,130 @@ +import sys + +from PySide2 import QtWidgets + +from eqt.ui import FormDialog, UIFormWidget + + +class MainUI(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + + # create a FormDockWidget + dock = UIFormWidget.FormDockWidget(parent=self) + dock.setWindowTitle('Example remove widget') + 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)) + + # create button for Form Dialog + pb = QtWidgets.QPushButton(self) + pb.setText("Open Form Dialog") + pb.clicked.connect(lambda: self.openFormDialog()) + + # create window layout + layout = QtWidgets.QHBoxLayout() + layout.addWidget(pb) + layout.addWidget(dock) + widg = QtWidgets.QWidget() + widg.setLayout(layout) + self.setCentralWidget(widg) + + # print dictionary of all widgets in dock + print("\nDictionary of widgets in the Form Dock Widget:\n" + str(dock.getWidgets())) + + 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) + 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 + self.dialog.onCancel = self.rejected + + # print dictionary of all widgets in dialog + print("Dictionary of widgets in Form Dialog:\n" + str(self.dialog.getWidgets())) + + dialog.open() + + def addWidgetsToExampleForm(self, form): + + # 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') + + # 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') + + # 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 = 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')) + + def rejected(self): + print("\nDialog closed.") + + def remove(self, form, button, userselection=False): + if userselection is False: + userselection = form.getWidget('userinput').currentText() + form.getWidget('userinput').removeItem(form.getWidget('userinput').currentIndex()) + print("\nRemove " + userselection) + form.removeWidget(userselection) + if form.getWidget('userinput').currentIndex() == -1: + button.setEnabled(False) + if button == form.getWidget('Button Remove w1') or button == form.getWidget( + 'Button Remove Spanning'): + button.setEnabled(False) + + print("\nDictionary of widgets after deletion of " + userselection + ":\n" + + str(form.getWidgets())) + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + + window = MainUI() + + sys.exit(app.exec_()) diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index c35676b..c5539c7 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -39,6 +39,37 @@ def add_two_widgets(self): form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') form.addWidget(QtWidgets.QCheckBox('test checkbox'), 'CheckBox: ', 'checkBox') + def _test_remove_one_widget(self, name): + """ + Remove one widget. + Checks the number of widgets in the form before and after deletion are consistent. + Checks the number of rows in the layout and number of widgets in the form are + consistent. + + name: name in the dictionary of the widget to be removed + """ + qwidget = self.form.getWidget(name, role='field') + rowpre, role = self.layout.getWidgetPosition(qwidget) # checks the widget exists + prerowcount = self.layout.rowCount() + predictionary = self.form.getWidgets().copy() + prenumwidgets = self.form.getNumWidgets() + self.form.removeWidget(name) + postrowcount = self.layout.rowCount() + postdictionary = self.form.getWidgets() + postnumwidgets = self.form.getNumWidgets() + self.assertNotEqual(predictionary, postdictionary) + self.assertEqual(prenumwidgets, postnumwidgets + 1) + self.assertEqual(prerowcount, postrowcount + 1) + self.assertEqual(postrowcount, postnumwidgets) + + def test_remove_every_widget(self): + """Remove every widget from `self.form`""" + list_widgets = [ + 'label', 'checkBox', 'comboBox', 'doubleSpinBox', 'spinBox', 'slider', + 'uiSliderWidget', 'radioButton', 'textEdit', 'plainTextEdit', 'lineEdit', 'button'] + for name in list_widgets: + self._test_remove_one_widget(name) + def test_getWidgetState_returns_visibility(self): """ Check that the visibility of the widget is saved to the state @@ -295,6 +326,7 @@ def setUp(self): self.add_every_widget() self.simple_form = FormDialog() self.add_two_widgets() + self.layout = self.form.formWidget.uiElements['groupBoxFormLayout'] def test_getWidgetState_returns_QLabel_value(self): """Check that the value of the QLabel is saved to the state""" @@ -369,6 +401,7 @@ def setUp(self): self.add_every_widget() self.simple_form = FormWidget() self.add_two_widgets() + self.layout = self.form.uiElements['groupBoxFormLayout'] def test_getWidgetState_returns_QLabel_value(self): """Check that the value of the QLabel is saved to the state""" @@ -443,6 +476,7 @@ def setUp(self): self.add_every_widget() self.simple_form = FormDockWidget() self.add_two_widgets() + self.layout = self.form.widget().uiElements['groupBoxFormLayout'] def test_getWidgetState_returns_QLabel_value(self): """Check that the value of the QLabel is saved to the state"""