Medical Imaging Interaction Toolkit  2024.06.00
Medical Imaging Interaction Toolkit
Creating the CustomViewer plugin

As we want to develop our Custom Viewer as part of BlueBerry to demonstrate the customization capabilities of the application framework, we have to integrate the components of our example application as BlueBerry plugins in order to make the application framework's functionalities available to our application. For example plugin startup with BlueBerry, a convenience application called BlueBerryExampleLauncher is already provided in the Examples/BlueBerryExampleLauncher directory of MITK. This application acts as a BlueBerry loading mechanism for any example application provided.

To make an example application startable by the BlueBerry example launcher, we have to provide a folder inside Examples/Plugins containing all files making up the application plugin itself. Additionally, we have to add entries to the PluginList.cmake in the Examples/Plugins directory for plugin registration and create a new file in the Examples/BlueBerryExampleLauncher/Configurations folder containing the plugin itself as well as any further required plugin.

The resulting plugin folder for our custom viewer application consists of several subfolders and files:

The directory structure for the CustomViewer plugin.


We can see a documentation and resources folder, a source folder containing all source files, some cmake-related files and a plugin.xml file for the BlueBerry related plugin extension and extension-point declarations.

Next, we add some source code to our plugin folder. First, we need a class for our CustomViewer application itself which we derive from the berry:IApplication application class:

In short, this class acts as an entry point for the BlueBerry application runtime. It defines what the application does when it is started (Start()-Method) and before it is ended (Stop()-Method). Our Start()-Method creates a BlueBerry display for GUI-rendering and a WorkbenchAdvisor for workbench control. Then the BlueBerry workbench is created and run given the created display and WorkbenchAdvisor:

wbAdvisor.reset(new berry::WorkbenchAdvisor);
int code = berry::PlatformUI::CreateAndRunWorkbench(display, wbAdvisor.data());
// exit the application with an appropriate return code
return code == berry::PlatformUI::RETURN_RESTART ? EXIT_RESTART : EXIT_OK;

The Stop()-method does not need to be further defined. In addition, a default perspective identifier is given to define an initial perspective to be shown by the WorkbenchWindow.

Later, we well need a proper WorkbenchAdvisor class derived from berry::WorkbenchAdvisor. In it, we will create a WorkbenchWindowAdvisor, which on his part is used to control the WorkbenchWindow's GUI creation. For now, we simply use the berry::WorkbenchWindowAdvisor, which creates the GUI in a default way. As the development of our custom viewer advances, we will want to take part in the GUI creation ourselves, so we will need to customize our own WorkbenchWindowAdvisor accordingly.

Now we create a class named ViewerPerspective which is derived from berry::IPerspectiveFactory:

class ViewerPerspective : public QObject, public berry::IPerspectiveFactory
{
Q_OBJECT
public:
ViewerPerspective();
};

It acts as an initial perspective to be displayed as part of our bulk application plugin. For now, the perspective will remain empty and can, alongside the definition of further perspectives, be provided with views later by overwriting the CreateInitialLayout()-Method.

Finally, we need a ctkPluginActivator derived class which is used to customize the starting and stopping of our plugin:

class org_mitk_example_gui_customviewer_Activator : public QObject, public ctkPluginActivator
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org_mitk_example_gui_customviewer")
Q_INTERFACES(ctkPluginActivator)
public:
void start(ctkPluginContext *context) override;
void stop(ctkPluginContext *context) override;
static ctkPluginContext *GetPluginContext();
private:
static ctkPluginContext *PluginContext;
};

During plugin-start, we register our plugin classes given the ctkPluginContext by overwriting the start()-Method.

void org_mitk_example_gui_customviewer_Activator::start(ctkPluginContext *context)
{
BERRY_REGISTER_EXTENSION_CLASS(CustomViewer, context)
BERRY_REGISTER_EXTENSION_CLASS(ViewerPerspective, context)
PluginContext = context;
}

In order to connect our application and its perspectives to the BlueBerry components we have to declare the according extension-point-contributions inside the plugin.xml file. As can be seen in the image below, our application and its perspectives contribute to the org.blueberry.osgi.applications and org.blueberry.ui.perspectives extension points respectively.

Extension point contributions of our initial custom viewer application


When we start the BlueBerryExampleLauncher (either directly or via the provided batch files), we can now choose our bulk application (among others present), and a window is presented showing our empty initial perspective.

Our first application window showing one single perspective


Go to the previous page Introduction. Or proceed to the next page Main Window Layout: ViewerPerspective and DicomPerspective.

BERRY_REGISTER_EXTENSION_CLASS
#define BERRY_REGISTER_EXTENSION_CLASS(_ClassType, _PluginContext)
Definition: berryMacros.h:78
berry::Display
Definition: berryDisplay.h:23
berry::SmartPointer
Implements transparent reference counting.
Definition: berryICommandCategoryListener.h:21
berry::WorkbenchAdvisor
Definition: berryWorkbenchAdvisor.h:92
berry::PlatformUI::CreateAndRunWorkbench
static int CreateAndRunWorkbench(Display *display, WorkbenchAdvisor *advisor)
berry::IPerspectiveFactory::CreateInitialLayout
virtual void CreateInitialLayout(IPageLayout::Pointer layout)=0
berry::IPerspectiveFactory
Definition: berryIPerspectiveFactory.h:82
berry::PlatformUI::RETURN_RESTART
static const int RETURN_RESTART
Definition: berryPlatformUI.h:64
berry::PlatformUI::CreateDisplay
static Display * CreateDisplay()