Under the UI Hood - setiastro/setiastrosuitepro GitHub Wiki

How it all works together: DocManager Γ— SubWindows Γ— MDI Γ— Shortcuts Γ— Drag-and-Drop

The cast

  • DocManager (DocManager) – Owns all ImageDocuments, emits documentAdded/Removed, tracks the active doc, and knows how to open, duplicate, save, and apply edits (undo/redo recorded on the document).
  • ImageDocument – Holds the image array, metadata (incl. header snapshot), mask layers, and an undo/redo history. Emits changed whenever the visible state should refresh.
  • Subwindow (ImageSubWindow) – Widget that shows one ImageDocument inside the MDI. Handles zoom/pan, display-stretch (normal/hard + optional Link RGB), mask overlay, and per-view titles/rename.
  • MDI Workspace (QMdiArea via MdiArea) – Desktop containing all image subwindows. Determines the topmost window under the cursor for drops; double-click background opens files.
  • Shortcut system (ShortcutManager, ShortcutCanvas, ShortcutButton) – Overlay on the MDI viewport with draggable β€œdesktop” buttons for registered actions. Supports presets, multi-select, rubber-band, headless drag-apply.

Lifecycle at a glance

File path / slot / array
        β”‚
        β–Ό
DocManager.open_* β†’ ImageDocument β†’ documentAdded
        β”‚
        └─ MainWindow spawns ImageSubWindow bound to that document
               β”‚
               └─ DocManager.set_mdi_area hooks subwindow activation β†’ tracks active doc
  • Edits: Tools call DocManager.update_active_document(...) (or aliases). This pushes to the doc’s undo stack and emits ImageDocument.changed β†’ subwindow re-renders.
  • Save: DocManager.save_document(doc, path, bit_depth_override?) validates bit-depth per format and updates doc metadata.

Active document plumbing

  • On MDI activation, DocManager._on_subwindow_activated maps the subwindow back to its ImageDocument and updates the active pointer.
  • DocManager.get_active_document() falls back to asking MDI or β€œlast opened.”

Subwindow specifics

  • Rendering: Linear view or Display-Stretch (normal/hard; Link RGB ignored for mono). Converts to 8-bit RGB for display; optional red mask overlay.
  • View/Title: Keeps zoom/scroll/autostretch; can copy/paste view state; title shows β–  when a mask is active. Quick rename: F2 (view) or rename the document (propagates to peers).
  • Close guard: Warns on unsaved edits (based on undo) unless the app is force-closing.

Drag-and-Drop channels

1) View State (zoom/pan/stretch)

  • Start: Drag the ⧉ tab on a subwindow.
  • Payload: MIME_VIEWSTATE β†’ { doc_ptr, scale, hval, vval, autostretch, autostretch_target }.
  • Drop: On another subwindow (copy view) or MDI background (duplicate view).
  • Handled by: ImageSubWindow.apply_view_state(...) or MdiArea.viewStateDropped.

2) Command (+ optional preset)

  • Start: Alt+Drag a ShortcutButton (or other action sources).

  • Payload: MIME_CMD β†’ { command_id, preset }.

  • Drop: Front-most subwindow under the cursor.

  • Handled by:

    • Canvas: ShortcutCanvas finds the target and calls ShortcutManager.apply_command_to_subwindow(...).
    • That method prefers main-window _handle_command_drop(...), else tries target.apply_command(...), else mw.run_command(...), else activates the subwindow and triggers the registered QAction.

3) Mask

  • Start: Shift+Drag the ⧉ tab from a subwindow.
  • Payload: MIME_MASK β†’ { mask_doc_ptr, mode, invert, feather, name }.
  • Drop: Target subwindow.
  • Handled by: Main window _handle_mask_drop(...). Title shows β–  when a mask is active; overlay toggle via UI/shortcut.

4) Astrometry (WCS/SIP)

  • Start: Ctrl+Drag the ⧉ tab.
  • Payload: MIME_ASTROMETRY β†’ { wcs_from_doc_ptr, name }.
  • Drop: Target subwindow.
  • Handled by: Main window _on_astrometry_drop(...).

5) Files & Folders

  • Start: Drag from OS into the ShortcutCanvas.
  • Handled by: Canvas collects local files (recursing folders by allowed endings) and asks main window/DocManager to open, then spawns subwindows.

Shortcuts overlay (on the MDI viewport)

  • Register actions: ShortcutManager.register_action(command_id, QAction).

  • Create instances: add_shortcut(command_id, pos, label?) β†’ a ShortcutButton with its own SID and preset (stored in QSettings).

  • Use:

    • Double-click β†’ run action (opens UI if interactive).
    • Alt+Drag (single selected) β†’ headless apply to a subwindow via MIME_CMD.
    • Ctrl/Shift or rubber-band to multi-select; move as a group; Delete to remove.
    • Right-click β†’ Run / Edit Preset (built-in dialogs or JSON) / Clear Preset / Rename / Delete.
  • Persistence: Positions, labels, and presets saved as a v2 JSON blob in QSettings (v1 auto-migrates).


MDI coordination

  • Accepts drops for viewstate/command/mask/astrometry and uses StackingOrder to find the topmost subwindow under the cursor.
  • Background double-click emits backgroundDoubleClicked β†’ open files.
  • Falls back to default Qt behavior for unknown drops.

Undo/Redo model

  • Each ImageDocument records edits with a step_name.
  • apply_edit() pushes current image/metadata to undo, clears redo, updates metadata, then emits changed.
  • Subwindows listen and re-render; layer composites refresh as needed.

Persistence & safety notes

  • QSettings stores: theme, linked-stretch default, panel layout, shortcut positions/labels/presets.
  • Save As: filename hardening + extension normalization; format-specific bit-depth validation.
  • FITS/MEF bonus: on open, known rejection layers (REJ_LOW/HIGH/COMB) auto-open as sibling read-only docs with friendly names.

Quick scenarios

  • Headless stretch to a view: create a Statistical Stretch shortcut with preset β†’ Alt+Drag onto the target subwindow.
  • Copy A’s zoom to B: drag ⧉ from A, drop on B.
  • Move a mask from A to B: Shift+Drag ⧉ from A, drop on B; toggle overlay to visualize.