Upload
kylie-coffey
View
223
Download
3
Embed Size (px)
Citation preview
Port of 3D Slicer to Qt
Julien Finet & Jean-Christophe Fillion-RobinKitware 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/
Slicer4:Build_Instructions
Events with KWWidgets
1. Connect vtkCallbackCommand
vtkObject::AddObserver
2. Fire eventvtkObject::InvokeEvent()
3. ProcessmyClass::myMethod()
Events with KWWidgets
Object 1 Object 2
Object2 Callback
Node Selected
Event
Events with KWWidgets 1) Connect
Object 1 Object 2MethodX
Node Selected
Event
this->CallbackCommand = vtkCallbackCommand::New();this->CallbackCommand->SetClientData(object2); this->CallbackCommand->SetCallback(vtkObject2::MethodX);
Object2 Callback
Object2 Callback
Events with KWWidgets 1) Connect
Object 1 Object 2
object1>AddObserver(vtkObject1::NodeSelectedEvent, this>CallbackCommand);
Node Selected
Event
Events with KWWidgets 2) Signal
Object 1 Object 2Method2
void vtkObject1::Method2(){ … this->InvokeEvent(vtkObject1::NodeSelectedEvent, NULL);}
Object2 Callback
Node Selected
Event
void vtkObject2::MethodX(vtkObject *caller, unsigned long event, void *callData ){ if (vtkObject2::SafeDownCast(caller) && event == vtkObject2::NodeSelectedEvent) { …
Events with KWWidgets 3) Process
Object 1 Object 2MethodX
Node Selected
Event
Object2 Callback
Events with Qt
1. Connect QObject::connect(obj1,signal, obj2, slot);
2. Fire eventemit mySignal();
3. ProcessmyClass::mySlot();
Events with Qt
Object 1 Object 2
Node Selected
Signal
Events with Qt 1) Connect
Object 1 Object 2
this->QObject::connect(obj1, SIGNAL(nodeSelected(vtkMRMLNode*)), obj2, SLOT(methodX(vtkMRMLNode*)));
Node Selected
Signal
Events with Qt 2) Signal
Object 1 Object 2
Node Selected
Signal
void vtkObject1::Method2(){ … emit this->currentNodeSelected(node);}
Events with Qt 3) Process
Object 1 Object 2MethodX
Node Selected
Signal
void vtkObject2::MethodX(vtkMRMLNode* node){ …}
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// CTK includes#include <ctkVTKObject.h>…class MyWidget: public QWidget{ Q_OBJECT QVTK_OBJECTpublic: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); …}
QVTK_OBJECT adds the function qvtkConnect()
Interaction between MRML & Qt
MRML Node
Module Widget
Qt Slider
void onNodeModified( vtkMRMLNode* node){ slider->setValue(node->GetValue());}
Modified Event
void onValueChanged(double newValue){ node->SetValue(newValue);}
valueChanged(double)
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_OBJECTpublic: …private: CTK_DECLARE_PRIVATE(MyButton);}; friend class MyButtonPrivate;
ctkPrivateInterface<MyButton, MyButtonPrivate> ctk_d;
Don’t forget to declare the private class
Private Implementationclass MyButtonPrivate : public qCTKPrivate<MyButton>{public: CTK_DECLARE_PUBLIC(MyButton); void init(); bool Collapsed; …};
void MyButtonPrivate::init(){ CTK_P(MyButton); p->setCheckable(true); this->Collapsed = false;}
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; } …}MyButtonPrivate* d = ctk_d()
MyButton* p = ctk_p()
Slicer3 vs SlicerQt
• SlicerQtQt only
• Slicer3 KWWidgets + Qt
Slicer
qMRMLWidgets QtGUI
Slicer Architecture
QtCoreCTKCore
CTKWidgets
Base/QTCore
Base/QTGUI
Base/Logic MRML
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
QTCLI: Example…<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”) …
3) Dynamically created panel
1) Xml description
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 Loader
Plugin implementation
Plugin header
Loadable Modules: Logic + UI
Logic(vtkSlicerModuleLogic)
create()create() create()create()
Module Widget (qSlicerAbstractModuleWidget)
Module Plugin(qSlicerAbstractModule)
vtkMRMLSceneModule Designer UIqSlicer…ModuleWidget.ui
Widgets Awareness
Library QTCore QtGui VTK MRML App.
CTK/Libs/Core Yes
CTK/Libs/Widgets Yes Yes
CTK/Libs/Visualization/VTK/Core Yes Yes
CTK/Libs/Visualization/VTK/Widgets Yes Yes
Slicer3/Libs/qMRMLWidgets Yes Yes Yes Yes
Slicer3/Base/QTCore Yes Yes Yes Yes
Slicer3/Base/QTGUI Yes Yes Yes Yes Yes
CTK Widgets commontk.org
• http://www.commontk.org/index.php/Documentation/ImageGallery
ctkCollapsibleButton
ctkCollapsibleGroupBox
ctkColorPickerButton ctkTreeComboBox
ctkFixedTitleComboBox
qMRMLWidgets• http://wiki.slicer.org/slicerWiki/index.php/
Slicer3:Developers:Projects:QtSlicer/Gallery• Usually contains the slot setMRMLScene(vtkMRMLScene*)
qMRMLNodeSelector
qMRMLTreeWidget
qMRMLListWidget
qMRMLWindowLevelWidget
Widgets in Qt Designer
• A plugin must be created– Slicer3/Libs/qMRMLWidgets/Plugins/qMRMLNodeSelectorPlugin.[h|cxx]
• More info on– http://wiki.slicer.org/slicerWiki/index.php/Slicer3:Developers:Projects:QtSlicer/Tutorials/WidgetWriting
Widget ExampleQt 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
void qCTKCollapsibleButtonPrivate::init(){ QCTK_P(qCTKCollapsibleButton); … this->Collapsed = false; … this->CollapsedHeight = 10; …} qCTKCollapsibleButton.cxx
10 = default value
Questions
• More info: http://wiki.slicer.org/slicerWiki/index.php/Slicer3:Developers:Projects:QtSlicer