XQMprViewer
An open-source Qt + VTK multi-planar (MPR) DICOM viewer — a from-scratch foundation for Qt/VTK medical imaging tooling.
Problem
Working on a production Qt + VTK medical imaging system taught me
two things the hard way. First: code written under delivery
pressure accrues debt fast — the system worked, but its structure
made every new feature more expensive than the last. Second: there
was a performance question we tried and failed to answer back then
— rendering each viewport on its own thread. Every
vtkRenderWindow on a separate thread sounds like an obvious win
on multi-core hardware, but OpenGL’s context model disagrees: off
the main thread, the pipeline breaks down — and no reliable path
around it exists, not in VTK’s own machinery and not in any
community solution I could find, despite extensive searching
across GitHub, Stack Overflow, and the VTK forums, then and since.
XQMprViewer exists to answer what that experience left open: what does a clean, modular foundation for Qt + VTK medical tooling look like — and since per-thread rendering is off the table, what do the rendering topologies you can choose actually cost?
Approach
XQMprViewer is a Qt Widgets + VTK multi-planar reconstruction
(MPR) viewer in C++17, with a layered architecture separating UI,
controllers, adapters, rendering, and overlays. The reusable
viewport library (XQVtkViewport) keeps its public API
pImpl-isolated so Qt and VTK headers never leak into consumer
code, and provides a layout factory for horizontal, vertical, and
grid splits. The application renders axial, coronal, and sagittal
MPR views via vtkResliceImageViewer, loads DICOM directories
asynchronously on a background thread, and synchronizes a
draggable sphere annotation across all three planes, with runtime
layout switching and per-pane plane assignment.
The threading question lives on as the project’s core experiment:
the viewer implements two rendering topologies — a viewport mode
(multiple viewports over a single vtkRenderWindow) and a
multi-window mode (separate render windows) — with live FPS and
frame-delay readouts exposing what each topology costs. The same
application, two architectures, measurable side by side.
Architecture
The two rendering topologies share the same IView/controller
architecture, not a common rendering library — MainWindow selects a
mode, each mode gets its own IView-based view bound to its own
controller, and only ViewportController owns the XQVtkViewport
library; DicomController and SphereController serve both paths.
Tradeoffs
Building for comparability constrained the design: both rendering modes live behind interfaces clean enough that switching topologies doesn’t rewrite the application — abstraction effort a single-mode viewer wouldn’t need. And the original goal, true multi-threaded rendering, remains deliberately unsolved here: rather than fighting OpenGL’s threading model with fragile workarounds, the project accepts the constraint and maps the design space inside it.
Outcome
An open-source (GPLv3) modular Qt/VTK foundation with published Doxygen API documentation — built entirely from scratch, sharing no code with the client work it’s domain-adjacent to. The dual-topology comparison turns a question I once couldn’t answer under deadline pressure into something measurable, and the codebase doubles as my reference architecture for future Qt + VTK work.