diff --git a/EasyReflectometryApp/Backends/Py/sample.py b/EasyReflectometryApp/Backends/Py/sample.py index 4f432219..9945a011 100644 --- a/EasyReflectometryApp/Backends/Py/sample.py +++ b/EasyReflectometryApp/Backends/Py/sample.py @@ -305,6 +305,13 @@ def setCurrentModelIndex(self, new_value: int) -> None: if self._project_lib.current_model_index != new_value: self._project_lib.current_model_index = new_value self.modelsIndexChanged.emit() + # A model switch starts from the first assembly and layer of the new model + # (the lib does the same). The layer table is cached, so the assembly/layer + # side has to be refreshed too, or the layer editor keeps showing the + # previous model's layers (#407). + self._project_lib.current_assembly_index = 0 + self._project_lib.current_layer_index = 0 + self._refreshCurrentAssemblySelectionState() self.assembliesTableChanged.emit() self.externalRefreshPlot.emit() self.externalSampleChanged.emit() diff --git a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/MultiLayer.qml b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/MultiLayer.qml index 23c199c1..ccb9a056 100644 --- a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/MultiLayer.qml +++ b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/MultiLayer.qml @@ -58,21 +58,21 @@ EaElements.GroupColumn { EaComponents.TableViewComboBox{ readonly property int rowIndex: index - property string currentAssemblyName: Globals.BackendWrapper.sampleCurrentAssemblyName + // Track the row's own material so the combo re-syncs whenever the layer + // list is refreshed (e.g. a model switch), even if the assembly name and + // the materials list are unchanged. + readonly property string layerMaterial: { + const layer = Globals.BackendWrapper.sampleLayers[index] + return layer ? String(layer.material) : "" + } horizontalAlignment: Text.AlignLeft model: Globals.BackendWrapper.sampleMaterialNames onActivated: function(comboIndex) { Globals.BackendWrapper.sampleSetLayerMaterialAtIndex(rowIndex, comboIndex) } - onModelChanged: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].material) - } - onCurrentAssemblyNameChanged: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].material) - } - Component.onCompleted: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].material) - } + onModelChanged: currentIndex = indexOfValue(layerMaterial) + onLayerMaterialChanged: currentIndex = indexOfValue(layerMaterial) + Component.onCompleted: currentIndex = indexOfValue(layerMaterial) } EaComponents.TableViewTextInput { diff --git a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/SurfactantLayer.qml b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/SurfactantLayer.qml index 1f3f26d3..62911131 100644 --- a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/SurfactantLayer.qml +++ b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/Assemblies/SurfactantLayer.qml @@ -97,21 +97,21 @@ EaElements.GroupColumn { EaComponents.TableViewComboBox{ readonly property int rowIndex: index - property string currentAssemblyName: Globals.BackendWrapper.sampleCurrentAssemblyName + // Track the row's own solvent so the combo re-syncs whenever the layer + // list is refreshed (e.g. a model switch), even if the assembly name and + // the materials list are unchanged. + readonly property string layerSolvent: { + const layer = Globals.BackendWrapper.sampleLayers[index] + return layer ? String(layer.solvent) : "" + } horizontalAlignment: Text.AlignLeft model: Globals.BackendWrapper.sampleMaterialNames onActivated: function(comboIndex) { Globals.BackendWrapper.sampleSetLayerSolventAtIndex(rowIndex, comboIndex) } - onModelChanged: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].solvent) - } - onCurrentAssemblyNameChanged: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].solvent) - } - Component.onCompleted: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleLayers[index].solvent) - } + onModelChanged: currentIndex = indexOfValue(layerSolvent) + onLayerSolventChanged: currentIndex = indexOfValue(layerSolvent) + Component.onCompleted: currentIndex = indexOfValue(layerSolvent) } mouseArea.onPressed: { if (Globals.BackendWrapper.sampleCurrentLayerIndex !== index) { diff --git a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelEditor.qml b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelEditor.qml index c4df32ba..6f752ff0 100644 --- a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelEditor.qml +++ b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelEditor.qml @@ -73,12 +73,18 @@ EaElements.GroupBox { property var fullModel: ["Multi-layer", "Repeating Multi-layer", "Surfactant Layer"] property var limitedModel: ["Multi-layer", "Repeating Multi-layer"] model: index === 0 || index === assembliesView.model - 1 ? limitedModel : fullModel + // Track the row's own type so the combo re-syncs when the assemblies + // list is refreshed (e.g. a model switch reusing the same rows). + readonly property string assemblyType: { + const assembly = Globals.BackendWrapper.sampleAssemblies[index] + return assembly ? String(assembly.type) : "" + } onActivated: function(comboIndex) { Globals.BackendWrapper.sampleSetAssemblyTypeAtIndex(rowIndex, model[comboIndex]) } - Component.onCompleted: { - currentIndex = indexOfValue(Globals.BackendWrapper.sampleAssemblies[index].type) - } + onModelChanged: currentIndex = indexOfValue(assemblyType) + onAssemblyTypeChanged: currentIndex = indexOfValue(assemblyType) + Component.onCompleted: currentIndex = indexOfValue(assemblyType) } EaComponents.TableViewButton { diff --git a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelSelector.qml b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelSelector.qml index 2f5acdcf..8304d971 100644 --- a/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelSelector.qml +++ b/EasyReflectometryApp/Gui/Pages/Sample/Sidebar/Basic/Groups/ModelSelector.qml @@ -54,6 +54,9 @@ EaElements.GroupBox { EaComponents.TableViewTextInput { horizontalAlignment: Text.AlignLeft text: Globals.BackendWrapper.sampleModels[index].label + // Commit while typing so "Model editor: " follows the edit + // instead of waiting for Enter or focus loss (#407). + onTextEdited: Globals.BackendWrapper.sampleSetModelNameAtIndex(index, text) onEditingFinished: Globals.BackendWrapper.sampleSetModelNameAtIndex(index, text) } diff --git a/tests/test_py_sample.py b/tests/test_py_sample.py index bf62dab8..f7cf99e2 100644 --- a/tests/test_py_sample.py +++ b/tests/test_py_sample.py @@ -75,3 +75,36 @@ def test_structure_cache_cleared_and_signal_emitted_on_invalidation(qcore_applic assert emitted == [True] assert len(backend.structure) == 4 assert backend.structureTotalThickness == 40.0 + + +def test_set_current_model_index_refreshes_layers_and_selection(qcore_application): + materials = make_material_collection(make_material('Air'), make_material('D2O'), make_material('Si')) + first = make_sample( + make_assembly(name='Superphase', layers=[make_layer(name='Air Layer', material=materials[0])]), + make_assembly(name='Substrate', layers=[make_layer(name='Si Layer', material=materials[2])]), + ) + second = make_sample( + make_assembly(name='Superphase', layers=[make_layer(name='D2O Layer', material=materials[1])]), + make_assembly(name='Substrate', layers=[make_layer(name='Si Layer', material=materials[2])]), + ) + project = make_project( + materials=materials, + models=make_model_collection(make_model(name='M1', sample=first), make_model(name='M2', sample=second)), + ) + project.current_model_index = 0 + project.current_assembly_index = 1 + + backend = Sample(project) + assert [layer['material'] for layer in backend.layers] == ['Si'] + + fired = [] + for name in ('assembliesIndexChanged', 'layersIndexChanged', 'layersChange'): + getattr(backend, name).connect(lambda name=name: fired.append(name)) + + backend.setCurrentModelIndex(1) + + assert backend.currentModelName == 'M2' + assert backend.currentAssemblyIndex == 0 + assert backend.currentLayerIndex == 0 + assert [layer['material'] for layer in backend.layers] == ['D2O'] + assert set(fired) == {'assembliesIndexChanged', 'layersIndexChanged', 'layersChange'}