diff --git a/bec_widgets/qt_utils/widget_hierarchy.py b/bec_widgets/qt_utils/widget_hierarchy.py index 677e3956..a2471a34 100644 --- a/bec_widgets/qt_utils/widget_hierarchy.py +++ b/bec_widgets/qt_utils/widget_hierarchy.py @@ -119,7 +119,12 @@ def print_widget_hierarchy( def export_config_to_dict( - widget, config=None, indent=0, grab_values: bool = False, print_hierarchy: bool = False + widget, + config=None, + indent=0, + grab_values: bool = False, + print_hierarchy: bool = False, + save_all: bool = True, ) -> dict: """ Export the widget hierarchy to a dictionary. @@ -129,21 +134,34 @@ def export_config_to_dict( indent(int,optional): Level of indentation. grab_values(bool,optional): Whether to grab the values of the widgets. print_hierarchy(bool,optional): Whether to print the hierarchy to the console. + save_all(bool,optional): Whether to save all widgets or only those with values. Returns: config(dict): Dictionary containing the widget hierarchy. """ if config is None: config = {} widget_info = f"{widget.__class__.__name__} ({widget.objectName()})" - config[widget_info] = {} + if grab_values: value = get_value(widget) - if value is not None: - config[widget_info]["value"] = value + if value is not None or save_all: + if widget_info not in config: + config[widget_info] = {} + if value is not None: + config[widget_info]["value"] = value + if print_hierarchy: print_widget_hierarchy(widget, indent, grab_values) + for child in widget.children(): - export_config_to_dict(child, config, indent + 1, grab_values, print_hierarchy) + child_config = export_config_to_dict( + child, None, indent + 1, grab_values, print_hierarchy, save_all + ) + if child_config or save_all: + if widget_info not in config: + config[widget_info] = {} + config[widget_info].update(child_config) + return config @@ -208,7 +226,11 @@ if __name__ == "__main__": import_config_from_dict(main_widget, new_config_dict, set_values=True) print(30 * "#") config_dict_new = export_config_to_dict(main_widget, grab_values=True, print_hierarchy=True) + config_dict_new_reduced = export_config_to_dict( + main_widget, grab_values=True, print_hierarchy=True, save_all=False + ) print(30 * "#") - print(f"Config dict new: {config_dict_new}") + print(f"Config dict new FULL: {config_dict_new}") + print(f"Config dict new REDUCED: {config_dict_new_reduced}") app.exec() diff --git a/tests/test_hierarchy.py b/tests/test_hierarchy.py new file mode 100644 index 00000000..a5569ad3 --- /dev/null +++ b/tests/test_hierarchy.py @@ -0,0 +1,99 @@ +import pytest +from PyQt5.QtWidgets import ( + QWidget, + QVBoxLayout, + QLineEdit, + QComboBox, + QTableWidget, + QSpinBox, +) + +from bec_widgets.qt_utils.widget_hierarchy import ( + export_config_to_dict, + import_config_from_dict, +) + + +@pytest.fixture(scope="function") +def example_widget(qtbot): + # Create a widget with a few child widgets + main_widget = QWidget() + layout = QVBoxLayout(main_widget) + line_edit = QLineEdit(main_widget) + combo_box = QComboBox(main_widget) + table_widget = QTableWidget(2, 2, main_widget) + spin_box = QSpinBox(main_widget) + layout.addWidget(line_edit) + layout.addWidget(combo_box) + layout.addWidget(table_widget) + layout.addWidget(spin_box) + + # Add text items to the combo box + combo_box.addItems(["Option 1", "Option 2", "Option 3"]) + + qtbot.addWidget(main_widget) + qtbot.waitExposed(main_widget) + yield main_widget + + +def test_export_import_config(example_widget): + initial_config = { + "QWidget ()": { + "QLineEdit ()": {"value": "New Text"}, + "QComboBox ()": {"value": 1}, + "QTableWidget ()": {"value": [["a", "b"], ["c", "d"]]}, + "QSpinBox ()": {"value": 10}, + } + } + import_config_from_dict(example_widget, initial_config, set_values=True) + + exported_config_full = export_config_to_dict(example_widget, grab_values=True) + exported_config_reduced = export_config_to_dict( + example_widget, grab_values=True, save_all=False + ) + + expected_full = { + "QWidget ()": { + "QVBoxLayout ()": {}, + "QLineEdit ()": {"value": "New Text", "QObject ()": {}}, + "QComboBox ()": {"value": 1, "QStandardItemModel ()": {}}, + "QTableWidget ()": { + "value": [["a", "b"], ["c", "d"]], + "QWidget (qt_scrollarea_viewport)": {}, + "QStyledItemDelegate ()": {}, + "QHeaderView ()": { + "QWidget (qt_scrollarea_viewport)": {}, + "QWidget (qt_scrollarea_hcontainer)": { + "QScrollBar ()": {}, + "QBoxLayout ()": {}, + }, + "QWidget (qt_scrollarea_vcontainer)": { + "QScrollBar ()": {}, + "QBoxLayout ()": {}, + }, + "QItemSelectionModel ()": {}, + }, + "QAbstractButton ()": {}, + "QAbstractTableModel ()": {}, + "QItemSelectionModel ()": {}, + "QWidget (qt_scrollarea_hcontainer)": {"QScrollBar ()": {}, "QBoxLayout ()": {}}, + "QWidget (qt_scrollarea_vcontainer)": {"QScrollBar ()": {}, "QBoxLayout ()": {}}, + }, + "QSpinBox ()": { + "value": 10, + "QLineEdit (qt_spinbox_lineedit)": {"value": "10", "QObject ()": {}}, + "QValidator (qt_spinboxvalidator)": {}, + }, + } + } + expected_reduced = { + "QWidget ()": { + "QLineEdit ()": {"value": "New Text"}, + "QComboBox ()": {"value": 1}, + "QTableWidget ()": {"value": [["a", "b"], ["c", "d"]]}, + "QSpinBox ()": {"value": 10, "QLineEdit (qt_spinbox_lineedit)": {"value": "10"}}, + } + } + + assert exported_config_full == expected_full + assert exported_config_reduced == expected_reduced