Skip to content

Commit

Permalink
Closes #70: add methods to remove widgets from forms (#93)
Browse files Browse the repository at this point in the history
- Adds method to remove widgets from forms.
- Adds method to extract the number of widgets from forms.
- Adds `addSpanningWidget` method to `FormDockWidget`.
- Adds example to remove widgets from the `FormDialog` and the `FormDockWidget`.
- Adds the unit test for the removal of widgets from the forms.
  • Loading branch information
DanicaSTFC authored Oct 30, 2023
1 parent 9193417 commit ec2c5a1
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
14 changes: 14 additions & 0 deletions eqt/ui/FormDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
41 changes: 41 additions & 0 deletions eqt/ui/UIFormWidget.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from PySide2 import QtWidgets

from .UISliderWidget import UISliderWidget
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
130 changes: 130 additions & 0 deletions examples/remove_widgets_example.py
Original file line number Diff line number Diff line change
@@ -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_())
34 changes: 34 additions & 0 deletions test/test__formUI_status_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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"""
Expand Down

0 comments on commit ec2c5a1

Please sign in to comment.