• Switch to Warm
  • Switch to Cool
  • Switch to Ember
  • Switch to Dusk
  • Switch to Plum
  • Switch to Midnight
  • Switch to Harbor
  • Switch to Cobalt
  • Switch to Noir
  • Switch to Tropic
  • Workspace
  • Projects
  • Notes

Retrospective: Building XQMprViewer After Two Years of Production Qt/VTK Work

Retrospective

Published Jun 1, 2026

What went well

Starting XQMprViewer from a clean slate, after two years inside a production medical imaging codebase, meant every architectural decision was informed by specific pain I’d already felt. The pImpl isolation on the viewport library, the IView/controller split, the decision to never let Qt or VTK headers leak past the adapter boundary, none of these were theoretical. They were responses to concrete maintenance costs I’d paid repeatedly in the production system, where tangled Qt/VTK dependencies made every refactor a cascade of recompilation.

The result is a codebase where adding a new rendering mode means writing a new IView implementation and a controller, without touching the existing viewport library or the DICOM loading path. That modularity was the explicit goal, and it held.

What was harder than expected

The dual-topology comparison (viewport mode versus multi-window mode) sounded like a straightforward experiment: same data, two render paths, compare FPS. The hard part was making the comparison fair. A single vtkRenderWindow shared across viewports handles OpenGL context sharing internally; separate render windows each create their own context, and on some drivers that means re-uploading textures per window. The FPS numbers weren’t comparable until I normalized for texture state, which meant instrumenting VTK’s internal pipeline at a level I hadn’t planned on.

The second surprise was how much of the engineering time went into the sphere annotation synchronization across planes, not the rendering itself. Keeping a draggable point consistent across axial, coronal, and sagittal views requires coordinate transforms that assume the volume’s origin and spacing are exactly right, a DICOM metadata assumption that’s correct in theory and subtly wrong in practice when files come from different scanners with different padding conventions.

What I’d do differently

I would write the DICOM metadata validation layer first, before any rendering code. In both XQMprViewer and the production system, the majority of hard-to-reproduce bugs traced back to unexpected metadata: missing ImagePositionPatient, inconsistent PixelSpacing, slices out of order. A dedicated validation pass that rejects or flags bad data upfront would have saved more time than any rendering optimization.

The second thing: I’d commit to a test harness earlier. The project has manual visual tests but no automated pipeline for verifying that a rendered frame matches expected output. For a medical imaging tool, pixel-level regression testing isn’t optional. It’s the kind of discipline I’d enforce on a production system but skipped on a personal project because “I’ll add it later.” Later never comes.