Architecture¶
Package layout¶
src/spino/
├── app.py # main window: builds the tabbed UI, wires orchestration
├── __main__.py # `python -m spino`
├── config_io.py # defaults (read from pipeline), help text, JSON (de)serialise
├── runner.py # subprocess entry: overlay settings → run pipeline
├── theme.py # colour palette + tab list (replaces GUIBRUSHR ConstantVariables)
├── panels/ # one class per config group + run_panel (log/output)
├── gui_toolkit/ # reused GUIBRUSHR widgets + theming (layout/, widget/, graphics.yaml)
├── pipeline/ # the scheduling pipeline (8 near-verbatim modules)
└── data/ # bundled catalogs (cat/), aux files (aux/), sky-transmission FITS
Three layers¶
GUI (
app.py,panels/*): aMyTabPanelnotebook with one panel per config group. Each panel builds labelledMy*widgets and exposescollect()(widgets → dict) andset_values()(dict → widgets).app.pymerges all panels’collect()into a single flat settings dict.Runner (
runner.py): a headless subprocess entry point. It loads asettings.json,setattrs every key onto the pipeline’sphase_configmodule, then imports and callsphase_scheduler.main(). Running the pipeline in a child process (rather than a thread) keeps the Tk UI responsive, isolates matplotlib’s Agg backend, and guarantees a clean config each run.Pipeline (
pipeline/*): the scientific code, bundled almost verbatim from the original headless tool. It is driven entirely by module-level globals inphase_config.py.
Data flow¶
app.collect_all() ─► settings.json ─► subprocess: runner.py
│ setattr onto phase_config
▼
phase_scheduler.main()
│ stdout
run_panel log pane ◄──────────────────┘
run_panel output list ◄── scans OUTPUT_DIR for *.pdf / *.csv
The Run panel launches the subprocess with stdout/stderr piped; a daemon
reader thread pushes lines onto a queue.Queue, and the Tk main loop drains the
queue via after(150, …) into the log widget (only the queue crosses the thread
boundary, so Tk stays single-threaded).
Notes on the vendored code¶
gui_toolkit: copied from GUIBRUSHR’s
GUI/LAYOUT,GUI/WIDGET, andHelpButton; the only edits were rewriting theGUIBRUSHR....import prefix tospino.gui_toolkit....and repointingGraphicsConfig’s YAML path to the co-locatedgraphics.yaml. The matplotlib/PDF widgets were not copied (results open in the system viewer), so the toolkit needs only PyYAML.pipeline: flat imports (
from phase_config import …) are preserved exactly as upstream;runner.pyinsertspipeline/onsys.pathso they resolve. The only pipeline edits: one import line, repointing thephase_configdata paths at the bundleddata/folder (with a user-writable defaultOUTPUT_DIR), and inlining three SI constants to drop a heavy petitRADTRANS dependency.