ImFusion SDK 4.3
ImFusionGUI Migration Guide

With SDK version 4.0 we introduced the new ImFusionGUI component to provide a platform-independent abstraction layer for interactive GUI applications. Previously, our visualization stack relied on Qt for input event handling and embedding into a OpenGL-enabled display surface.

ImFusionGUI was designed to mostly follow the established display and view architecture and provide generic types for input events that can be used with all major GUI platform toolkits. However, in several instances we decided to improve the architecture to make it simpler and easier to maintain so that there is not always a 1-1 mapping between the old and the new API. Please follow this document to get detailed information on what has changed.

Input Event Handling

The old architecture was using QEvent for representing user input events and handler functions usually exhibit a signature in the form bool sceneEvent(QEvent* event). The new architecture uses the GUI::InputEvent class instead and the canonical form for a handler function is GUI::EventResult handleInputEvent(const InputEvent& event).

An important change is that InputEvents are now immutable. Furthermore, event handlers shall never change any global GUI state (e.g. changing the mouse cursor, spawning a context menu). Instead, all feedback wrt. to the GUI is to be indicate through the GUI::EventResult struct that they return. Since InputEvents are often propagated to a series of cascading handlers until propagation shall stop the EventResult offers a combine() function to conveniently merge their payload.

Changes in Concrete Classes and Interfaces

DataDisplayHandler

The DataDisplayHandler interface exhibits a breaking change because the base interface type for views and displays has changed: The signature of all pure virtual member functions now expects a GUI::View instad of an InteractiveView and a GUI::DisplayBase instead of a DisplayWidgetMulti as parameters.

Note
Concrete implementations of DataDisplayHandlers usually conduct RTTI (e.g. dynamic_cast) to determine a view type and implement dispatch logic. Make sure to update those checks if you need to support the new GUI::SliceView and GUI::VolumeView classes.
It might make sense to conduct RTTI on the GlView inside instead of on the GUI::View to simplify the code/logic: for instance you have a single check for a GlSliceView instead of two separate checks for ImageView2D and GUI::SliceView.

DisplayWidget[Multi]

Significant architectural changes were made to the successors of DisplayWidget and DisplayWidgetMulti. Not only do they no longer depend on any Qt classes but their extensive set of functionality and responsibilities was split into multiple dedicated classes for a cleaner separation of concerns and improved reusability:

  • The pure abstract GUI::DisplayBase interface serves as minimal common denominator and intended to be passed to consumers that only need to query the current viewport, views, layout, or to request a render update.
  • The GUI::Display class strives to be a minimal working implementation of this interface where you can actually add and render views, and forward input events to them. However, all extra functionality needs to be implemented manually. It is conceptually similar to the original DisplayWidget, however without any dependency on Qt.
  • The GUI::RadiologyDisplay class provides a similar functionality as the original DisplayWidgetMulti, however instead of all this extra functionality being merged into a single class it merely composes the following utility classes:
    • The GUI::DisplayAutoLayouter manages a library of layouting functions to generate suitable DisplayLayouts any number of currently visible views. Also, the functionality for temporarily hiding showing views and rearranging their order is located here.
    • The GUI::RadiologyViewGroup is technically the counterpart of the old ViewGroup class to conveniently create and maintain a set of image views typically found in radiology workstation. The RadiologyDisplay comes with one RadiologyViewGroup preinstalled so that these standard views are always available.
    • The GUI::RadiologyViewOverlays class enables you to manage the existence and behavior of ViewOverlays that are common in radiology workstations.
  • The GUI::QtDisplayWidget and GUI::QtDisplayWindow classes provide wrappers to embed any DisplayBase into a Qt-based application, either using a QOpenGLWidget or a QWindow as OpenGL backend.

InteractiveViews

The new equivalent of the old InteractiveView is the GUI::View base class. Except for some general cleanup and renaming of some of the member functions the architecture and design has remained the same.

InteractiveOverlays

Use GUI::ViewOverlay as base class for view overlays. The general architecture is still the same except for:

  • The GUI::ViewOverlay::Anchor is no longer a property of the ViewOverlay but instead maintained by the parent view.
  • The OverlayInteraction interface was not migrated to the new API since it did not serve any benefit because there was always a 1-1 mapping between overlay and overlay interaction instances. Input event handling is now to be implemented directly by overriding the virtual virtual handleInputEvent() member function.

Almost all InteractiveOverlays have been migrated to the new architecture. The class name remained the same except for dropping the Interactive prefix and being moved to the ImFusion::GUI namespace instead. For some overlays the original InteractiveOverlay variant was kept as a temporary deprecation layer.

InteractiveObjects

Use GUI::ViewObject as base class for interactive scene objects to be rendered inside a view. The general architecture is mostly the same except for:

Coordinate Systems

The ImFusion::GUI component now uses consistently OpenGL coordinates (the same coordinate system as the underlying GlViews etc.), which means that the origin is in the lower-left corner of the viewport. This applies to all interfaces such as GUI::DisplayBase, GUI::View, GUI::ViewOverlay, and GUI::InputEvent.

Previously, DisplayWidget and InteractiveView were using Qt coordinate system where the origin is in the upper-left corner requiring a flip along the Y axis whenever one needed to convert to and from the coorindate system of the underlying renderers.

Mixing Old and New Architecture

The new ImFusionGUI component was designed carefully to be interchangeable with the old framework to allow for a fluent transition during migration. This means that except for very few cases you can use classes inheriting from the new interfaces inside an old InteractiveView, as well as use classes still based on the old API inside the new API.

Adapters

We designed a special compatibility layer of "adapters" for use cases where a component outside the GUI framework shall be able to work with both the old DisplayWidgetMulti-based framework and with the new GUI::RadiologyDisplay-based framework (e.g. Controllers). These adapters mimic the interface of the old framework and forward the calls to the corresponding instance of either the old or the new framework that they wrap. This enables you to implement a transition period where your existing code supports both frameworks at the same time.

For instance, existing code that expects a DisplayWidgetMulti-style API can use the DisplayWidgetMultiAdapter to transparently forward calls to either an actual DisplayWidgetMulti (in which case the calls are simply forwarded) or a GUI::RadiologyDisplay (in which case the corresponding new API is used). There are also the InteractiveViewAdapter, ImageView2DAdapter, ImageView3DAdapter, and ViewGroupAdapter classes that implement the same behavior for their corresponding classes.

Warning
To mimic existing use cases these adapter classes have nullable/pointer semantics and can be 'null'. If they are null conversion to bool will yield false, comparison to nullptr will be true, and you must not call any of its other member functions.
Search Tab / S to search, Esc to close