3 D Slicer Qt Port Julien Finet Kitware

  • Slides: 38
Download presentation
3 D Slicer Qt Port Julien Finet Kitware Inc. Dec. 16 th 2009

3 D Slicer Qt Port Julien Finet Kitware Inc. Dec. 16 th 2009

Background • 3 D Slicer version 3. x use KWWidgets – VTK-style interface to

Background • 3 D Slicer version 3. x use KWWidgets – VTK-style interface to Tk – 3 D Slicer 1. x, 2. x used Tk directly • Qt 4. 6 – Embedded Linux, Mac OS X, Windows, Linux/X 11, Windows CE/Mobile, Symbian, Maemo – LGPL – 600+ classes – Tens of thousands of applications – 15+ millions of users • NIH Supplement to help with port – 9/17/2009 - 9/16/2011

Qt – How to get Qt • Required version: Qt 4. 6 • Building

Qt – How to get Qt • Required version: Qt 4. 6 • Building Slicer with Qt – http: //wiki. slicer. org/slicer. Wiki/index. php/Slicer 3: Developers: Projects: Qt. Slicer/Tutorials/Com pile. With. Qt • Links: – Doc: http: //qt. nokia. com/doc/4. 6/index. html – Tutorials: http: //qt. nokia. com/doc/4. 6/tutorials. html

Qt – First steps • • • Hello World Signals / Slots Events vs

Qt – First steps • • • Hello World Signals / Slots Events vs Signals/Slots Layouts What QObject does for you ? – Parent / child relationship • Designer • Not only GUI widgets in Qt

Qt in Slicer • Events with KWWidgets 1. Fire event 2. Connect 3. Process

Qt in Slicer • Events with KWWidgets 1. Fire event 2. Connect 3. Process void vtk. Slicer. Node. Selector. Widget: : Process. Command(char *selected. ID) { … this->Invoke. Event(vtk. Slicer. Node. Selector. Widget: : Node. Selected. Event, NULL); … } vtk. Slicer. Node. Selector. Widget. cxx void vtk. Slicer. Cameras. GUI: : Add. GUIObservers() { … this->View. Selector. Widget->Add. Observer( vtk. Slicer. Node. Selector. Widget: : Node. Selected. Event, (vtk. Command *)this->GUICallback. Command ); … } vtk. Slicer. Cameras. GUI. cxx void vtk. Slicer. Cameras. GUI: : Process. GUIEvents( vtk. Object *caller, unsigned long event, void *call. Data ) { if (vtk. Slicer. Node. Selector. Widget: : Safe. Down. Cast(caller)) { if (event == vtk. Slicer. Node. Selector. Widget: : Node. Selected. Event) { … } vtk. Slicer. Cameras. GUI. cxx

Qt in Slicer • Events with Qt 1. Fire event 2. Connect void q.

Qt in Slicer • Events with Qt 1. Fire event 2. Connect void q. MRMLNode. Selector: : node. Id. Selected(int index) { … emit current. Node. Changed(d->MRMLCurrent. Node); } q. MRMLNode. Selector. cxx void q. Slicer. Cameras. Module. Widget: : setup() { … connect(d->View. Node. Selector, SIGNAL(current. Node. Changed(vtk. MRMLNode*)), this, SLOT(on. Current. View. Node. Changed(vtk. MRMLNode*))); … } q. Slicer. Cameras. Module. Widget. cxx 3. Process void q. Slicer. Cameras. Module. Widget: : on. Current. View. Node. Changed(vtk. MRMLNode* mrml. Node) { vtk. MRMLView. Node* current. View. Node = vtk. MRMLView. Node: : Safe. Down. Cast(mrml. Node); … } q. Slicer. Cameras. Module. Widget. cxx

Qt and VTK • q. VTKConnect() – Based on VTK/GUISupport/Qt/vtk. Event. Qt. Slot. Connect

Qt and VTK • q. VTKConnect() – Based on VTK/GUISupport/Qt/vtk. Event. Qt. Slot. Connect // q. VTK includes #include <q. VTKObject. h> … class QMRML_WIDGETS_EXPORT q. MRMLNode. Selector : public q. CTKAdd. Remove. Combo. Box { Q_OBJECT QVTK_OBJECT public: … QVTK_OBJECT adds the function qvtk. Connect() q. MRMLNode. Selector. h void q. MRMLNode. Selector: : add. Node(vtk. MRMLNode* mrml. Node) { … this->qvtk. Connect(mrml. Node, vtk. Command: : Modified. Event, this, SLOT(on. MRMLNode. Modified(vtk. Object*, void*))); … } q. MRMLNode. Selector. cxx

Porting Slicer to Qt • Create Qt/KWW co-existence layer (done!) • Prototype a few

Porting Slicer to Qt • Create Qt/KWW co-existence layer (done!) • Prototype a few modules (done!) • Create an architecture for Qt based modules (in process) • Train developers (first session at January 2010 Project Week) • Port modules (ongoing through 2010…) • Turn off KWW (by end of 2010? ) • Continual Improvement (through end of supplement and beyond…)

Porting Slicer to Qt Executable: Slicer. QT • Slicer: Qt only Executable: Slicer 3

Porting Slicer to Qt Executable: Slicer. QT • Slicer: Qt only Executable: Slicer 3 • Slicer: KWWidgets + Qt

Modules • Core Modules: Slicer 3/Base/QTCore. Modules – q. Slicer. Transforms. Module • Loadable

Modules • Core Modules: Slicer 3/Base/QTCore. Modules – q. Slicer. Transforms. Module • Loadable Modules: Slicer 3/QTModules – Measurements – Volumes • CLI Modules

Plugin Mechanism • Previously: itksys: : Dynamic. Loader(dlopen) • Now: Use the QT Plugins

Plugin Mechanism • Previously: itksys: : Dynamic. Loader(dlopen) • Now: Use the QT Plugins framework … class Q_SLICER_QTMODULES_VOLUMES_EXPORT q. Slicer. Volumes. Module : public q. Slicer. Abstract. Loadable. Module { Q_INTERFACES(q. Slicer. Abstract. Loadable. Module); public: … }; … Q_EXPORT_PLUGIN 2(q. Slicer. Volumes. Module, q. Slicer. Volumes. Module); … QPlugin. Loader loader; loader. set. File. Name(plugin. Path); loader. load(); QObject * object = this->Loader. instance(); q. Slicer. Abstract. Loadable. Module* module = qobject_cast<q. Slicer. Abstract. Loadable. Module*>(object); Plugin header Plugin implementation Plugin Loader

Modules: Logic + UI Module (q. Slicer. Abstract. Module) QObject create() UI (q. Slicer.

Modules: Logic + UI Module (q. Slicer. Abstract. Module) QObject create() UI (q. Slicer. Abstract. Module. Widget) Logic (q. Slicer. Abstract. Module. Logic) QWidget vtk. MRMLScene

How to write a loadable module • Create directories in Slicer 3/QTModules – –

How to write a loadable module • Create directories in Slicer 3/QTModules – – My. Module/Resources/UI My. Module/Resources/Icons (optional) • Create the files – – – My. Module/CMake. Lists. txt My. Module/q. Slicer. My. Module. [h/cxx] My. Module/q. Slicer. My. Module. Widget. [h/cxx] My. Module/q. Slicer. My. Module. Logic. [h/cxx] (optional) My. Module/Resources/q. Slicer. My. Module. qrc (optional) My. Module/Resources/UI/q. Slicer. My. Module. ui

My. Module UI – 1 / 4 • Open Qt Designer with QT_PLUGIN_PATH set

My. Module UI – 1 / 4 • Open Qt Designer with QT_PLUGIN_PATH set to “Slicer 3 -build/bin” – Designing a module UI requires the plugins: q. CTKWidgets, q. VTKWidgets, q. MRMLWidgets and q. Slicer. Base. QTGUI – Warning: plugins must be compiled under the same mode than Qt (Release vs. Debug) – On Linux: cd Slicer 3 -build; python Designer. py – More info on http: //wiki. slicer. org/slicer. Wiki/index. php/Slicer 3: D evelopers: Projects: Qt. Slicer/Tutorials/Qt. Designer

My Module UI – 2 / 4 • Create a UI form: – My.

My Module UI – 2 / 4 • Create a UI form: – My. Module/Resources/UI/q. Slicer. My. Module. ui • q. Slicer. Widget has the signal mrml. Scene. Changed() Qt Designer

My. Module UI – 3 / 4 Drag widgets on the form Names are

My. Module UI – 3 / 4 Drag widgets on the form Names are important Qt Designer

My Module UI – 4 / 4 Connect widgets together with the signals/slots Here

My Module UI – 4 / 4 Connect widgets together with the signals/slots Here the MRMLScene of the module is propagated to the Node. Selector Qt Designer

My. Module Resources • If icons are used, they should be in – My.

My. Module Resources • If icons are used, they should be in – My. Module/Resources/Icons/ • Update the resource. qrc file – My. Module/Resources/q. Slicer. My. Module. qrc <!DOCTYPE RCC> <RCC version="1. 0"> <qresource> <file>Icons/My. Icon. png</file> … </qresource> </RCC> q. Slicer. My. Module. qrc

q. Slicer. My. Module. h #ifndef __q. Slicer. My. Module_h #define __q. Slicer. My.

q. Slicer. My. Module. h #ifndef __q. Slicer. My. Module_h #define __q. Slicer. My. Module_h // Slicer. QT includes #include "q. Slicer. Abstract. Loadable. Module. h“ #include "q. Slicer. My. Module. Win 32 Header. h“ // generated by CMake class q. Slicer. My. Module. Private; class Q_SLICER_QTMODULES_MYMODULE_EXPORT q. Slicer. My. Module : public q. Slicer. Abstract. Loadable. Module { Q_OBJECT public: q. Slicer. Transforms. Module(QObject *parent=0); virtual QString title()const { return “Transforms”; } virtual QString help. Text()const; virtual QString acknowledgement. Text()const; protected: // Create and return a widget representation of the object virtual q. Slicer. Abstract. Module. Widget * create. Widget. Representation(); virtual q. Slicer. Abstract. Module. Logic* create. Logic(); }; #endif q. Slicer. My. Module. h

q. Slicer. My. Module. cxx #include "q. Slicer. My. Module. h" // Slicer. QT

q. Slicer. My. Module. cxx #include "q. Slicer. My. Module. h" // Slicer. QT includes #include "q. Slicer. My. Module. Widget. h" // QT includes #include <Qt. Plugin> //--------------------------------------Q_EXPORT_PLUGIN 2(q. Slicer. My. Module, q. Slicer. My. Module); //--------------------------------------q. Slicer. Welcome. Module(QObject* parent) : public q. Slicer. Abstract. Loadable. Module(parent) { } //--------------------------------------q. Slicer. Abstract. Module. Widget * q. Slicer. Welcome. Module: : create. Widget. Representation() { return new q. Slicer. Welcome. Module. Widget; } //--------------------------------------q. Slicer. Abstract. Module. Widget * q. Slicer. Transforms. Module: : create. Widget. Representation() { return new q. Slicer. My. Module. Widget; } //--------------------------------------q. Slicer. Abstract. Module. Logic* q. Slicer. Transforms. Module: : create. Logic() { return 0; } Instantiate the UI widget q. Slicer. My. Module. cxx

q. Slicer. My. Module. Widget. h #ifndef __q. Slicer. My. Module. Widget_h #define __q.

q. Slicer. My. Module. Widget. h #ifndef __q. Slicer. My. Module. Widget_h #define __q. Slicer. My. Module. Widget_h // Slicer. QT includes #include "q. Slicer. Abstract. Module. Widget. h" // q. CTK includes #include <q. CTKPimpl. h> #include "q. Slicer. My. Module. Win 32 Header. h" Generated by CMake class q. Slicer. My. Module. Widget. Private; class Q_SLICER_QTMODULES_MYMODULE_EXPORT q. Slicer. My. Module. Widget : public q. Slicer. Abstract. Module. Widget { Q_OBJECT public: typedef q. Slicer. Abstract. Module. Widget Superclass; q. Slicer. My. Module. Widget(QWidget *parent=0); protected: virtual void setup(); Setup the UI private: QCTK_DECLARE_PRIVATE(q. Slicer. My. Module. Widget); }; #endif q. Slicer. My. Module. Widget. h

q. Slicer. My. Module. Widget. cxx #include "q. Slicer. My. Module. Widget. h" #include

q. Slicer. My. Module. Widget. cxx #include "q. Slicer. My. Module. Widget. h" #include "ui_q. Slicer. My. Module. h" //--------------------------------------struct q. Slicer. My. Module. Widget. Private: public q. CTKPrivate<q. Slicer. My. Module. Widget>, public Ui_q. Slicer. My. Module { }; Generated by CMake (via uic) from q. Slicer. My. Module. ui //--------------------------------------q. Slicer. My. Module. Widget : : q. Slicer. My. Module. Widget( QWidget* parent) : q. Slicer. Abstract. Module. Widget(parent) { … QCTK_INIT_PRIVATE(q. Slicer. My. Module. Widget); void setup. Ui(q. Slicer. Widget *q. Slicer. My. Module) } { … //--------------------------------------vertical. Layout = new QVBox. Layout(q. Slicer. My. Module); void q. Slicer. Welcome. Module. Widget: : setup() CTKCollapsible. Button = new q. CTKCollapsible. Button(q. Slicer. My. Module); { CTKCollapsible. Button->set. Collapsed(true); QCTK_D(q. Slicer. Welcome. Module. Widget); d->setup. Ui(this); horizontal. Layout = new QHBox. Layout(CTKCollapsible. Button); } label = new QLabel(CTKCollapsible. Button); horizontal. Layout->add. Widget(label); Creates the QWidgets } horizontal. Slider = new QSlider(CTKCollapsible. Button); … Ui_q. Slicer. My. Module. h

My. Module project • Create a CMake. Lists. txt in My. Module SET(qt_module_SRCS q.

My. Module project • Create a CMake. Lists. txt in My. Module SET(qt_module_SRCS q. Slicer. My. Module. cxx q. Slicer. My. Module. h q. Slicer. My. Module. Widget. cxx q. Slicer. My. Module. Widget. h ) Slicer 3_build_qtmodule( NAME “My. Module” TITLE “My Module” EXPORT_DIRECTIVE "Q_SLICER_QTMODULES_MYMODULE_EXPORT“ SRCS ${qt_module_SRCS} MOC_SRCS q. Slicer. My. Module. Widget. h UI_SRCS Resources/UI/q. Slicer. My. Module. ui TARGET_LIBRARIES ${qt_module_target_libraries} RESOURCES Resources/q. Slicer. My. Module. qrc ) QTModules/My. Module/CMake. Lists. txt • Add your module name in QTModules/CMake. Lists. txt

Tadam ! My. Module Panel here Slicer 3

Tadam ! My. Module Panel here Slicer 3

Module: with a logic and slots: q. Slicer. Transforms. Module. Widget class … q.

Module: with a logic and slots: q. Slicer. Transforms. Module. Widget class … q. Slicer. Transforms. Module. Widget : public q. Slicer. Abstract. Module. Widget { Q_OBJECT … public slots: void load. Transform(); … }; struct q. Slicer. Transforms. Module. Widget. Private: public q. CTKPrivate<q. Slicer. Transforms. Module. Widget>, public Ui_q. Slicer. Transforms. Module { q. Slicer. Transforms. Module. Logic* logic() const; }; void q. Slicer. Transforms. Module. Widget: : setup() { QCTK_D(q. Slicer. Transforms. Module. Widget); d->setup. Ui(this); … this->connect(d->Load. Transform. Push. Button, SIGNAL(clicked()), SLOT(load. Transform())); } Called by the “Load Transform” pushbutton void q. Slicer. Transforms. Module. Widget: : load. Transform() { QCTK_D(q. Slicer. Transforms. Module. Widget); QString file. Name = QFile. Dialog: : get. Open. File. Name(this); d->logic()->Add. Transform(file. Name); }

Widgets Library QT VTK MRML q. CTKWidgets Yes q. VTKWidgets Yes q. MRMLWidgets Yes

Widgets Library QT VTK MRML q. CTKWidgets Yes q. VTKWidgets Yes q. MRMLWidgets Yes Yes q. Slicer. Base. QTGUI Yes Yes

q. CTKWidgets • Common Toolkit (CTK) • Currently hosted on the Slicer repository q.

q. CTKWidgets • Common Toolkit (CTK) • Currently hosted on the Slicer repository q. CTKFixed. Title. Combo. Box q. CTKCollapsible. Group. Box q. CTKCollapsible. Button q. CTKColor. Picker. Button q. CTKTree. Combo. Box

q. MRMLWidgets • Depends on QT and MRML • Usually contains the slot set.

q. MRMLWidgets • Depends on QT and MRML • Usually contains the slot set. MRMLScene(vtk. MRMLScene*) q. MRMLNode. Selector q. MRMLList. Widget q. MRMLMatrix. Widget q. MRMLTree. Widget

q. CTKPimpl • Hide the implementation details of an interface • http: //en. wikipedia.

q. CTKPimpl • Hide the implementation details of an interface • http: //en. wikipedia. org/wiki/Opaque_pointe r // q. CTK includes #include "q. CTKPimpl. h" Don’t forget to declare the private class // QT includes #include <QAbstract. Button> class q. CTKCollapsible. Button. Private; class QCTK_WIDGETS_EXPORT q. CTKCollapsible. Button : public QAbstract. Button { Q_OBJECT public: q. CTKCollapsible. Button(QWidget *parent = 0); … private: QCTK_DECLARE_PRIVATE(q. CTKCollapsible. Button); }; #endif friend class q. CTKCollapsible. Button. Private; q. CTKPrivate. Interface<q. CTKCollapsible. Button, q. CTKCollapsible. Button. Private> qctk_d;

q. CTKPimpl //--------------------------------------class q. CTKCollapsible. Button. Private : public q. CTKPrivate<q. CTKCollapsible. Button> {

q. CTKPimpl //--------------------------------------class q. CTKCollapsible. Button. Private : public q. CTKPrivate<q. CTKCollapsible. Button> { public: QCTK_DECLARE_PUBLIC(q. CTKCollapsible. Button); void init(); bool … }; Collapsed; //--------------------------------------void q. CTKCollapsible. Button. Private: : init() { QCTK_P(q. CTKCollapsible. Button); p->set. Checkable(true); // checked and Collapsed are synchronized: checked != Collapsed p->set. Checked(true); } this->Collapsed = false; q. CTKCollapsible. Button* p = qctk_p() q. CTKCollapsible. Button. Private* d = qctk_d() friend class q. CTKCollapsible. Button qctk_d. set. Public(this) //--------------------------------------q. CTKCollapsible. Button: : q. CTKCollapsible. Button(QWidget* parent) : QAbstract. Button(parent) { QCTK_INIT_PRIVATE(q. CTKCollapsible. Button); qctk_d()->init(); } //--------------------------------------void q. CTKCollapsible. Button: : collapse(bool c) { QCTK_D(q. CTKCollapsible. Button); if (c == d->Collapsed) { return; } … }

Widgets in Qt Designer • A plugin must be created – Slicer 3/Libs/q. CTKWidgets/Plugins/q.

Widgets in Qt Designer • A plugin must be created – Slicer 3/Libs/q. CTKWidgets/Plugins/q. MRMLNo de. Selector. Plugin. [h|cxx] – Slicer 3/Libs/q. MRMLWidgets/Plugins/q. MRML Node. Selector. Plugin. [h|cxx] • More info on – http: //wiki. slicer. org/slicer. Wiki/index. php/Slicer 3: Developers: Projects: Qt. Slicer/Tutorials/Widg et. Writing

Widget Example Qt Designer class QCTK_WIDGETS_EXPORT q. CTKCollapsible. Button : public QAbstract. Button {

Widget Example Qt Designer class QCTK_WIDGETS_EXPORT q. CTKCollapsible. Button : public QAbstract. Button { Q_OBJECT Q_PROPERTY(bool collapsed READ collapsed WRITE set. Collapsed) Q_PROPERTY(int collapsed. Height READ collapsed. Height WRITE set. Collapsed. Height) … public: void set. Collapsed(bool); bool collapsed()const; void set. Collapsed. Height(int); int collapsed. Height()const; q. CTKCollapsible. Button. h 10 = default value void q. CTKCollapsible. Button. Private: : init() { QCTK_P(q. CTKCollapsible. Button); … this->Collapsed = false; … this->Collapsed. Height = 10; … } q. CTKCollapsible. Button. cxx

QTCLI • Same idea than KWWidgets – Parse XML to build UI • Support

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>Histogram. Bins</name> <flag>b</flag>

QTCLI: Example … <parameters> <label>Registration Parameters</label> <description>Parameters used for registration</description> <integer> <name>Histogram. Bins</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> … Xml description UI panel in Slicer … q. CTKCollapsible. Button* registration. Parameters = new q. CTKCollapsible. Button(“Registration Parameters”, this); QLabel* histogram. Bin. Label = new QLabel(“Histogram Bins”, registration. Parameters); QSlider* histogram. Bin = new QSlider(registration. Parameters); histogram. Bin->set. Minimum(1); histogram. Bin->set. Maximum(500); histogram. Bin->set. Step(5); histogram. Bin->set. Value(30); QObject: : connect(histogram. Bin, SIGNAL(value. Changed(int)), this, SIGNAL(on. Histogram. Value. Changed(int))); … Generated code

Slicer Architecture QTCLI q. Slicer. CLIModule QTCore. Modules q. Slicer. Cameras. Modules, q. Slicer.

Slicer Architecture QTCLI q. Slicer. CLIModule QTCore. Modules q. Slicer. Cameras. Modules, q. Slicer. Transforms. Module QTGUI q. Slicer. Module. Panel, q. Slicer. Application, q. Slicer. IOManager QTCore q. Slicer. Module. Factory QTBase q. Slicer. Abstract. Module, q. Slicer. IOManager

What’s coming soon ? • • • CLI modules Node tree widgets 3 D

What’s coming soon ? • • • CLI modules Node tree widgets 3 D view widget Lookup table editor Slice view widget …

What’s in the Pipeline ? • Wizards • Python • More widgets – help

What’s in the Pipeline ? • Wizards • Python • More widgets – help from the CTK community – http: //www. commontk. org/cgibin/trac. cgi/wiki/Widget. Plans

Questions ?

Questions ?