Medical Imaging Interaction Toolkit  2023.04.00
Medical Imaging Interaction Toolkit
A deeper look into the IGT Plugins

We already know the three views of the IGT tracking plugin from the first tutorial step (The IGT Tracking Plugin): The MITK-IGT Tracking Toolbox , The MITK-IGT Navigation Tool Manager and The NavigationData Player . While the first tutorial step only dealt with the usage of these views, we now want to have a deeper look at some parts of the code.

IGT Widgets

A lot of the IGT functionality is available as a widget. So if you need some part of it for your own plugin, you might just want to include the widget. This is for example done for the QmitkNavigationToolCreationWidget, which is used in the The MITK-IGT Tracking Toolbox view as well as in the The MITK-IGT Navigation Tool Manager view.


Data access

The fist important question is, how to get the tools you want to use for your application. The most simple way is using the The MITK-IGT Navigation Tool Manager to load, create or edit the tools and access it via the NavigationToolStorage. It's provided by a microservice and you can access it e.g. via the module context (make sure, your module depends on IGT):

  us::ModuleContext* context = us::GetModuleContext();
  std::vector<us::ServiceReference <mitk::NavigationToolStorage> > refs = context->GetServiceReferences<mitk::NavigationToolStorage>();
  m_ToolStorage = context->GetService<mitk::NavigationToolStorage>(refs.front());

Then, simply access the tools by

  m_ToolStorage->GetTool();

There are also many different functions available, e.g. providing the number of available tools or the name of the current tracking device. Just have a look at the documentation of the mitk::NavigationToolStorage if you are interested in it.

Autoload all parameters from last session

To increase the usability of the application, the tracking toolbox restores all parameters from the last session. Therefore, we will use the following code to save all parameters. It is also used in the specific tracking device widgets, but we will just have a look at one example, restoring some parameters like the Show-Tracking-Volume-Checkbox, the filename of the autosave path of the tool storage or the index of the tracking volume.

To store the settings, the following code is used:

void QmitkMITKIGTTrackingToolboxView::StoreUISettings()
{
// persistence service does not directly work in plugins for now
// -> using QSettings
QSettings settings;
settings.beginGroup(QString::fromStdString(VIEW_ID));
MITK_DEBUG << "Store UI settings";
// set the values of some widgets and attrbutes to the QSettings
settings.setValue("ShowTrackingVolume", QVariant(m_Controls->m_ShowTrackingVolume->isChecked()));
settings.setValue("toolStorageFilename", QVariant(m_ToolStorageFilename));
settings.setValue("VolumeSelectionBox", QVariant(m_Controls->m_VolumeSelectionBox->currentIndex()));
settings.setValue("SimpleModeEnabled", QVariant(m_SimpleModeEnabled));
settings.setValue("OpenIGTLinkDataFormat", QVariant(m_Controls->m_OpenIGTLinkDataFormat->currentIndex()));
settings.setValue("EnableOpenIGTLinkMicroService", QVariant(m_Controls->m_EnableOpenIGTLinkMicroService->isChecked()));
settings.endGroup();
}

We will reload it with

void QmitkMITKIGTTrackingToolboxView::LoadUISettings()
{
// persistence service does not directly work in plugins for now -> using QSettings
QSettings settings;
settings.beginGroup(QString::fromStdString(VIEW_ID));
// set some widgets and attributes by the values from the QSettings
m_Controls->m_ShowTrackingVolume->setChecked(settings.value("ShowTrackingVolume", false).toBool());
m_Controls->m_EnableOpenIGTLinkMicroService->setChecked(settings.value("EnableOpenIGTLinkMicroService", true).toBool());
m_Controls->m_VolumeSelectionBox->setCurrentIndex(settings.value("VolumeSelectionBox", 0).toInt());
m_Controls->m_OpenIGTLinkDataFormat->setCurrentIndex(settings.value("OpenIGTLinkDataFormat", 0).toInt());
m_ToolStorageFilename = settings.value("toolStorageFilename", QVariant("")).toString();
if (settings.value("SimpleModeEnabled", false).toBool()) { this->OnToggleAdvancedSimpleMode(); }
settings.endGroup();

Load Tool Storage

This code will load a previously stored or autosaved tool storage:

// try to deserialize the tool storage from the given tool storage file name
if (!m_ToolStorageFilename.isEmpty())
{
// try-catch block for exceptions
try
{
mitk::NavigationToolStorageDeserializer::Pointer myDeserializer = mitk::NavigationToolStorageDeserializer::New(GetDataStorage());
m_toolStorage->UnRegisterMicroservice();
m_toolStorage = myDeserializer->Deserialize(m_ToolStorageFilename.toStdString());
m_toolStorage->RegisterAsMicroservice();
//update label
UpdateToolStorageLabel(m_ToolStorageFilename);
//update tool preview
m_Controls->m_TrackingToolsStatusWidget->RemoveStatusLabels();
m_Controls->m_TrackingToolsStatusWidget->PreShowTools(m_toolStorage);
}
catch (const mitk::IGTException& e)
{
MITK_WARN("QmitkMITKIGTTrackingToolBoxView") << "Error during restoring tools. Problems with file (" << m_ToolStorageFilename.toStdString() << "), please check the file? Error message: "<<e.GetDescription();
this->OnResetTools(); //if there where errors reset the tool storage to avoid problems later on
}
}

Threaded Tracking

Usually, the tracking is done in a separate thread in order to still allow parallel usage of the workbench. If we track the devices in the same thread, the workbench might freeze. That's why we use thread workers

//initialize worker thread
m_WorkerThread = new QThread();
m_Worker = new QmitkMITKIGTTrackingToolboxViewWorker();

...which are connected to functions

//connect worker thread
connect(m_Worker, SIGNAL(AutoDetectToolsFinished(bool, QString)), this, SLOT(OnAutoDetectToolsFinished(bool, QString)));
connect(m_Worker, SIGNAL(ConnectDeviceFinished(bool, QString)), this, SLOT(OnConnectFinished(bool, QString)));
connect(m_Worker, SIGNAL(StartTrackingFinished(bool, QString)), this, SLOT(OnStartTrackingFinished(bool, QString)));
connect(m_Worker, SIGNAL(StopTrackingFinished(bool, QString)), this, SLOT(OnStopTrackingFinished(bool, QString)));
connect(m_Worker, SIGNAL(DisconnectDeviceFinished(bool, QString)), this, SLOT(OnDisconnectFinished(bool, QString)));
connect(m_WorkerThread, SIGNAL(started()), m_Worker, SLOT(ThreadFunc()));
connect(m_Worker, SIGNAL(ConnectDeviceFinished(bool, QString)), m_Controls->m_ConfigurationWidget, SLOT(OnConnected(bool)));
connect(m_Worker, SIGNAL(DisconnectDeviceFinished(bool, QString)), m_Controls->m_ConfigurationWidget, SLOT(OnDisconnected(bool)));
connect(m_Worker, SIGNAL(StartTrackingFinished(bool, QString)), m_Controls->m_ConfigurationWidget, SLOT(OnStartTracking(bool)));
connect(m_Worker, SIGNAL(StopTrackingFinished(bool, QString)), m_Controls->m_ConfigurationWidget, SLOT(OnStopTracking(bool)));
//Add Listener, so that we know when the toolStorage changed.
std::string m_Filter = "(" + us::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.NavigationToolStorage" + ")";
mitk::PluginActivator::GetContext()->connectServiceListener(this, "OnToolStorageChanged", QString(m_Filter.c_str()));
//move the worker to the thread
m_Worker->moveToThread(m_WorkerThread);

The thread will be startet, if we connect a tracking device by pushing the connect button or start tracking etc:

//initialize worker thread
m_Worker->SetWorkerMethod(QmitkMITKIGTTrackingToolboxViewWorker::eConnectDevice);
m_Worker->SetTrackingDevice(this->m_Controls->m_ConfigurationWidget->GetTrackingDevice());
m_Worker->SetInverseMode(m_Controls->m_InverseMode->isChecked());
m_Worker->SetNavigationToolStorage(this->m_toolStorage);
m_Worker->SetTrackingDeviceData(data);
//start worker thread
m_WorkerThread->start();

...and finished afterwards:

m_WorkerThread->quit();
m_WorkerThread->wait();

You can access the data from the worker thread if needed:

//get data from worker thread
m_TrackingDeviceData = m_Worker->GetTrackingDeviceData();
m_ToolVisualizationFilter = m_Worker->GetToolVisualizationFilter();
if( m_ToolVisualizationFilter.IsNotNull() )
{
//Connect the NeedleProjectionFilter to the ToolVisualizationFilter as third filter of the IGT pipeline
m_NeedleProjectionFilter->ConnectTo(m_ToolVisualizationFilter);
if (m_Controls->showHideToolProjectionCheckBox->isChecked())
{
ShowToolProjection(m_Controls->m_toolselector->currentIndex());
}
}

Which of the functions is finally executed will be chosen in the ThreadFunc:

void QmitkMITKIGTTrackingToolboxViewWorker::ThreadFunc()
{
switch (m_WorkerMethod)
{
case eAutoDetectTools:
this->AutoDetectTools();
break;
case eConnectDevice:
this->ConnectDevice();
break;
case eStartTracking:
this->StartTracking();
break;
case eStopTracking:
this->StopTracking();
break;
case eDisconnectDevice:
this->DisconnectDevice();
break;
default:
MITK_WARN << "Undefined worker method was set ... something went wrong!";
break;
}
}

To deconstruct the workers, we first terminate the threads and then delete them

// wait for thread to finish
m_WorkerThread->terminate();
m_WorkerThread->wait();
//clean up worker thread
if (m_WorkerThread) { delete m_WorkerThread; }
if (m_Worker) { delete m_Worker; }

Return to the [IGT Tutorial Overview]

MITK_DEBUG
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
mitk::IGTException
An object of this class represents an exception of the MITK-IGT module.
Definition: ExceptionHandling/mitkIGTException.h:25
MITK_WARN
#define MITK_WARN
Definition: mitkLogMacros.h:19
us::ServiceConstants::OBJECTCLASS
const US_Core_EXPORT std::string & OBJECTCLASS()