Problem
All Area Detector viewer toggle states — colormap, autoscale, log scale, crosshair, threshold, freeze, display ROIs, transpose, pixel ordering — are hardcoded defaults that reset on every restart. Users lose their preferred view configuration each session.
Scope — Tier 1: Portable Preferences Only
This issue covers semantic viewer preferences that should round-trip through TOML export/import. Machine-local layout state (window geometry, dock positions, last-typed prefix) is handled separately by #85 via QSettings.
See State Persistence Architecture for the full two-tier design.
Decision rule
"Would I want this setting to travel with my TOML config to another machine?"
What to persist (Tier 1)
| Key |
Type |
Default |
Description |
colormap |
str |
"viridis" |
Selected gradient from color bar context menu |
autoscale |
bool |
true |
Auto-scale toggle (5%-95% histogram) |
log_scale |
bool |
false |
Logarithmic intensity scale |
crosshair_visible |
bool |
false |
Crosshair overlay toggle |
threshold_enabled |
bool |
false |
Auto threshold toggle |
freeze_image |
bool |
false |
Lock/Freeze image toggle |
display_rois |
bool |
true |
ROI overlay visibility |
transpose |
bool |
false |
Image transpose |
pixel_order |
str |
"C" |
Pixel ordering (C or Fortran) |
plotting_frequency |
int |
10 |
Stats plotting refresh rate |
TOML representation
# New section — existing sections unchanged
[viewer_state.area_detector]
colormap = "viridis"
autoscale = true
log_scale = false
crosshair_visible = false
threshold_enabled = false
freeze_image = false
display_rois = true
transpose = false
pixel_order = "C"
plotting_frequency = 10
Implementation
New functions in settings.py
Follow the existing save_detector_prefix() / save_input_channel() pattern:
def load_viewer_state(viewer_key: str = "area_detector") -> dict:
cfg = ConfigSource(_get_effective_locator()).load()
return cfg.get("viewer_state", {}).get(viewer_key, {})
def save_viewer_state(viewer_key: str, state: dict) -> bool:
src = ConfigSource(_get_effective_locator())
existing = src.load()
vs = existing.setdefault("viewer_state", {})
vs[viewer_key] = state
return src.save({"viewer_state": vs})
Wire into area detector viewer
- On init: call
load_viewer_state("area_detector") to set checkbox/colormap values instead of hardcoded defaults
- On each toggle change (existing signal handlers like
autoscale_checked(), threshold_checked(), etc.): call save_viewer_state() with updated values
- On
closeEvent: bulk save as safety net
The round-trip
- User imports a TOML (with
[viewer_state.area_detector]) → it flows into the local DB profile, viewer opens with those preferences
- User changes a toggle in the UI → DB profile is updated immediately via
ConfigSource.save()
- User exports →
export_profile_to_toml() includes viewer state, producing a complete portable config
- User shares the TOML → recipient loads it and gets detector config AND viewer preferences
The database (dashpva.db) stays local and is not shared. The TOML file is the portable artifact.
Acceptance criteria
Related
Problem
All Area Detector viewer toggle states — colormap, autoscale, log scale, crosshair, threshold, freeze, display ROIs, transpose, pixel ordering — are hardcoded defaults that reset on every restart. Users lose their preferred view configuration each session.
Scope — Tier 1: Portable Preferences Only
See State Persistence Architecture for the full two-tier design.
Decision rule
What to persist (Tier 1)
colormap"viridis"autoscaletruelog_scalefalsecrosshair_visiblefalsethreshold_enabledfalsefreeze_imagefalsedisplay_roistruetransposefalsepixel_order"C"plotting_frequency10TOML representation
Implementation
New functions in
settings.pyFollow the existing
save_detector_prefix()/save_input_channel()pattern:Wire into area detector viewer
load_viewer_state("area_detector")to set checkbox/colormap values instead of hardcoded defaultsautoscale_checked(),threshold_checked(), etc.): callsave_viewer_state()with updated valuescloseEvent: bulk save as safety netThe round-trip
[viewer_state.area_detector]) → it flows into the local DB profile, viewer opens with those preferencesConfigSource.save()export_profile_to_toml()includes viewer state, producing a complete portable configThe database (
dashpva.db) stays local and is not shared. The TOML file is the portable artifact.Acceptance criteria
load_viewer_state()andsave_viewer_state()added tosettings.pyConfigSource.save()on each state change[viewer_state.area_detector]sectionConfigSourceinternals needed —viewer_stateis just another dict keyRelated
QSettings