0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 11:11:49 +02:00

fix: (#612) fix additional MD form

makes sure the form is validated on any changes of the additional
metadata table model so that they are propagated to the scan control
widget even when nothing is entered in the standard form
This commit is contained in:
2025-05-16 14:23:49 +02:00
committed by Jan Wyzula
parent 7d7a88669f
commit d915d2f507
3 changed files with 55 additions and 10 deletions

View File

@ -53,6 +53,7 @@ class DictBackedTableModel(QAbstractTableModel):
if value in self._disallowed_keys or value in self._other_keys(index.row()): if value in self._disallowed_keys or value in self._other_keys(index.row()):
return False return False
self._data[index.row()][index.column()] = str(value) self._data[index.row()][index.column()] = str(value)
self.dataChanged.emit(index, index)
return True return True
return False return False
@ -109,6 +110,7 @@ class DictBackedTableModel(QAbstractTableModel):
class DictBackedTable(QWidget): class DictBackedTable(QWidget):
delete_rows = Signal(list) delete_rows = Signal(list)
data_updated = Signal()
def __init__(self, initial_data: list[list[str]]): def __init__(self, initial_data: list[list[str]]):
"""Widget which uses a DictBackedTableModel to display an editable table """Widget which uses a DictBackedTableModel to display an editable table
@ -141,6 +143,11 @@ class DictBackedTable(QWidget):
self._add_button.clicked.connect(self._table_model.add_row) self._add_button.clicked.connect(self._table_model.add_row)
self._remove_button.clicked.connect(self.delete_selected_rows) self._remove_button.clicked.connect(self.delete_selected_rows)
self.delete_rows.connect(self._table_model.delete_rows) self.delete_rows.connect(self._table_model.delete_rows)
self._table_model.dataChanged.connect(self._emit_data_updated)
def _emit_data_updated(self, *args, **kwargs):
"""Just to swallow the args"""
self.data_updated.emit()
def delete_selected_rows(self): def delete_selected_rows(self):
"""Delete rows which are part of the selection model""" """Delete rows which are part of the selection model"""

View File

@ -43,6 +43,7 @@ class ScanMetadata(PydanticModelForm):
self._additional_metadata = DictBackedTable(initial_extras or []) self._additional_metadata = DictBackedTable(initial_extras or [])
self._scan_name = scan_name or "" self._scan_name = scan_name or ""
self._md_schema = get_metadata_schema_for_scan(self._scan_name) self._md_schema = get_metadata_schema_for_scan(self._scan_name)
self._additional_metadata.data_updated.connect(self.validate_form)
super().__init__(parent=parent, metadata_model=self._md_schema, client=client, **kwargs) super().__init__(parent=parent, metadata_model=self._md_schema, client=client, **kwargs)

View File

@ -1,9 +1,11 @@
# pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
from unittest.mock import MagicMock from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest import pytest
from bec_lib.endpoints import MessageEndpoints from bec_lib.endpoints import MessageEndpoints
from bec_lib.messages import AvailableResourceMessage, ScanQueueHistoryMessage, ScanQueueMessage from bec_lib.messages import AvailableResourceMessage, ScanQueueHistoryMessage, ScanQueueMessage
from qtpy.QtCore import QModelIndex, QPoint, Qt
from bec_widgets.utils.forms_from_types.items import StrMetadataField from bec_widgets.utils.forms_from_types.items import StrMetadataField
from bec_widgets.utils.widget_io import WidgetIO from bec_widgets.utils.widget_io import WidgetIO
@ -540,6 +542,29 @@ def test_get_scan_parameters_from_redis(scan_control, mocked_client):
assert kwargs == {"steps": 10, "relative": False, "exp_time": 2.0, "burst_at_each_point": 1} assert kwargs == {"steps": 10, "relative": False, "exp_time": 2.0, "burst_at_each_point": 1}
TEST_MD = {"sample_name": "Test Sample", "test key 1": "test value 1", "test key 2": "test value 2"}
TEST_TABLE_ENTRY = [["test key 1", "test value 1"], ["test key 2", "test value 2"]]
def test_scan_metadata_is_updated_even_without_default_form_changes(
scan_control: ScanControl, qtbot
):
assert scan_control._metadata_form._scan_name == "line_scan"
scan_control.comboBox_scan_selection.setCurrentText("grid_scan")
assert scan_control._metadata_form._scan_name == "grid_scan"
scan_control._metadata_form._additional_metadata._add_button.click()
qtbot.wait(100)
table_model = scan_control._metadata_form._additional_metadata._table_model
model_key = table_model.index(0, 0, QModelIndex())
table_model.setData(model_key, "test key 1", Qt.EditRole)
model_value = model_key.siblingAtColumn(1)
table_model.setData(model_value, "test value 1", Qt.EditRole)
assert scan_control._metadata_form._additional_metadata.dump_dict() == {
"test key 1": "test value 1"
}
assert scan_control._scan_metadata == {"sample_name": "", "test key 1": "test value 1"}
def test_scan_metadata_is_connected(scan_control): def test_scan_metadata_is_connected(scan_control):
assert scan_control._metadata_form._scan_name == "line_scan" assert scan_control._metadata_form._scan_name == "line_scan"
scan_control.comboBox_scan_selection.setCurrentText("grid_scan") scan_control.comboBox_scan_selection.setCurrentText("grid_scan")
@ -548,16 +573,28 @@ def test_scan_metadata_is_connected(scan_control):
assert isinstance(sample_name, StrMetadataField) assert isinstance(sample_name, StrMetadataField)
sample_name._main_widget.setText("Test Sample") sample_name._main_widget.setText("Test Sample")
scan_control._metadata_form._additional_metadata._table_model._data = [ scan_control._metadata_form._additional_metadata._table_model._data = TEST_TABLE_ENTRY
["test key 1", "test value 1"],
["test key 2", "test value 2"],
]
scan_control._metadata_form.validate_form() scan_control._metadata_form.validate_form()
assert scan_control._scan_metadata == { assert scan_control._scan_metadata == TEST_MD
"sample_name": "Test Sample",
"test key 1": "test value 1",
"test key 2": "test value 2", def test_scan_metadata_is_passed_to_scan_function(scan_control: ScanControl):
} scan_control.comboBox_scan_selection.setCurrentText("grid_scan")
sample_name = scan_control._metadata_form._form_grid.layout().itemAtPosition(0, 1).widget()
sample_name._main_widget.setText("Test Sample")
scan_control._metadata_form._additional_metadata._table_model._data = TEST_TABLE_ENTRY
scan_control._metadata_form.validate_form()
assert scan_control._scan_metadata == TEST_MD
scans = SimpleNamespace(grid_scan=MagicMock())
with (
patch.object(scan_control, "scans", scans),
patch.object(scan_control, "get_scan_parameters", lambda: ((), {})),
):
scan_control.run_scan()
scans.grid_scan.assert_called_once_with(metadata=TEST_MD)
def test_restore_parameters_with_fewer_arg_bundles(scan_control, qtbot): def test_restore_parameters_with_fewer_arg_bundles(scan_control, qtbot):