330 likes | 392 Views
Port of 3D Slicer to Qt. Julien Finet & Jean-Christophe Fillion -Robin Kitware Inc. June 22 th 2010. Background. Slicer version 3.x use KWWidgets VTK-style interface to Tk 3D Slicer 1.x, 2.x used Tk directly Qt
E N D
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/Slicer4:Build_Instructions
Events with KWWidgets • Connect vtkCallbackCommand vtkObject::AddObserver • Fire event vtkObject::InvokeEvent() • Process myClass::myMethod()
Events with KWWidgets Object2 Callback Node Selected Event Object 1 Object 2
Events with KWWidgets 1) Connect this->CallbackCommand = vtkCallbackCommand::New(); this->CallbackCommand->SetClientData(object2); this->CallbackCommand->SetCallback(vtkObject2::MethodX); Object2 Callback Node Selected Event Object 1 Object 2 MethodX
Events with KWWidgets 1) Connect object1>AddObserver(vtkObject1::NodeSelectedEvent, this>CallbackCommand); Object2 Callback Node Selected Event Object 1 Object 2
Events with KWWidgets 2) Signal void vtkObject1::Method2(){ … this->InvokeEvent(vtkObject1::NodeSelectedEvent, NULL); } Object2 Callback Node Selected Event Object 1 Object 2 Method2
Events with KWWidgets 3) Process void vtkObject2::MethodX(vtkObject *caller, unsigned long event, void *callData ) { if (vtkObject2::SafeDownCast(caller) && event == vtkObject2::NodeSelectedEvent) { … Object2 Callback Node Selected Event Object 1 Object 2 MethodX
Events with Qt • Connect QObject::connect(obj1,signal, obj2, slot); • Fire event emit mySignal(); • 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 QVTK_OBJECT adds the function qvtkConnect() Usage // 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); }; Don’t forget to declare the private class friend 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); 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; } … } MyButton* p = ctk_p() MyButtonPrivate* d = ctk_d()
Slicer3 vsSlicerQt • SlicerQtQt only • Slicer3 KWWidgets + Qt
Slicer Architecture Slicer Base/Logic MRML CTK Core QtCore Base/QTCore Base/QTGUI qMRMLWidgets CTK Widgets QtGUI
Modules • Core Modules: Slicer3/Base/QTCoreModules • Transforms qSlicerTransformsModule • … • Loadable Modules: Slicer3/QTModules • TractographyFiducialSeedinglibqSlicerTractographyFiducialSeedingModule.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: … }; Plugin header … Q_EXPORT_PLUGIN2(qSlicerVolumesModule, qSlicerVolumesModule); … Plugin implementation QPluginLoader loader; loader.setFileName(pluginPath); loader.load(); QObject * object = this->Loader.instance(); qSlicerAbstractLoadableModule* module = qobject_cast<qSlicerAbstractLoadableModule*>(object); Plugin Loader
Loadable Modules: Logic + UI Module Plugin (qSlicerAbstractModule) create() create() Module Widget (qSlicerAbstractModuleWidget) Logic (vtkSlicerModuleLogic) Module Designer UI qSlicer…ModuleWidget.ui vtkMRMLScene
CTK Widgets commontk.org • http://www.commontk.org/index.php/Documentation/ImageGallery ctkFixedTitleComboBox ctkCollapsibleGroupBox ctkCollapsibleButton ctkTreeComboBox ctkColorPickerButton
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/qMRMLNodeSelectorPlugin.[h|cxx] • More info on • http://wiki.slicer.org/slicerWiki/index.php/Slicer3:Developers:Projects:QtSlicer/Tutorials/WidgetWriting
Widget Example class QCTK_WIDGETS_EXPORT qCTKCollapsibleButton : public QAbstractButton { Q_OBJECT Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed) Q_PROPERTY(intcollapsedHeight READ collapsedHeight WRITE setCollapsedHeight) … public: void setCollapsed(bool); bool collapsed()const; void setCollapsedHeight(int); intcollapsedHeight()const; Qt Designer 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/Slicer3:Developers:Projects:QtSlicer