|
Medical Imaging Interaction Toolkit
2026.06.00
Medical Imaging Interaction Toolkit
|
This page is the human-friendly walkthrough of the JSON layout file format used by the MxN multi-widget editor. The on-disk preset files (Modules/QtWidgets/resource/mxnLayout_*.json), the documents accepted by QmitkMxNMultiWidget::ApplyLayout, and the documents emitted by QmitkMxNMultiWidget::SerializeLayout all share this single format.
The closed, normative reference is the JSON Schema Modules/QtWidgets/resource/mxn-layout-v2.schema.json (Draft 2020-12). When something below is ambiguous or contradicts the schema, the schema wins. This page focuses on intent, examples, and the everyday parts of the format; the schema covers the closed list of accepted enum values, exact regex patterns, and the additionalProperties: false closure rules.
Related runtime concepts are not duplicated here; see the MxN Multi-Widget concept page for QmitkMxNMultiWidget, QmitkSynchronizedNodeSelectionWidget, and QmitkSynchronizedWidgetConnector, which together implement the runtime side of the synchronization the layout document describes.
A layout document captures:
select_all UX-mode of the selection bundle).A layout document does not capture global rendering state (selected position, current time step, camera) or the loaded data set. Those are owned by other parts of the system. Loading a layout into a session that already has data nodes simply applies the new geometry; the data stays.
version: must be the exact string "2.0". The C++ loader rejects any other value. v1.x files are upgraded out-of-band — see "Migrating from
v1.x" below.name: optional human-readable preset name. Pure metadata; ignored by the loader and not used for routing. (The window leaves carry an optional name of their own, used the same way — see "Window identity and display
label" below.)groups: optional. When present, authoritative. See "Lazy vs. strict mode" below.root: required. The root of the splitter tree. Always a split, even for a single window (which is then a one-child split).A node in the tree is one of two kinds, distinguished by type:
A split divides its area among its children along one axis. horizontal arranges children left-to-right; vertical arranges them top-to-bottom. (Same semantics as Qt's QSplitter::Horizontal / QSplitter::Vertical.) Children must be a non-empty array. The document root has no parent and therefore no size.
A window is a leaf render-window. Every window must declare:
id: the window's identity. A unique identifier within the document, in the canonical fully-qualified form <editor_name>__<bare_id>. The same string is used verbatim as the engine-side render-window name registered with the rendering manager, as the URL path segment in REST sub-resources, as the per-renderer DataNode property context key, and in persisted session state. The loader does not prepend or strip a prefix at any boundary. Schema-enforced shape: ^[A-Za-z][A-Za-z0-9.-]*__[A-Za-z0-9_.-]+$. The editor-name segment contains no _, the namespace delimiter is the literal __, and the bare-id segment uses the existing URL-segment-safe alphabet.view_direction: which anatomical plane the window shows. The closed enum is "axial", "sagittal", "coronal", "original" (lowercase). Unknown strings throw at load time; there is no silent fallback.links: per-dimension synchronization references. v2.0 has one dimension, selection; v3.0 will add more dimensions here additively (zoom, time, crosshair, ...). Every window must declare links.selection explicitly – there are no implicit singletons.A window may additionally declare:
name: optional human-readable display label. Free-form (no pattern constraint, not required to be unique within the document). Pure metadata; the loader does not use it for routing, addressing, persisted-state keying, or REST URL construction (those all use id). Omit the field entirely if the cell has no display name — empty strings are rejected.size on a child is a splitter weight. Only the ratio between siblings matters; Qt redistributes weights proportionally on resize. Absolute values are NOT pixel measurements – prefer small numbers (e.g. 1, 2, 3) over screenshot-derived pixel counts. [size: 1, size: 1, size: 1] and [size: 100, size: 100, size: 100] produce the exact same layout.
size is optional. When omitted, the cell takes a default weight of 1. Mixed-defined siblings compute as ratios:
[size: 3, default, default] -> 3:1:1 (first cell is 3/5 of the row)[size: 2, size: 1] -> 2:1 (first cell is 2/3 of the row)[default, default, default] -> 1:1:1 (equal split)size must be >= 1 if present (the schema rejects 0 and the C++ loader throws on < 1). The format has no first-class way to hide a cell while keeping it in the tree; size: 0 is not a valid stand-in.
Cells share runtime state by linking to a group name:
Two cells with the same links.selection value are mutually linked: they share the connector that mediates selection list, visibility, and stack order. Group names are arbitrary URL-segment-safe strings (alphanumeric, underscore, dot, hyphen). The same group name can be used across multiple dimensions in v3 (e.g. "selection": "main", "zoom": "main"); group names live in a single namespace and dimensions are orthogonal.
Per-group persisted state lives once at the top level:
v2.0 declares one such property: select_all (the selection bundle's UX mode — whether the group displays every data node or a curated subset). v3.0 dimensions that need persisted per-group state add their properties to the same group entry additively.
There is no per-cell select_all. The setting belongs to the group, not to any one of its members.
The top-level groups dict is optional:
groups is present. Every label appearing in any cell's links must be a key in groups; missing labels throw at load time. This catches typos and tool drift early.groups is omitted. Every referenced label becomes an implicit group with property defaults (select_all: true). Cells still declare links.selection explicitly.Per-cell links is always required — laziness applies only to the group-properties block at the top, never to per-cell linking.
QmitkMxNMultiWidget::SerializeLayout always emits strict mode (full groups dict, every property explicit), so a serialize → apply round trip is stable and reviewer-friendly.
The result is a 2x3 grid with equal weights everywhere. Row 1 (mxn__widget0..mxn__widget2) shares the selection bundle named main; row 2 (mxn__widget3..mxn__widget5) shares its own bundle named row2. Changing the selection in any row-1 cell propagates only to the other two row-1 cells; row 2 is independent.
To make the top row twice as tall as the bottom row, change the outer children's sizes to 2 and 1 (or omit one and leave the other at 2, since the omitted child defaults to 1).
When a layout is applied, each group's runtime synchronized state for per-renderer node properties (today: per-node visible, per-node layer; future v3 keys analogously) is seeded from the cell that appears first in document order whose links.<dim> names that group. After seeding, every other group member is normalised to the seed cell's values for those keys.
children arrays in array order).groups dict (e.g. select_all) are not seeded from cells — the value declared there wins outright.If a hand-author wants a specific cell to be the seed, they list that cell first among the group's members in the layout document. In the worked example above, mxn__widget0 is the seed for main and mxn__widget3 is the seed for row2.
Each window leaf carries a required identity (id) and an optional display label (name). The two are deliberately separate fields so that renaming the human-facing label never invalidates persisted references.
**id (identity).** The id is the single canonical string for a window across every artifact in the system: the layout JSON, the REST URL, the engine's render-window registration with the rendering manager, the per-renderer DataNode property context key, and any persisted session-state reference. The loader does not prepend or strip a prefix at any boundary; what the document holds is what every other surface sees.
The qualified-id form is <editor_name>__<bare_id>:
<editor_name> matches ^[A-Za-z][A-Za-z0-9.-]*$ (no _, so the first-__ split is unambiguous).__ is the literal namespace delimiter.<bare_id> matches ^[A-Za-z0-9_.-]+$ (URL-segment-safe; may itself contain further __ substrings, since split is by first occurrence).The combined regex is ^[A-Za-z][A-Za-z0-9.-]*__[A-Za-z0-9_.-]+$. Within a document, ids must be unique.
The schema enforces structural shape only. The C++ loader additionally enforces that <editor_name> matches the loading editor's multiWidgetName (default mxn); a layout written for a different editor instance is rejected up-front with a message naming the offending id. Editor-name constructor inputs that contain _ or otherwise violate the editor-name regex are rejected at editor construction time, not at load time.
The recommended default for tool-generated layouts is mxn__widget<i> where <i> is the leaf's pre-order traversal index (0-based, contiguous). SerializeLayout writes this form. Hand-authored presets may use any unique bare-id segment (e.g. mxn__alpha, mxn__upper_left) as long as the qualified id is URL-segment-safe and unique within the document.
Renaming an id breaks every cached reference to it (persisted sessions, REST clients holding URLs, scene-file context keys); treat it as permanent.
**name (display label).** Optional, free-form, not required to be unique. Holds whatever string a user-facing surface should show for the cell — "Tumor axial", "Reference T1", "Comparison view 2". Pure metadata: the loader does not use it for routing, addressing, persisted-state keying, or REST URL construction. The schema rejects an empty name; tools should omit the field entirely instead of emitting "". Tools that auto-generate layouts (SerializeLayout, the migration script) leave name unset by default; hand-authors and UIs that surface a "rename window" action populate it.
The convention is symmetric across the document: the top-level optional name is the display label of the preset, and a per-window optional name is the display label of that window. Both are pure metadata; both can be safely renamed at any time.
Files written by older MITK releases use a different shape (top-level content array, integer synchGroup, capitalised view directions, per-cell selectAll). The current loader does not read v1.x documents directly; attempting to load one surfaces an error message that points at a one-shot conversion tool.
To upgrade a v1.x file:
The script writes a v2.0 document to standard output (or to the path given via -o) that the editor's "Load layout" action accepts unchanged. It is a pure stdlib Python 3 script with no required external dependencies; if jsonschema is importable in the running interpreter, the script also validates its own output against the v2 schema before writing.
The migration is intentionally one-shot. There is no in-memory v1.x translation in the C++ loader — the rationale is that custom MxN presets are rare and an out-of-band script is cheaper to maintain than a permanent in-process compatibility shim.
The schema is closed: additionalProperties: false everywhere, including inside links and inside groups entries. This is deliberate. v2.0 only knows the selection synchronization dimension and the select_all group property. A future v3.0 schema bump will add further dimensions inside links (e.g. zoom, time, crosshair) and possibly further per-group properties inside group entries. Older loaders presented with a v3.0 document refuse it loudly at the version check — by design. A loader that cannot honour the synchronization should not silently load the file and drop part of the contract.
The mental-model break between v2 and v3 is "more dimension keys inside `links`, more group properties inside group entries" — never "selection got reshaped". Tooling that learns the v2 link/groups shape continues to work in v3 without modification.
Modules/QtWidgets/resource/mxn-layout-v2.schema.jsonModules/QtWidgets/resource/mxnLayout_twoRowsEachDirection.jsonModules/QtWidgets/resource/migrate-mxn-layout-v1-to-v2.pyQmitkMxNMultiWidget::ApplyLayout and QmitkMxNMultiWidget::SerializeLayout