From 015dccc90d25a73b3730fb626dbc322be2341296 Mon Sep 17 00:00:00 2001 From: appleb_m Date: Tue, 10 Mar 2026 17:36:33 +0100 Subject: [PATCH] Tutorials: fixes to buttons, in particualr back action --- src/aare/gui/tutorials/tutorial_actions.py | 11 --- src/aare/gui/tutorials/tutorial_manager.py | 13 ++- .../gui/tutorials/tutorial_registration.py | 26 ++---- src/aare/gui/tutorials/tutorial_targets.py | 22 +++-- src/aare/gui/tutorials/tutroial_texts.py | 91 +++++++++++++++---- 5 files changed, 101 insertions(+), 62 deletions(-) diff --git a/src/aare/gui/tutorials/tutorial_actions.py b/src/aare/gui/tutorials/tutorial_actions.py index 229299dd..c9711ca8 100644 --- a/src/aare/gui/tutorials/tutorial_actions.py +++ b/src/aare/gui/tutorials/tutorial_actions.py @@ -46,15 +46,4 @@ class TutorialActionExecutor: _set_nested_value(context.state, "demo.sample_mounted", False) return - if action_id == "mark_demo_sample_selected": - sample_name = str(params.get("sample_name", "demo_sample")) - _set_nested_value(context.state, "demo.selected_sample", sample_name) - return - - if action_id == "simulate_demo_mount": - sample_name = str(params.get("sample_name", "demo_sample")) - _set_nested_value(context.state, "demo.selected_sample", sample_name) - _set_nested_value(context.state, "demo.sample_mounted", True) - return - raise ValueError(f"Unknown tutorial action: {action_id}") diff --git a/src/aare/gui/tutorials/tutorial_manager.py b/src/aare/gui/tutorials/tutorial_manager.py index e9ba22c6..cb53d5d6 100644 --- a/src/aare/gui/tutorials/tutorial_manager.py +++ b/src/aare/gui/tutorials/tutorial_manager.py @@ -218,7 +218,6 @@ class TutorialOverlay(QWidget): self.callout.setText("

".join(text_parts)) self.callout.setMaximumWidth(460) self.callout.adjustSize() - self._position_callout(rect) self._waiting_for_click = bool(view.waiting_for_target_click) @@ -234,6 +233,7 @@ class TutorialOverlay(QWidget): self.callout.show() self._position_end_button() + self._position_callout(rect) self._reposition_action_buttons() self.update() @@ -434,7 +434,7 @@ class TutorialManager(QObject): self.context.event_log.append(event) return event - def get_current_step(self) -> TutorialStep | None: + def get_current_step(self) -> TutorialStepDefinition | None: if self.current_scenario is None or self.runtime_state is None: return None @@ -533,7 +533,7 @@ class TutorialManager(QObject): self.step_changed.emit(self.current_scenario.id, step.id) self._try_auto_advance_current_step() - def _resolve_step_target(self, step: TutorialStep) -> ResolvedTutorialTarget | None: + def _resolve_step_target(self, step: TutorialStepDefinition) -> ResolvedTutorialTarget | None: if step.target is None or self.context is None: return None try: @@ -659,7 +659,12 @@ class TutorialManager(QObject): return if not self.current_scenario.allow_back: return - self._advance_to_index(self.runtime_state.active_step_index - 1) + + target_index = self.runtime_state.active_step_index - 1 + if target_index < 0: + return + + self._advance_to_index(target_index) def _on_skip_requested(self) -> None: step = self.get_current_step() diff --git a/src/aare/gui/tutorials/tutorial_registration.py b/src/aare/gui/tutorials/tutorial_registration.py index dad3a13c..c55eb7b0 100644 --- a/src/aare/gui/tutorials/tutorial_registration.py +++ b/src/aare/gui/tutorials/tutorial_registration.py @@ -49,26 +49,19 @@ def register_tutorials(window, tutorial_manager) -> None: ), TutorialStepDefinition( id="select_demo_sample", - kind=StepKind.ACTION, + kind=StepKind.INFO, title=TutorialTextRef("tutorial.manual.select_sample.title"), body=TutorialTextRef( "tutorial.manual.select_sample.body", {"sample_name": "demo_sample"}, ), target=TutorialTarget(TargetKind.WIDGET, "tell_samples"), - completion=CompletionRule( - kind=CompletionKind.STATE_MATCH, - value={"path": "demo.selected_sample", "equals": "demo_sample"}, - ), - flow=StepFlow.REQUIRE_COMPLETE_OR_SKIP, - skippable=True, - setup_actions=[ - TutorialAction("mark_demo_sample_selected", {"sample_name": "demo_sample"}), - ], + hint=TutorialTextRef("tutorial.manual.select_sample.hint"), + flow=StepFlow.NEXT_ONLY, ), TutorialStepDefinition( id="mount_demo_sample", - kind=StepKind.ACTION, + kind=StepKind.INFO, title=TutorialTextRef("tutorial.manual.mount.title"), body=TutorialTextRef( "tutorial.manual.mount.body", @@ -76,15 +69,7 @@ def register_tutorials(window, tutorial_manager) -> None: ), hint=TutorialTextRef("tutorial.manual.mount.hint"), target=TutorialTarget(TargetKind.WIDGET, "tell_samples"), - completion=CompletionRule( - kind=CompletionKind.STATE_MATCH, - value={"path": "demo.sample_mounted", "equals": True}, - ), - flow=StepFlow.REQUIRE_COMPLETE_OR_SKIP, - skippable=True, - setup_actions=[ - TutorialAction("simulate_demo_mount", {"sample_name": "demo_sample"}), - ], + flow=StepFlow.NEXT_ONLY, ), TutorialStepDefinition( id="sample_camera_intro", @@ -132,6 +117,7 @@ def register_tutorials(window, tutorial_manager) -> None: kind=StepKind.INFO, title=TutorialTextRef("tutorial.manual.raster_canvas.title"), body=TutorialTextRef("tutorial.manual.raster_canvas.body"), + hint=TutorialTextRef("tutorial.manual.raster_canvas.hint"), target=TutorialTarget(TargetKind.WIDGET, "raster_canvas"), flow=StepFlow.NEXT_ONLY, ), diff --git a/src/aare/gui/tutorials/tutorial_targets.py b/src/aare/gui/tutorials/tutorial_targets.py index 43f2afa4..e8f4064e 100644 --- a/src/aare/gui/tutorials/tutorial_targets.py +++ b/src/aare/gui/tutorials/tutorial_targets.py @@ -15,19 +15,23 @@ class MainWindowTutorialTargetResolver: target: TutorialTarget, context: TutorialContext, ) -> ResolvedTutorialTarget | None: + beamline = getattr(self.window, "beamline", None) + data_collection = getattr(self.window, "data_collection", None) + loopctr = getattr(beamline, "loopctr", None) + widget_map: dict[str, QWidget | None] = { "tell_samples": getattr(self.window, "tell_samples", None), "sample_camera": getattr(self.window, "sample_camera", None), - "beamline_controls": getattr(self.window, "beamline", None), - "beamline_loop_center": getattr(getattr(self.window, "beamline", None), "loopctr", None), - "beamline_loop_center_auto": getattr(getattr(getattr(self.window, "beamline", None), "loopctr", None), "find_tip", None), - "beamline_loop_center_box": getattr(getattr(getattr(self.window, "beamline", None), "loopctr", None), "bounding_box", None), - "raster_panel": getattr(getattr(self.window, "data_collection", None), "raster", None), - "raster_parameters": getattr(getattr(self.window, "data_collection", None), "raster", None), + "beamline_controls": beamline, + "beamline_loop_center": loopctr, + "beamline_loop_center_auto": getattr(loopctr, "find_tip", None), + "beamline_loop_center_box": getattr(loopctr, "bounding_box", None), + "raster_panel": getattr(data_collection, "raster", None), + "raster_parameters": getattr(data_collection, "raster", None), "raster_canvas": getattr(self.window, "sample_camera", None), - "rotation_parameters": getattr(getattr(self.window, "data_collection", None), "screening", None), - "simple_collection_parameters": getattr(getattr(self.window, "data_collection", None), "simple", None), - "file_path_panel": getattr(getattr(self.window, "data_collection", None), "file_path_panel", None), + "rotation_parameters": getattr(data_collection, "screening", None), + "simple_collection_parameters": getattr(data_collection, "simple", None), + "file_path_panel": getattr(data_collection, "file_path_panel", None), } widget = widget_map.get(target.target_id) diff --git a/src/aare/gui/tutorials/tutroial_texts.py b/src/aare/gui/tutorials/tutroial_texts.py index caadd138..6087ebef 100644 --- a/src/aare/gui/tutorials/tutroial_texts.py +++ b/src/aare/gui/tutorials/tutroial_texts.py @@ -3,33 +3,88 @@ MANUAL_MOUNT_TUTORIAL = { "en": { "tutorial.manual.title": "Manual workflow demo", "tutorial.manual.description": "A guided walkthrough of the manual data-collection workflow.", + "tutorial.manual.welcome.title": "Welcome", - "tutorial.manual.welcome.body": "This tutorial is a safe guided demo of the manual workflow. It is intended to teach the interface and does not need to operate real hardware.", + "tutorial.manual.welcome.body": ( + "This tutorial is a safe guided demo of the manual workflow. " + "It teaches the interface and intended workflow." + ), + "tutorial.manual.sample_list.title": "Sample list", - "tutorial.manual.sample_list.body": "This is the sample list. For this tutorial we will use the demo sample '{sample_name}'.", - "tutorial.manual.select_sample.title": "Select a sample", - "tutorial.manual.select_sample.body": "Select '{sample_name}' in the sample list. In normal use, this chooses the sample you want to work with.", - "tutorial.manual.mount.title": "Mount the sample", - "tutorial.manual.mount.body": "To mount a sample, right-click the selected sample and choose the mount action.", - "tutorial.manual.mount.hint": "In this tutorial, the mount step is simulated so you can learn the workflow safely.", + "tutorial.manual.sample_list.body": ( + "This is the sample list. For this tutorial, imagine working with " + "'{sample_name}'." + ), + + "tutorial.manual.select_sample.title": "Selecting a sample", + "tutorial.manual.select_sample.body": ( + "Normally, you begin by selecting your sample in the sample list." + ), + "tutorial.manual.select_sample.hint": ( + "In the full interactive tutorial, this step can later be validated from a real selection event." + ), + + "tutorial.manual.mount.title": "Mounting a sample", + "tutorial.manual.mount.body": ( + "To mount a sample, select it, right-click it, and choose the mount action." + ), + "tutorial.manual.mount.hint": ( + "For now this tutorial explains the workflow without requiring a real mount action." + ), + "tutorial.manual.sample_camera.title": "Sample camera", - "tutorial.manual.sample_camera.body": "This is the main sample camera view. The orange square marks the beam position. Left-clicking on the sample moves the smargon so that point is brought to the beam.", - "tutorial.manual.sample_camera.hint": "You can also use the mouse wheel here to rotate omega in 90° steps.", + "tutorial.manual.sample_camera.body": ( + "This is the main sample camera view. The orange square marks the beam position. " + "Left-clicking on the sample moves the smargon so that point is brought to the beam." + ), + "tutorial.manual.sample_camera.hint": ( + "You can also use the mouse wheel here to rotate omega in 90° steps." + ), + "tutorial.manual.beamline_controls.title": "Beamline controls", - "tutorial.manual.beamline_controls.body": "Additional controls for motion, lighting, zoom, and alignment are available in this panel.", + "tutorial.manual.beamline_controls.body": ( + "Additional controls for sample motion, lighting, zoom, and alignment are available here." + ), + "tutorial.manual.auto_loop_center.title": "Auto loop centering", - "tutorial.manual.auto_loop_center.body": "This button runs automatic loop centering. It can help bring the sample into position automatically.", - "tutorial.manual.ml_box.title": "ML bounding box", - "tutorial.manual.ml_box.body": "This button uses the ML box detection to generate a raster region automatically.", + "tutorial.manual.auto_loop_center.body": ( + "This button runs automatic loop centering and can help position the sample automatically." + ), + + "tutorial.manual.ml_box.title": "ML box for raster generation", + "tutorial.manual.ml_box.body": ( + "This button uses ML box detection to generate a raster region automatically." + ), + "tutorial.manual.raster_parameters.title": "Raster parameters", - "tutorial.manual.raster_parameters.body": "Here you can change raster collection parameters such as exposure, transmission, grid spacing, and other scan settings.", + "tutorial.manual.raster_parameters.body": ( + "Here you can change raster collection parameters such as exposure, transmission, " + "grid size, and related scan settings." + ), + "tutorial.manual.raster_canvas.title": "Drawing a raster", - "tutorial.manual.raster_canvas.body": "You can draw a raster grid in the sample camera view using the mouse. This lets you define the region to screen.", + "tutorial.manual.raster_canvas.body": ( + "You can draw a raster grid in the sample camera view using the mouse." + ), + "tutorial.manual.raster_canvas.hint": ( + "You mentioned using right-click for raster drawing, so this is a good place to teach that gesture explicitly." + ), + "tutorial.manual.rotation_parameters.title": "Rotation parameters", - "tutorial.manual.rotation_parameters.body": "This panel contains rotation scan parameters. You can adjust values, reload parameters from the database, or override them with user-defined values.", + "tutorial.manual.rotation_parameters.body": ( + "This panel contains rotation scan parameters. You can change values, reload parameters " + "from the database, or override them with user-defined values." + ), + "tutorial.manual.simple_collection.title": "Simple data collection", - "tutorial.manual.simple_collection.body": "This panel contains simplified collection parameters for quick data collection setup.", + "tutorial.manual.simple_collection.body": ( + "This panel contains simplified collection parameters for quick data collection setup." + ), + "tutorial.manual.file_names.title": "File names and paths", - "tutorial.manual.file_names.body": "Here you can review the file naming and output path settings before collection.", + "tutorial.manual.file_names.body": ( + "Here you can review output paths and file naming before collection. " + "This is also a good place to explain how filenames are built." + ), } } \ No newline at end of file