Migration guide from CamiTK 4.1 to CamiTK 5.0
CamiTK 5 fixes a lot of bugs (see the Release Notes) and introduces one big new feature: the viewer extension (ability to define plugins that provides new viewers, see the viewer extension documentation for more information). Viewer extensions are now a first class type of extension, and are as easy to create and integrate as action and component extensions. They also makes applications easier to build and test.
The methods in the Component
class that used a pointer to a Viewer
in the previous versions of CamiTK are now using a QString
to name the required viewer. This ensures safety and modularity to access a specific viewer (e.g., to manage the case where the corresponding viewer extension is not currently loaded).
Deprecated API members
Since CamiTK 5.0
Thanks to the CAMITK_API_DEPRECATED
macro, deprecated methods are now marked by the compiler. You should get a compilation warnings with a explanation message if you use deprecated CamiTK API members.
This is the list of deprecated members since CamiTK 4.1 (they still works but beware: they might disappear very soon from the API):
setVisibility(Viewer*, bool)
: Please usesetVisibility(QString viewerName, bool)
insteadgetVisibility(Viewer*) const
: Please usegetVisibility(QString viewerName, bool)
insteadsetFrameVisibility(Viewer*, bool)
(inComponent
,Frame
orInterfaceFrame
): Please usesetFrameVisibility(QString viewerName, bool)
insteadgetFrameVisibility(Viewer*)
(inComponent
,Frame
orInterfaceFrame
): Please useComponent::getFrameVisibility(QString viewerName, bool)
instead
As the viewers are now either set in the central part of the main window or in a dock, the following methods are also deprecated in MainWindow
:
showViewer(Viewer*, bool)
: This method is now calledshowDockViewer(..)
removeViewer(Viewer* viewer)
: Using the new viewer extension system, removing viewers is not required anymore (just hide its widget). Removing viewer can be harmful and result in crash. The viewer’s GUI can now simply be hidden, therefore there is no need to remove a viewer.
Also now refreshInterfaceNode()
is not required anymore. Everything should now be done transparently. If it still does not work in your case you can call setNodeModified(true)
to force a specific InterfaceNode
/Component
to refresh the visualisation in the explorer.
Finally, in previous CamiTK versions, viewers were mostly created using the singleton pattern. An instance of a viewer was accessible through a static method of the viewer’s classe (e.g., InteractiveViewer::get3DViewer()
or MedicalImageViewer::getInstance()
). Thanks to the viewer extension mechanism, this system is more open to change and more securely managed by the Application
class.
A viewer extension is in charge or instantiating one or more viewers and registered them using specific name(s) when it is loaded in the application. Once the viewer extension is loaded, the viewers are now accessible through the Application::getViewer()
and Application::getViewers()
methods. You may also easily instantiate a new viewer of any loaded viewer extension for your private use (e.g., in a specific action or main window GUI), by calling Application::getNewViewer(QString name, QString className)
. Application
can now be considered as a viewer factory.
In order to use a viewer in an extension, you now need to declare the dependency in the CMakeLists.txt
using the NEEDS_VIEWER_EXTENSION
parameter (see the troubleshooting section below)
Viewer management migration step-by-step
Since CamiTK 5.0
As viewers are now first-class extension in CamiTK, all the viewers previously available directly in CamiTK core library are now available as independent extensions. This necessitates some minor change in order to migrate your code. This section will guide you through these changes.
The following viewer extensions are provided by CamiTK Community Edition since CamiTK 5.0:
Extension | Viewer class | Header | Provided viewer instance name(s)1 |
---|---|---|---|
explorer | Explorer | Explorer.h | “Explorer” |
frameexplorer | FrameExplorer | FrameExplorer.h | “Frame Explorer” |
propertyexplorer | PropertyExplorer | PropertyExplorer.h | “Property Explorer” |
interactivegeometryviewer | InteractiveGeometryViewer | InteractiveGeometryViewer.h | “3D Viewer”2 |
interactivesliceviewer | InteractiveSliceViewer | InteractiveSliceViewer.h | “Axial Viewer”2, “Coronal Viewer”2, “Sagittal Viewer”2 and “Arbitrary Viewer”2 |
medicalimageviewer | MedicalImageViewer | MedicalImageViewer.h | “Medical Image Viewer” |
bitmap | Bitmap | Bitmap.h | “Bitmap Viewer” |
actionviewer3 | ActionViewer | ActionViewer.h | “Action Viewer” |
1 You are free to instance your own specific viewer from the provieded viewer class. The extension comes with default instances that are automatically available once the extension is loaded. 2 These names were kept for facilitating the migration to CamiTK 5.0 3 This viewer manages actions (and not components as all the other viewers)
Note that the tutorials
CEP provides two additionnal examples:
MixedViewer
(shows how to reuse existing viewer)TextViewer
(shows how to create a specific viewer from scratch).
See the tutorial list for more information.
The following subsections shows how to migrate your code through the use of examples. One of the main consequence of relaxing the dependency to viewers is that they are now access by their names.
Component visibility
# Before CamiTK 5.0
comp->getVisibility(InteractiveViewer::get3DViewer())
# Since CamiTK 5.0
comp->getVisibility("3D Viewer");
Component frame visibility
# Before CamiTK 5.0
currentComponent->setFrameVisibility(InteractiveViewer::get3DViewer(), false);
# Since CamiTK 5.0
currentComponent->setFrameVisibility("3D Viewer", false);
generic viewer use case
# Before CamiTK 5.0
InteractiveViewer::getAxialViewer()->...
# Since CamiTK 5.0
Application::getViewer("Axial Viewer")
// or more secure: test if the extension was correctly loaded first by checking the get values before using it:
AxialViewer* axialViewer = dynamic_cast<AxialViewer*>(Application::getViewer("Axial Viewer"));
if (axialViewer != nullptr) {
axialViewer->...
}
viewer specific use case 1
# Before CamiTK 5.0
InteractiveViewer::get3DViewer()->refresh();
# Since CamiTK 5.0
Application::refresh();
# or (although this is not recommended as there is no benefit to call `refresh()` on a specific viewer,
# even for performance reason, and as this might introduce discrepancy in visual feedback)
Application::getViewer("3D Viewer")->refresh();
viewer specific use case 2
# Before CamiTK 5.0
InteractiveViewer::get3DViewer()->getRendererWidget()->removeProp(oldProp);
# Since CamiTK 5.0
InteractiveGeometryViewer* default3DViewer = dynamic_cast<InteractiveGeometryViewer*>(Application::getViewer("3D Viewer"))
if (default3DViewer != nullptr) {
default3DViewer->getRendererWidget()->removeProp(oldVolume);
)
viewer specific use case 3
# Before CamiTK 5.0
QObject::connect(InteractiveViewer::get3DViewer(), SIGNAL(selectionChanged()), this, SLOT(apply()));
# Since CamiTK 5.0
QObject::connect(Application::getViewer("3D Viewer"), SIGNAL(selectionChanged()), this, SLOT(apply()));
viewer specific use case 4
# Before CamiTK 5.0
MedicalImageViewer::getInstance()->setVisibleViewer(MedicalImageViewer::VIEWER_CORONAL);
# Since CamiTK 5.0
MedicalImageViewer* medicalImageViewer = dynamic_cast<MedicalImageViewer*>(Application::getViewer("Medical Image Viewer"));
if (medicalImageViewer != nullptr) {
medicalImageViewer->setVisibleViewer(MedicalImageViewer::VIEWER_CORONAL);
}
viewer specific use case 5
# Before CamiTK 5.0
myNewViewer = InteractiveViewer::getNewViewer(QString("My New Viewer"), InteractiveViewer::SLICE_VIEWER);
# Since CamiTK 5.0,
// Viewer type is specified by its name (see table above)
myNewViewer = Application::getNewViewer("My New Viewer", "InteractiveSliceViewer");
Migration troubleshooting
Component not updated in the explorer
My action is instantiating a new component on the fly, but it does not appear in the explorer
As viewers are not directly linked with CamiTK Core anymore, you now need to call Application::refresh()
after instantiating a new Component
(or a subclass) on the fly (e.g., if there is an instantiation of a new ImageComponent
during an image acquisition, call Application::refresh()
just after the instanciation).
viewer’s include file not found
If you get an error about a viewer include not found. E.g.:
fatal error: MedicalImageViewer.h: No such file or directory
xxx | #include <MedicalImageViewer.h>
You just have to add the following option in the camitk_extension
macro to specify that your extension requires a specific viewer extension (this is the same mechanism as if your extension depends on an action or component extension):
camitk_extension(
...
NEEDS_VIEWER_EXTENSION medicalimageviewer
)
getInstance()
not found
If you get an error about the getInstance()
method of viewer class not found. E.g.:
error: ‘getInstance’ is not a member of MedicalImageViewer
xxx | MedicalImageViewer::getInstance()->setVisibleViewer(MedicalImageViewer::VIEWER_CORONAL)
You just have to replace the corresponding line by:
Application::getViewer("Medical Image Viewer")->setVisibleViewer(MedicalImageViewer::VIEWER_CORONAL);
As the viewers are now provided through extensions, we strongly advise you to check the return value of Application::getViewer("Medical Image Viewer")
first.
So a complete safer replacement could be:
MedicalImageViewer* medicalImageViewer = dynamic_cast<MedicalImageViewer*>(Application::getViewer("Medical Image Viewer"));
if (medicalImageViewer != nullptr) {
medicalImageViewer->setVisibleViewer(MedicalImageViewer::VIEWER_CORONAL);
}
else {
CAMITK_WARNING("Medical Image Viewer required.")
}
get3DViewer
(or getAxialViewer
, getCoronalViewer
, or getSagittalViewer
) is not a member of camitk::InteractiveViewer
As all the previously available viewers are now extensions, the getters of InteractiveViewer
default instances are also available through their corresponding extensions.
So if you get the following error (or similar):
error: ‘get3DViewer’ is not a member of ‘camitk::InteractiveViewer’
xxx | InteractiveViewer::get3DViewer()->...; // ... corresponds a specific action to perform (i.e., a call to one of the viewer's specific method)
Just replace the affected line with
InteractiveViewer* viewer3D = dynamic_cast<InteractiveViewer*>(Application::getViewer("3D Viewer"));
if (viewer3D != nullptr) {
viewer3D->...; // replace ... by the expression that you were using in your code
}
else {
CAMITK_WARNING("3D Interactive Viewer extension not available but required")
}
The following table gives the CamiTK 5 corresponding calls for default InteractiveViewer
instances:
In CamiTK 4 | In CamiTK 5 |
---|---|
InteractiveViewer::get3DViewer() |
Application::getViewer("3D Viewer") |
InteractiveViewer::getAxialViewer() |
Application::getViewer("Axial Viewer") |
InteractiveViewer::getCoronalViewer() |
Application::getViewer("Coronal Viewer") |
InteractiveViewer::getSagittalViewer() |
Application::getViewer("Sagittal Viewer") |
You will also probably need to add the dependency to the viewer using NEEDS_VIEWER_EXTENSION
as explained above.
As viewers are now extensions, it means that in some (rare) cases the viewer extension might not have been loaded.
In this case any extension that uses the viewer should not have been loaded either.
In some extremely rare cases (that are theoretical and have not yet occured out in pratical life), the call to Application::getViewer
will result in a crash.
To avoid this unexpected potential problem and avoid static analysis tool warnings, it is better to check the result of Application::getViewer
against nullptr
.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.