QtPortInSlicer - How to write a module

Download Report

Transcript QtPortInSlicer - How to write a module

Port of 3D Slicer to Qt
Julien Finet & Jean-Christophe Fillion-Robin
Kitware Inc.
June 22th 2010
Background
• Slicer version 3.x use KWWidgets
– VTK-style interface to Tk
– 3D Slicer 1.x, 2.x used Tk directly
• Qt
– Embedded Linux, Mac OS X, Windows,
Linux/X11, Windows CE/Mobile, Symbian,
Maemo
– Commercial/LGPL
– 600+ classes
– Tens of thousands of applications
– 15+ millions of users
Qt – How to get Qt
• Required version: Qt 4.6.2
• Building Slicer with Qt
– Use Superbuild
– http://www.slicer.org/slicerWiki/index.php/Slice
r4:Build_Instructions
Events with KWWidgets
1. Connect
vtkCallbackCommand
vtkObject::AddObserver
2. Fire event
vtkObject::InvokeEvent()
3. Process
myClass::myMethod()
Events with KWWidgets
Node
Selected
Event
Object 1
Object2
Callback
Object 2
Events with KWWidgets
1) Connect
this->CallbackCommand = vtkCallbackCommand::New();
this->CallbackCommand->SetClientData(object2);
this->CallbackCommand->SetCallback(vtkObject2::MethodX);
Node
Selected
Event
Object 1
Object2
Callback
Object 2
MethodX
Events with KWWidgets
1) Connect
object1>AddObserver(vtkObject1::NodeSelectedEvent,
this>CallbackCommand);
Node
Selected
Event
Object 1
Object2
Callback
Object 2
Events with KWWidgets
2) Signal
void vtkObject1::Method2(){
…
this->InvokeEvent(vtkObject1::NodeSelectedEvent, NULL);
}
Node
Selected
Event
Object 1
Method2
Object2
Callback
Object 2
Events with KWWidgets
3) Process
void vtkObject2::MethodX(vtkObject *caller, unsigned long event, void *callData )
{
if (vtkObject2::SafeDownCast(caller) && event == vtkObject2::NodeSelectedEvent)
{
…
Node
Selected
Event
Object 1
Object2
Callback
Object 2
MethodX
Events with Qt
1. Connect
QObject::connect(obj1,signal, obj2, slot);
2. Fire event
emit mySignal();
3. Process
myClass::mySlot();
Events with Qt
Node
Selected
Signal
Object 1
Object 2
Events with Qt
1) Connect
this->QObject::connect(obj1, SIGNAL(nodeSelected(vtkMRMLNode*)),
obj2, SLOT(methodX(vtkMRMLNode*)));
Node
Selected
Signal
Object 1
Object 2
Events with Qt
2) Signal
void vtkObject1::Method2(){
…
emit this->currentNodeSelected(node);
}
Node
Selected
Signal
Object 1
Object 2
Events with Qt
3) Process
void vtkObject2::MethodX(vtkMRMLNode* node)
{
…
}
Node
Selected
Signal
Object 1
Object 2
MethodX
Qt and VTK: qVTKConnect
• qvtkConnect(
vtkObject* vtk_obj,
unsigned long vtk_event,
const QObject* qt_obj,
const char* qt_slot,
float priority = 0.0)
• Also: qvtkReconnect, qvtkDisconnect,
qvtkDisconnectAll…
Qt and VTK: qVTKConnect
Usage
QVTK_OBJECT adds
the function qvtkConnect()
…
// CTK includes
#include <ctkVTKObject.h>
…
class MyWidget: public QWidget
{
Q_OBJECT
QVTK_OBJECT
public:
void setNode(vtkMRMLNode* mrmlNode);
protected slots:
void onMRMLNodeModified(vtkObject* sender);
};
void MyWidget
::setNode(vtkMRMLNode* mrmlNode)
{
…
qvtkConnect(
mrmlNode,
vtkCommand::ModifiedEvent,
this,
SLOT(onMRMLNodeModified(vtkObject*)));
…
}
void MyWidget
::onMRMLNodeModified(vtkObject* sender)
{
vtkMRMLNode* node =
vtkMRMLNode::SafeDowncast(sender);
…
}
Interaction between MRML & Qt
valueChanged
(double)
Modified
Event
Qt Slider
MRML Node
Module
Widget
void onNodeModified( vtkMRMLNode* node)
{
slider->setValue(node->GetValue());
}
void onValueChanged(double newValue)
{
node->SetValue(newValue);
}
Private Implementation
• Hide the implementation details of an
interface
• http://en.wikipedia.org/wiki/Opaque_pointer
#include "qCTKPimpl.h"
…
class MyButtonPrivate;
…
class MyButton : public QAbstractButton
{
Q_OBJECT
public:
…
private:
CTK_DECLARE_PRIVATE(MyButton);
};
friend
Don’t forget to declare the private class
class MyButtonPrivate;
ctkPrivateInterface<MyButton, MyButtonPrivate> ctk_d;
Private Implementation
class MyButtonPrivate : public
qCTKPrivate<MyButton>
{
public:
CTK_DECLARE_PUBLIC(MyButton);
void init();
bool Collapsed;
…
};
void MyButtonPrivate::init()
{
CTK_P(MyButton);
MyButton* p = ctk_p()
p->setCheckable(true);
this->Collapsed = false;
}
MyButtonPrivate* d = ctk_d()
MyButton::MyButton(QWidget* parent)
:QAbstractButton(parent)
{
CTK_INIT_PRIVATE(MyButton);
ctk_d()->init();
}
void MyButton::collapse(bool c)
{
CTK_D(MyButton);
if (c == d->Collapsed)
{
return;
}
…
}
Slicer3 vs SlicerQt
• SlicerQt
Qt only
• Slicer3
KWWidgets + Qt
Slicer Architecture
Base/Logic
Base/QTCore
Base/QTGUI
MRML
Slicer
qMRML
Widgets
CTK
Core
QtCore
CTK
Widgets
QtGUI
Modules
• Core Modules: Slicer3/Base/QTCoreModules
– Transforms qSlicerTransformsModule
–…
• Loadable Modules: Slicer3/QTModules
– TractographyFiducialSeeding
libqSlicerTractographyFiducialSeedingModule.so
– Volumes libqSlicerVolumesModule.so
– …
• CLI Modules: Slicer3/Applications/CLI
–…
QTCLI
• Same idea than KWWidgets
– Parse XML to build UI
• Support
– Shared Libraries
– Executables
– Python
UI panel in Slicer
3) Dynamically created panel
QTCLI: Example
1) Xml description
…
<parameters>
<label>Registration Parameters</label>
<description>Parameters used for registration</description>
<integer>
<name>HistogramBins</name>
<flag>b</flag>
<longflag>histogrambins</longflag>
<description>Number of histogram bins to use for Mattes
Mutual Information. </description>
<label>Histogram Bins</label>
<default>30</default>
<constraints>
<minimum>1</minimum>
<maximum>500</maximum>
<step>5</step>
</constraints>
</integer>
…
…
If (paramType == “integer”)
{
QSlider* intParameter = new QSlider(registrationParameters);
intParameter->setMinimum(paramMin);
intParameter->setMaximum(paramMax);
intParameter->setStep(paramStep);
intParameter->setValue(paramValue);
QObject::connect(intParameter, SIGNAL(valueChanged(int)), this, SIGNAL(onParamValueChanged(int)));
}
else if (paramType == “double”)
…
2) Parse parameters and generate UI
Plugin Mechanism
• Previously: itksys::DynamicLoader(dlopen)
• Now: Use the QT Plugins framework
…
class Q_SLICER_QTMODULES_VOLUMES_EXPORT qSlicerVolumesModule :
public qSlicerAbstractLoadableModule
{
Q_INTERFACES(qSlicerAbstractLoadableModule);
public:
…
};
…
Q_EXPORT_PLUGIN2(qSlicerVolumesModule, qSlicerVolumesModule);
…
QPluginLoader loader;
loader.setFileName(pluginPath);
loader.load();
QObject * object = this->Loader.instance();
qSlicerAbstractLoadableModule* module = qobject_cast<qSlicerAbstractLoadableModule*>(object);
Plugin header
Plugin implementation
Plugin Loader
Loadable Modules: Logic + UI
Module Plugin
(qSlicerAbstractModule)
create()
create()
Module Widget
(qSlicerAbstractModuleWidget)
Logic
(vtkSlicerModuleLogic)
Module Designer UI
qSlicer…ModuleWidget.ui
vtkMRMLScene
Widgets Awareness
Library
QTCore QtGui VTK
CTK/Libs/Core
Yes
CTK/Libs/Widgets
Yes
CTK/Libs/Visualization/VTK/Core
Yes
Yes
CTK/Libs/Visualization/VTK/Widgets
Yes
Yes
Slicer3/Libs/qMRMLWidgets
Yes
Slicer3/Base/QTCore
Yes
Slicer3/Base/QTGUI
Yes
MRML App.
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
CTK Widgets commontk.org
• http://www.commontk.org/index.php/Documentation/ImageGallery
ctkFixedTitleComboBox
ctkCollapsibleGroupBox
ctkCollapsibleButton
ctkColorPickerButton
ctkTreeComboBox
qMRMLWidgets
• http://wiki.slicer.org/slicerWiki/index.php/Slicer3:Developers:
Projects:QtSlicer/Gallery
• Usually contains the slot setMRMLScene(vtkMRMLScene*)
qMRMLNodeSelector
qMRMLListWidget
qMRMLWindowLevelWidget
qMRMLTreeWidget
Widgets in Qt Designer
• A plugin must be created
– Slicer3/Libs/qMRMLWidgets/Plugins/qMRMLNodeSel
ectorPlugin.[h|cxx]
• More info on
– http://wiki.slicer.org/slicerWiki/index.php/Slicer3:Devel
opers:Projects:QtSlicer/Tutorials/WidgetWriting
Widget Example
Qt Designer
class QCTK_WIDGETS_EXPORT qCTKCollapsibleButton :
public QAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed)
Q_PROPERTY(int collapsedHeight READ collapsedHeight
WRITE setCollapsedHeight)
…
public:
void setCollapsed(bool);
bool collapsed()const;
void setCollapsedHeight(int);
int collapsedHeight()const;
qCTKCollapsibleButton.h
10 = default value
void qCTKCollapsibleButtonPrivate::init()
{
QCTK_P(qCTKCollapsibleButton);
…
this->Collapsed = false;
…
this->CollapsedHeight = 10;
…
}
qCTKCollapsibleButton.cxx
Questions
• More info:
http://wiki.slicer.org/slicerWiki/index.php/Sli
cer3:Developers:Projects:QtSlicer