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