Main window¶
The main window is the top-level container. It switches between three pages via an internal QStackedWidget:
- A centered placeholder when no session is loaded.
- Level 1 — timeline overview (see §5.2).
- Level 2 — detail pair view (see §5.3).
The window opens at 1400 × 800 by default and is fully resizable.
Title bar¶
Shows one of three states:
| Title | Meaning |
|---|---|
MIDI-Camera Alignment Tool |
No participant loaded |
MIDI-Camera Alignment Tool — Participant 042 |
Session loaded, no unsaved changes |
MIDI-Camera Alignment Tool — Participant 042 * |
Session loaded, unsaved changes |
A trailing * appears whenever _dirty is set: after applying a global shift, adding/deleting an anchor, toggling an anchor, or rebasing paths during a load. The * disappears only after a successful Save Alignment.
Status bar¶
Below the main pages, the status bar shows one of:
No participant loaded— initial state.Participant 042 | 12 MIDI files | 12 camera clips | Global shift: 0.000s | Anchors: 5— full summary, updated after every state change.Level 2: trial_001.mid + C0001.MP4— while a Level 2 pair is active.Saved: /path/to/alignment.json— transient confirmation after Save Alignment.
File menu¶
Single top-level menu — no toolbar, no other menus.
| Item | Shortcut | Behaviour |
|---|---|---|
| Open Participant… | Ctrl+O | Folder picker. Prompts to save first if dirty. On selection, runs ParticipantLoader.load and swaps state to Level 1. Shows a post-load warning dialog if any files were skipped. If no .mid or .MP4 files were found, shows a "No Files Found" warning and does not change state. |
| Save Alignment… | Ctrl+S | File picker for *.json. Writes the session atomically via tempfile + os.replace. Disabled until a session is loaded. After success, clears the dirty flag, drops the * from the title, and shows the file path in the status bar. |
| Load Alignment… | Ctrl+L | File picker for *.json. Prompts to save first if dirty. Rebases paths if the stored participant_folder doesn't exist on disk (see below). |
| Exit | Ctrl+Q | Closes the window. Prompts to save first if dirty. Releases the Level 2 video and intensity workers. |
Unsaved-changes prompt¶
When closing, opening a new participant, or loading a JSON while _dirty is true, a modal QMessageBox appears:
You have unsaved changes. What would you like to do?
Save | Discard | Cancel
- Save — opens the Save dialog. If save succeeds, proceeds with the original action. If save fails or is cancelled, stays on the current session.
- Discard — proceeds with the original action, dropping all unsaved changes.
- Cancel — aborts; stays on the current session.
Moving participant folders¶
When you Load Alignment… and the JSON's participant_folder no longer exists on disk, a folder-picker dialog appears with this header:
Participant folder not found:
{old path}Select the new location:
If you pick a new folder, persistence.rebase_paths remaps every file_path, mp4_path, and xml_path under that new base and the session is marked dirty (so you're nudged to re-save the JSON with the corrected paths). If you cancel the folder picker, the load aborts and the previous session remains active.
Error dialogs¶
All errors come through a single router (_show_exception in alignment_tool/ui/main_window.py:260-266):
MediaLoadErrorsubclasses →QMessageBox.criticaltitled with the exception class name.PersistenceErrorsubclasses →QMessageBox.criticaltitled with the exception class name.InvariantErrorsubclasses →QMessageBox.warning(less severe).- Any other exception →
QMessageBox.critical(title="Error")with the exception string.
See §7.2 Error messages for an exhaustive list of what each exception means.