Suppose you have a main window and open a dialog to do some kind of specific operation on your data. The dialog has interactive elements such as QSpinBoxes and QComboBoxes that determine properties of the operation. For convenience, the default (or most recent) values are stored in the project data, and are updated according to the values held by the interactive elements whenever the dialog is closed with acceptance.
Since I use Qt Designer to create the interfaces, my approach is to make use of the accessibleName
attribute (I've yet to figure out what it is actually intended for) and give all the interactive elements a unique accessibleName
. I then store the default values in a dict
in the main window, from where they are passed on to the dialog when it is opened. During the dialog's initialization, a crawler scans through the dialog's layer to find all the elements with an accessibleName
and sets their values according to the settings dict
.
Here is some code to show how I handle that situation at the moment:
from PyQt5.QtWidgets import (
QMainWindow, QDialog, QLayout, QGroupBox, QDoubleSpinBox, QSpinBox,
QTimeEdit, QCheckBox, QRadioButton, QComboBox
)
from PyQt5.uic import loadUi
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
loadUi('main_window.ui', self)
self._operation_settings = {
'spinbox': 1,
'combobox': {
'names': ['item1', 'item2', 'item3'],
'selected': 0,
},
'groupbox': True,
}
def _open_operation_dialog(self):
dialog = OperationsDialog(self._operation_settings)
dialog.exec_()
class OperationsDialog(QDialog):
def __init__(self, settings):
super().__init__()
loadUi('operations_dialog.ui', self)
self._settings = settings
self._interactive_elements = \
collect_interactive_elements(self.layout())
self._setup_interactive_elements()
def _setup_interactive_elements(self) -> None:
elements = self._interactive_elements
for key, value in self._settings.items():
if isinstance(elements[key], (QDoubleSpinBox, QSpinBox)):
elements[key].setValue(value)
elif isinstance(elements[key], (QCheckBox, QRadioButton)):
elements[key].setChecked(value)
elif (
isinstance(elements[key], QGroupBox)
and elements[key].isCheckable()
):
elements[key].toggled.connect(self._toggle_group_box)
elements[key].setChecked(value)
elif isinstance(elements[key], QComboBox):
elements[key].addItems(value['names'])
elements[key].setCurrentIndex(value['selected'])
elif isinstance(elements[key], QTimeEdit):
elements[key].setTime(value)
def accept(self) -> None:
self._update_settings()
super().accept()
def _update_settings(self) -> None:
for key, element in self._interactive_elements.items():
if isinstance(element, (QDoubleSpinBox, QSpinBox)):
self._settings[key] = element.value()
elif isinstance(element, (QGroupBox, QCheckBox, QRadioButton)):
self._settings[key] = element.isChecked()
elif isinstance(element, QComboBox):
self._settings[key]['selected'] = element.currentIndex()
elif isinstance(element, QTimeEdit):
self._settings[key] = element.time()
def collect_interactive_elements(layout: QLayout) -> dict:
elements = {}
crawl(layout, elements)
return elements
def crawl(layout: QLayout, elements: dict) -> None:
for i in range(layout.count()):
child = layout.itemAt(i)
if child.widget() is not None:
widget = child.widget()
if widget.accessibleName():
elements[widget.accessibleName()] = widget
if isinstance(widget, QGroupBox):
crawl(widget.layout(), elements)
elif isinstance(widget, QGroupBox):
crawl(widget.layout(), elements)
elif child.layout() is not None:
crawl(child.layout(), elements)
Is this an okay way to do it? How is something like this usually done?
how to question
generally indicates the code isn't working as expected. If the code is working as expected please change the title of the question to indicate what the code is doing. \$\endgroup\$