lf_mri_gui is a PySide6 desktop application that serves as the control frontend
for the LF-MRI system. It is a hybrid client: some tabs communicate with the
backend microservices over HTTP, others execute local Python logic.
The backend services run in Docker via lf_mri_platform.
| # | Tab | Class | Communication |
|---|---|---|---|
| 0 | Sequence | SeqInterpTab |
REST → seq-interp:7475 if online, otherwise local Python fallback |
| 1 | Scanner | ScannerTab |
REST → orchestrator:1717 (always) |
| 2 | FID | FidTab |
Local Python + file I/O only |
lf_mri_gui/
├── app.py # Entry point (argparse + QApplication)
├── build_exe.bat # PyInstaller build wrapper
├── lf_mri_gui.spec # PyInstaller spec (one-folder .exe)
├── requirements.txt
├── cfg/
│ ├── hw_config.json # HackRF, PicoScope, GRU×3, DuePP
│ ├── server_config.json # orchestrator_url, seq_interp_url
│ └── updated_constraints_lf.json # LF hardware constraints preset
└── src/
├── app_window.py # LFMRIWindow(QMainWindow) — hosts QTabWidget
├── tabs/
│ ├── seq_interp_tab.py # Tab 0 — Sequence (hybrid HTTP/local)
│ ├── scanner_tab.py # Tab 1 — Scanner (orchestrator client)
│ └── fid_tab.py # Tab 2 — FID (local only)
├── clients/
│ ├── orchestrator_client.py # httpx client for lf_orchestration :1717
│ └── seq_interp_client.py # httpx client for seq-interp :7475
├── core/ # synchronizer, waveform_processor, seq_generator
├── gui/ # plot_panel, preview_panel, scheme_panel,
│ │ # controls_panel, block_table, adapters, workers
├── hardware/ # constraints.py
├── interfaces/ # pulseq_adapter, xml_generator, rf_exporter,
│ │ # gradient_exporter, picoscope_exporter,
│ │ # post_request_generator
└── fid/
└── seqgen_FID.py # FID sequence generator (local)
SeqInterpTab)Full .seq interpretation pipeline with HTTP-first, local fallback strategy.
SeqInterpClient.healthcheck() → GET http://localhost:7475/healthSeqInterpHttpWorker uploads .seq via POST /interpret/GET /interpret/ for statusGET /result/{task_id} — returns:
xml_text — sync XMLpost_json — scanner POST payloadmetadata — block counts, total durationwaveforms — downsampled Gx/Gy/Gz, RF amp/phase, ADC gate arraysLoadInterpWorker runs the full pipeline in-process:
PulseqLoader → Synchronizer → XMLGenerator → exporters → PostRequestGenerator
.seq file (button or File > Load .seq… / Ctrl+O)sync_v2.xml, PicoScope configready_for_scan(info_dict) → switches to Scanner tabScannerTab)Exclusively communicates with lf_orchestration at orchestrator_url (default http://localhost:1717).
┌──────────────────────┬──────────────────────────────────────────┐
│ Orchestrator URL │ Steps │
│ [____________] Conn │ ┌────┬───────────────────┬──────────┐ │
│ │ │ # │ Name │ Status │ │
│ Scenario │ ├────┼───────────────────┼──────────┤ │
│ [___________▼] │ │ 1 │ interpret_sequence│ done │ │
│ [Load] [Run All] │ │ 2 │ start_measurement │ running │ │
│ [Next] [Abort] │ │ 3 │ wait_data_ready │ pending │ │
│ │ └────┴───────────────────┴──────────┘ │
│ │ Step log │
└──────────────────────┴──────────────────────────────────────────┘
GET /scenario/listPOST /scenario/load/{name} → receives job_idPOST /scenario/{job_id}/run_all
or Next → POST /scenario/{job_id}/nextGET /scenario/{job_id} every 1.5 s → updates step tablePOST /scenario/{job_id}/abortLFMRIWindow)set_hw_config(path) — propagate hw_config.json pathapply_seq_info(info_dict) — receive POST payload from Sequence tabFidTab)Generates FID .seq files locally using src/fid/seqgen_FID.py.
On success emits fid_seq_generated(path) → LFMRIWindow hands the path
to SeqInterpTab.load_seq_file() and switches to the Sequence tab.
FidTab.fid_seq_generated(path) ──→ LFMRIWindow._on_fid_generated()
├── SeqInterpTab.load_seq_file(path)
└── tabs.setCurrentIndex(0)
SeqInterpTab.ready_for_scan(info) ─→ LFMRIWindow._on_ready_for_scan()
├── ScannerTab.apply_seq_info(info)
└── tabs.setCurrentIndex(1)
File > Load .seq… ──→ SeqInterpTab.load_seq_file(path)
tabs.setCurrentIndex(0)
File > Load HW Config… ──→ SeqInterpTab.set_hw_config(path)
ScannerTab.set_hw_config(path)
FidTab.set_hw_config(path)
Ctrl+O), Load HW Config, Set Output Directory,
Load LF Constraints, Exit (Ctrl+Q)Managed by D:\Projects\lf_mri_platform\docker-compose.yml.
| Service | Port | GUI talks to it? |
|---|---|---|
| seq-interp | 7475 | Yes — SeqInterpTab (HTTP mode) |
| orchestrator | 1717 | Yes — ScannerTab (always) |
| spectrometer | 8000 | No — via orchestrator only |
| reconstructor | 8081 | No — via orchestrator only |
| spectroscopy | 8002 | No — standalone signal processor |
| Tab | Feature | State |
|---|---|---|
| Scanner | Abort job | Button exists, endpoint may not be implemented in orchestrator |
| Scanner | Job history / past jobs | Not planned |
| FID | Full waveform preview | Placeholder only |
| App | Hardware Settings menu | "Not implemented" dialog |
| App | Reconstruction results viewer | Not planned |
# With Docker backend (recommended)
cd D:\Projects\lf_mri_platform
.\start.ps1 # starts Docker stack + GUI
# GUI only (no Docker)
cd D:\Projects\lf_mri\MRI-testing
python lf_mri_gui/app.py
# With a pre-loaded sequence
python lf_mri_gui/app.py path/to/sequence.seq
# Build standalone .exe
cd D:\Projects\lf_mri\MRI-testing\lf_mri_gui
.venv\Scripts\activate
build_exe.bat
# Output: dist\lf_mri_gui\lf_mri_gui.exe