Host User Guide Screenshot Automation
Historical. This plan was partly built: the qtcalibrate screenshot hooks exist, the
qtcalibrate_docshotstarget and the annotation renderer do not. The screenshot capture as it exists is described in host/docs/README.md and the application READMEs it links. Do not quote this plan as current.
Purpose
This document describes a repeatable workflow for generating host application
screenshots and annotated documentation images. It uses qtcalibrate as the
pilot application because its user guide is currently thin, its workflow is
visual, and its Qt widget tree already exposes stable object names for most
documentation targets.
The design is intended to drive a later implementation plan. It does not change application behavior by itself.
Problem
Host application screenshots are currently captured and annotated by hand. That has several costs:
- screenshots drift as UI controls, labels, and workflows change;
- hardware-dependent states are difficult to reproduce;
- annotations must be manually redrawn when layout changes;
- the same work must be repeated for every host application;
- documentation reviews cannot easily verify that images match the current UI.
The documentation pipeline needs a deterministic way to put each application into representative states, capture stable images, and generate annotations from the real UI geometry.
Goals
- Generate key screenshots for
qtcalibratewithout manual screen capture. - Use realistic sample data while avoiding private device identifiers, serial numbers, or local paths.
- Support both clean screenshots and annotated derivatives.
- Keep screenshot definitions close to the application or host documentation tree so they can be reviewed with UI changes.
- Make the pilot reusable for other Qt host tools such as
qtmonitor,qtprogram,sensorviz, andbtviz. - Allow focused local execution through CMake.
Non-Goals
- Replacing ordinary GUI tests or adding full workflow coverage.
- Simulating USB transport with bit-for-bit fidelity.
- Generating prose documentation automatically.
- Requiring physical tag hardware for the documentation image set.
- Changing
qtcalibrateruntime behavior in normal user mode.
Current State
The user-facing Qt Calibrate guide lives at
host/docs/src/apps/qtcalibrate.md and currently contains a single screenshot
placeholder.
The application lives at host/applications/qtcalibrate/. Its main widget file
already gives useful object names to the controls that should appear in the
guide, including:
tabWidget,calTab,orientationTab, andlogTab;graphWidgetfor the magnetometer collection view;qualityLabeland calibration constant labels;attachButton,detachButton,streamCheckBox;startButton,stopButton,clearButton,saveButton,loadButton;logTextEdit,logsaveButton,logclearButton,loglevelBox;tagWidgetandattitudeWidgetfor the shared QML orientation displays.
The main implementation currently calls Attach() during MainWindow
construction. When no compatible base is present, the application raises a
modal warning. This is correct for a user session but blocks deterministic
documentation capture unless the application has a hardware-free documentation
mode.
Proposed Architecture
Add a Qt-native documentation screenshot pipeline with three layers:
- A documentation/demo state provider for each application.
- A screenshot runner that drives the application into named states.
- An annotation renderer that draws callouts using Qt object geometry.
For qtcalibrate, the pilot should add a hardware-free documentation mode that
skips automatic USB attachment, enables representative UI states, and feeds
synthetic calibration/orientation data into the same display widgets used by
normal runtime code.
The first implementation may live as a dedicated executable target,
qtcalibrate_docshots, rather than as a user-visible mode in the app bundle.
That keeps production launch behavior unchanged and avoids exposing
documentation-only command-line flags to users. If sharing the normal binary is
more practical, the equivalent command-line shape should be hidden and explicit:
qtcalibrate --doc-mode --doc-screenshots host/docs/src/images
State Provider
The state provider should be small and application-owned. For qtcalibrate,
it should provide methods equivalent to:
- initialize as disconnected;
- initialize as attached and idle;
- enable streaming;
- start active calibration collection;
- stop collection and show completed calibration constants;
- switch to the Orientation tab with representative compass and attitude data;
- switch to the Log tab with representative informational and warning text.
The provider should avoid mocking the entire Tag API unless the later
implementation needs that seam for broader tests. The documentation objective is
to set visible UI state, not to validate USB behavior.
Synthetic sample data should be deterministic. A fixed seed or checked-in data fixture is preferred so screenshots are byte-stable except when rendering changes. The magnetometer samples should form a plausible sphere with realistic hard-iron offset, soft-iron distortion, and enough coverage gaps to make the quality metrics meaningful.
Captured fixtures should be preferred for final documentation screenshots once
the capture path exists. qtcalibrate sample capture starts when the user
presses Start and ends when collection stops. The fixture should contain the
calibration-window stream only: received magnetometer samples, paired
accelerometer samples when present, capture timestamps, and fitted calibration
summary data. Orientation-window replay can be added as a separate fixture path.
The replay path should be runnable without a USB tag:
qtcalibrate --replay-capture host/docs/fixtures/qtcalibrate/good-sphere-v1.json
Milestone screenshots should use the same fixture and freeze the Calibrate tab after feeding a fixed percentage of samples:
qtcalibrate --replay-capture host/docs/fixtures/qtcalibrate/good-sphere-v1.json --replay-percent 25
The initial documentation set should include the start/ready state, 0 percent collection, 25 percent collection, 50 percent collection, and 100 percent results.
The app-level capture mode should generate the replay milestone images without external desktop screenshot tools:
qtcalibrate --capture-startup-screenshot
qtcalibrate \
--replay-capture host/docs/fixtures/qtcalibrate/good-sphere-v1.json \
--capture-replay-screenshots
qtcalibrate \
--replay-capture host/docs/fixtures/qtcalibrate/good-sphere-v1.json \
--capture-orientation-screenshot
The default output directory is host/docs/src/images/. Orientation
screenshots should use a fixed documentation pose by default rather than the
final pose from the replay fixture; use --orientation-pose when the
illustration needs a specific heading, pitch, or roll.
Screenshot Runner
The runner should use Qt mechanisms instead of desktop screenshot tools:
QTestor direct Qt calls to select tabs and invoke controls;QObject::findChild()with stable object names for locating targets;QWidget::grab()for whole-window or panel captures;- fixed window geometry for repeatability;
- a fixed light theme and font policy where possible.
The runner should write images under host/docs/src/images/ using the existing
user-guide naming convention. The first qtcalibrate image set should be:
| File | State | Purpose |
|---|---|---|
qtcalibrate-startup.png |
App open, disconnected | Introduce the window and connection area. |
qtcalibrate-attached.png |
Attached and idle | Show the ready state before streaming. |
qtcalibrate-streaming.png |
Stream enabled | Show the controls available during live sample polling. |
qtcalibrate-collection-000.png |
Replay milestone 0 percent | Show collection just after pressing Start. |
qtcalibrate-collection-025.png |
Replay milestone 25 percent | Show early sphere coverage. |
qtcalibrate-collection-050.png |
Replay milestone 50 percent | Show partial sphere coverage and intermediate fit. |
qtcalibrate-collection.png |
Active calibration | Show the magnetometer point cloud and quality metrics. |
qtcalibrate-collection-100.png |
Replay milestone 100 percent | Show completed replayed collection. |
qtcalibrate-results.png |
Collection stopped | Show calibration constants and completed fit quality. |
qtcalibrate-orientation.png |
Orientation tab | Show compass and attitude preview after calibration. |
qtcalibrate-log.png |
Log tab | Show log level, save/clear controls, and example messages. |
qtcalibrate-file-menu.png |
File menu open | Show file actions if the guide documents them. |
The first pass should prioritize the collection, results, orientation, and log screens because those carry the most explanatory value.
Annotation Manifest
Annotations should be data-driven. A manifest file should identify the source image state, target widget object names, labels, and optional placement hints.
Example:
screens:
- image: qtcalibrate-collection.png
state: collection
annotations:
- target: graphWidget
label: Magnetometer sample cloud
- target: qualityLabel
label: Coverage and fit quality
- target: stopButton
label: Stop collection when coverage is good
The annotation renderer should resolve each target with findChild(), map
the widget rectangle into screenshot coordinates, and draw a consistent callout
onto a copy of the clean screenshot. The renderer should preserve clean images
and write annotated derivatives with a stable suffix, for example:
qtcalibrate-collection.png;qtcalibrate-collection-annotated.png.
This keeps guide authors free to choose whether a page needs a clean or annotated figure.
Documentation Integration
The generated images should support a rewrite of
host/docs/src/apps/qtcalibrate.md around the actual workflow:
- Before You Start.
- Connect To A Tag.
- Stream Calibration Samples.
- Collect A Full Magnetometer Sphere.
- Check Fit Quality.
- Save Or Read Calibration Constants.
- Verify Orientation.
- Troubleshooting.
The guide should reference generated images with ordinary Markdown paths, not special build-time substitutions. Image generation should be an explicit developer task, while MkDocs should continue to consume already-generated checked-in images.
CMake Integration
Add a focused CMake target for the pilot:
cmake --build <build-dir> --target qtcalibrate_docshots
The target should:
- build the app and screenshot runner;
- create the output directory if needed;
- run the screenshot runner with deterministic settings;
- fail if required screenshots cannot be produced;
- leave generated images in
host/docs/src/images/for review.
The target should not be part of the default build. It is a maintainer tool, not a package-build requirement.
Headless And CI Notes
Pure offscreen Qt rendering may be enough for widget-only screenshots, but
qtcalibrate embeds QQuickWidget compass and attitude views. Those are more
sensitive to graphics backend differences.
The implementation should test these modes in order:
- Native local rendering for developer updates.
- Linux CI under Xvfb with a software Qt Quick backend.
QT_QPA_PLATFORM=offscreenonly if theQQuickWidgetcaptures are proven nonblank and stable.
The runner should include a simple blank-image guard for QML-heavy captures so CI fails when the orientation widgets render as empty white or black panels.
Review And Stability Rules
- Screenshot updates should be reviewed like source changes.
- Fixture data should be deterministic and free of private identifiers.
- The clean screenshot should always be generated before annotations.
- Object names used by annotation manifests become documentation contracts. Renaming those widgets should update the manifest in the same change.
- Documentation-only screenshot refreshes should not change application logic.
Risks
| Risk | Mitigation |
|---|---|
| Demo state drifts from real tag behavior. | Keep demo state limited to visible UI state and use application-owned methods where practical. |
| QML orientation views render inconsistently in CI. | Use native or Xvfb rendering, software Qt Quick backend, and blank-image checks. |
| Annotations become visually noisy. | Generate clean and annotated variants; use annotations only where they clarify workflow. |
| Widget renames break screenshots. | Treat object names in manifests as reviewed contracts. |
The pilot becomes too qtcalibrate-specific. |
Keep runner and manifest concepts generic, with only the state provider app-specific. |
Implementation Phases
Phase 1: Pilot Design And Harness
- Add a
qtcalibrate_docshotstarget. - Add a documentation state provider for
qtcalibrate. - Capture clean screenshots for collection, results, orientation, and log tabs.
- Add deterministic sample data or replay a captured calibration sample fixture.
Phase 2: Annotation Renderer
- Add a manifest for
qtcalibrateannotations. - Render annotated variants from real widget geometry.
- Add blank-image and missing-target validation.
Phase 3: User Guide Rewrite
- Rewrite
host/docs/src/apps/qtcalibrate.mdaround the generated image set. - Add troubleshooting guidance for noisy data, failed fits, orientation mistakes, and connection loss.
- Build the host user guide with the generated images.
Phase 4: Generalize
- Extract shared runner or manifest utilities if a second application needs them.
- Pilot the same pattern on
qtmonitororqtprogram. - Decide whether screenshot targets should remain per-app or move into a shared host documentation tooling directory.
Open Questions
- Should generated images be checked into the repository, or produced only in release/documentation jobs?
- Should the documentation state provider be compiled into the production application behind an internal flag, or live only in a separate docshots executable?
- Should annotations use a YAML manifest, JSON, or a small C++/Qt resource file to avoid adding another parser dependency?
- What minimum set of screenshots should block documentation review for a UI change?
- Should the pipeline eventually support animated captures for workflows that are hard to explain in still images?