Difference between revisions of "Slicer3:Developers:Projects:QtSlicer/Tutorials/WidgetWriting"

From Slicer Wiki
Jump to: navigation, search
Line 11: Line 11:
  
 
==Before starting==
 
==Before starting==
There are 2 directories where custom widgets can be. You first have to decide in what directory your custom widget should go:
+
There are 2 locations where custom widgets can be. You first have to decide where your custom widget should go:
*<i>Slicer3/Libs/qCTKWidgets</i>: Qt only widget
+
*<i>CTK</i>: Qt and/or VTK only
 
*<i>Slicer3/Libs/qMRMLWidgets</i>: MRML-aware widgets
 
*<i>Slicer3/Libs/qMRMLWidgets</i>: MRML-aware widgets
A good practice is to split (if possible) your custom widget in 2 widgets, a QT-only widget, and a MRML-aware widget. The MRML widget inherits from the Qt only widget.<br>
+
A good practice is to split (as possible) your custom widget in 2: a QT-only widget and a MRML-aware widget where the MRML-aware widgets inherits from the Qt-only widget. An intermediate level can be added for Qt and VTK widgets.<br>
 
It is important to keep the widgets basic. If you think someone else can use your custom widget differently, or if someone would need less features than the custom widget currently have, it is probably a good idea to split the widget in 2 widgets linked with an IS-A relationship.<br>
 
It is important to keep the widgets basic. If you think someone else can use your custom widget differently, or if someone would need less features than the custom widget currently have, it is probably a good idea to split the widget in 2 widgets linked with an IS-A relationship.<br>
  
 
==Coding style==
 
==Coding style==
The name of the custom widgets must follow the pattern: <code>q[CTK|MRML][customName][baseQtWidget]</code>, where <code>baseQtWidget</code> is the name of the qt widget your custom widget inherits from (i.e. ComboBox, Slider, Dialog, Widget...), and <code>customName</code> is a descriptive name. Examples:
+
The name of the custom widgets must follow the pattern: <code>qMRML[customName][baseQtWidget]</code>, where <code>[baseQtWidget]</code> is the name of the qt widget your custom widget inherits from (i.e. <code>ComboBox</code>, <code>Slider</code>, <code>Dialog</code>, <code>Widget</code>...), and <code>[customName]</code> is a descriptive name. Examples:
*<code>qCTKCollapsibleGroupBox</code>
 
 
*<code>qMRMLLinearTransformSlider</code>
 
*<code>qMRMLLinearTransformSlider</code>
*<code><s>qCTKColorPicker</s></code><code>qCTKColorPickerButton</code>, <code>qCTKColorPickerDialog</code>
+
*<code><s>qMRMLMatrix</s></code><code>qMRMLMatrixWidget</code>
  
 
==Note==
 
==Note==
*When you create your widget, make sure you highly use the [http://doc.trolltech.com/4.5/properties.html Qt property system], it let you customize your widgets from the Qt Designer.
+
*When you create your widget, make sure you use the [http://doc.trolltech.com/4.5/properties.html Qt property system], it allow the customization of the widgets in [http://doc.trolltech.com/4.6/designer-manual.html Qt Designer].
*The process to create a custom widget is similar for qCTKWidgets and qMRMLWidgets, this is why we will use the notation <b>q***Widgets</b> to describe either <b>qCTKWidgets</b> or <b>qMRMLWidgets</b>.
 
  
 
==Step by step==
 
==Step by step==
*Create your widget files (.h and .cxx) in the <i>Lib/q***Widgets</i> directory.
+
*Create your widget files (.h and .cxx) in the <i>Lib/qMRMLWidgets</i> directory.
**If you use a <i>.ui</i> file, add it in the <i>Libs/q***Widgets/Resources/UI</i> directory
+
**If you use a <i>.ui</i> file, add it in the <i>Libs/qMRMLWidgets/Resources/UI</i> directory
**If you use icon files, add them in the <i>Libs/q***Widgets/Resources/Icons</i> directory and update the <i>Libs/q***Widgets/Resources/q***Widgets.qrc</i> file accordingly
+
**If you use icon files, add them in the <i>Libs/qMRMLWidgets/Resources/Icons</i> directory and update the <i>Libs/qMRMLWidgets/Resources/qMRMLWidgets.qrc</i> file accordingly
*Update the <i>Libs/q***Widgets/CMakeLists.txt</i> file with your widget files.
+
*Update the <i>Libs/qMRMLWidgets/CMakeLists.txt</i> file with your widget files.
** add the <i>.h</i> and <i>.cxx</i> files in the <code>q***Widgets_SRCS</code> variable
+
** add the <i>.h</i> and <i>.cxx</i> files in the <code>qMRMLWidgets_SRCS</code> variable
** add the <i>.h</i> file in the <code>q***Widgets_MOC_SRCS</code> variable
+
** add the <i>.h</i> file in the <code>qMRMLWidgets_MOC_SRCS</code> variable
** if any, add the <i>.ui</i> file in the <code>q***Widgets_UI_SRCS</code variable
+
** if any, add the <i>.ui</i> file in the <code>qMRMLWidgets_UI_SRCS</code variable
 
* compile with your favorite compiler.
 
* compile with your favorite compiler.
 
Congratulations, you just added a custom widget in Slicer. The widget can now be used anywhere in Slicer.<br>
 
Congratulations, you just added a custom widget in Slicer. The widget can now be used anywhere in Slicer.<br>
However the widget doesn't show up in QDesigner, let's create the custom widget plugin for QDesigner.
+
However the widget doesn't show up in QDesigner, let's create the associated widget plugin for QDesigner.
  
 
=Create a widget for the designer=
 
=Create a widget for the designer=
 
For a general presentation to create custom widgets for Qt Designer, see http://doc.trolltech.com/designer-creating-custom-widgets.html
 
For a general presentation to create custom widgets for Qt Designer, see http://doc.trolltech.com/designer-creating-custom-widgets.html
 
==Step by step==
 
==Step by step==
*Copy paste this template in the <i>Libs/q***Widgets/Plugins</i> directory.
+
*Copy paste this template in the <i>Libs/qMRMLWidgets/Plugins</i> directory.
 
{|
 
{|
!qCTKWidgets
 
 
!qMRMLWidgets
 
!qMRMLWidgets
|-
 
|<pre>
 
#ifndef __qCTKCustomWidgetPlugin_h
 
#define __qCTKCustomWidgetPlugin_h
 
 
#include "qCTKWidgetsAbstractPlugin.h"
 
 
class QCTK_WIDGETS_PLUGIN_EXPORT qCTKCustomWidgetPlugin : public QObject,
 
                                                          public qCTKWidgetsAbstractPlugin
 
{
 
  Q_OBJECT
 
 
public:
 
  qCTKCustomWidgetPlugin(QObject *_parent = 0);
 
 
 
  QWidget *createWidget(QWidget *_parent);
 
  QString domXml() const;
 
  QString includeFile() const;
 
  bool isContainer() const;
 
  QString name() const;
 
 
 
};
 
 
#endif
 
 
</pre>
 
Rename the file <i>qCTK[name of your widget]Plugin.h</i>
 
 
|<pre>
 
|<pre>
 
#ifndef __qMRMLCustomWidgetPlugin_h
 
#ifndef __qMRMLCustomWidgetPlugin_h
Line 101: Line 71:
 
|}
 
|}
 
*Replace <i>CustomWidget</i> with the name of your widget
 
*Replace <i>CustomWidget</i> with the name of your widget
*Copy-paste the following template in the <i>Libs/q***Widgets/Plugins</i> directory.
+
*Copy-paste the following template in the <i>Libs/qMRMLWidgets/Plugins</i> directory.
 
{|
 
{|
!qCTKWidgets
 
 
!qMRMLWidgets
 
!qMRMLWidgets
 
|-
 
|-
|<pre>
 
#include "qCTKCustomWidgetPlugin.h"
 
#include "qCTKCustomWidget.h"
 
 
qCTKCustomWidgetPlugin::qCTKCustomWidgetPlugin(QObject *_parent)
 
        : QObject(_parent)
 
{
 
 
}
 
 
QWidget *qCTKCustomWidgetPlugin::createWidget(QWidget *_parent)
 
{
 
  qCTKCustomWidget* _widget = new qCTKCustomWidget(_parent);
 
  return _widget;
 
}
 
 
QString qCTKCustomWidgetPlugin::domXml() const
 
{
 
  return "<widget class=\"qCTKCustomWidget\" \
 
          name=\"CTKCustomWidget\">\n"
 
          "</widget>\n";
 
}
 
 
QString qCTKCustomWidgetPlugin::includeFile() const
 
{
 
  return "qCTKCustomWidget.h";
 
}
 
 
bool qCTKCustomWidgetPlugin::isContainer() const
 
{
 
  return false;
 
}
 
 
QString qCTKCustomWidgetPlugin::name() const
 
{
 
  return "qCTKCustomWidget";
 
}
 
</pre>
 
Rename the file <i>qCTK[name of your widget]Plugin.cxx</i>
 
 
|<pre>#include "qMRMLCustomWidgetPlugin.h"
 
|<pre>#include "qMRMLCustomWidgetPlugin.h"
 
#include "qMRMLCustomWidget.h"
 
#include "qMRMLCustomWidget.h"
Line 185: Line 115:
 
|}
 
|}
 
*Replace <i>CustomWidget</i> by the name of your widget  
 
*Replace <i>CustomWidget</i> by the name of your widget  
*If the widget is a container, return <code>true</code in the method <code>isContainer()</code>. See <i>qCTKCollapsibleGroupBoxPlugin.cxx</i> for an example.
+
*If the widget is a container, return <code>true</code in the method <code>isContainer()</code>.
 
*You can customize how the widget is instantiated in the designer by reimplementing the method <code>domxml()</code>.
 
*You can customize how the widget is instantiated in the designer by reimplementing the method <code>domxml()</code>.
*Update <i>Libs/q***Widgets/Plugins/CMakeLists.txt</i> by adding your widget plugin files.  
+
*Update <i>Libs/qMRMLWidgets/Plugins/CMakeLists.txt</i> by adding your widget plugin files.  
** add the files <i>qMRMLCustomWidgetPlugin.h</i> and <i>qMRMLCustomWidgetPlugin.cxx</i> in the variable <code>q***WidgetsPlugin_SRCS</code>
+
** add the files <i>qMRMLCustomWidgetPlugin.h</i> and <i>qMRMLCustomWidgetPlugin.cxx</i> in the variable <code>qMRMLWidgetsPlugin_SRCS</code>
** add the file <i>qMRMLCustomWidgetPlugin.h</i> in the variable <code>q***WidgetsPlugin_MOC_SRCS</code>
+
** add the file <i>qMRMLCustomWidgetPlugin.h</i> in the variable <code>qMRMLWidgetsPlugin_MOC_SRCS</code>  
** if any, add the extension files as well. i.e. <i>qCTKCollapsibleWidgetContainerExtension.cxx</i>
+
*Finally add your widget in the method <code>qMRMLWidgetsPlugins::customWidgets()</code> of the file <i>Libs/qMRMLWidgets/Plugins/qMRMLWidgetsPlugins.h</i>
*Finally add your widget in the method <code>q***WidgetsPlugins::customWidgets()</code> of the file <i>Libs/q***Widgets/Plugins/q***WidgetsPlugins.h</i>
 
 
*compile
 
*compile
 
Congratulations, you just added a custom widget in a plugin that can be read by QDesigner. Read the [[Slicer3:Developers:Projects:QtSlicer:Tutorials:QtDesigner|next tutorial]] to learn how you can use the widget into Qt Designer.<br>
 
Congratulations, you just added a custom widget in a plugin that can be read by QDesigner. Read the [[Slicer3:Developers:Projects:QtSlicer:Tutorials:QtDesigner|next tutorial]] to learn how you can use the widget into Qt Designer.<br>
 
'''Coding style:'''
 
'''Coding style:'''
 
For a better code readability, make sure you respect the alphabetical order of the files in <i>CMakeLists.txt</i>.
 
For a better code readability, make sure you respect the alphabetical order of the files in <i>CMakeLists.txt</i>.

Revision as of 20:52, 16 June 2010

Home < Slicer3:Developers:Projects:QtSlicer < Tutorials < WidgetWriting

QtSlicer/Tutorials

Creating a custom widget in Slicer

Intro

A custom widget is a specialization of another widget or a group of widgets working together. Custom widgets are usually created when you need to add new functionality to existing widgets or groups of widgets. If you simply want a collection of widgets in a special configuration it can be easily done in QtDesigner and probably doesn't require to be a custom widget.

In the following we will assume you are familiar with Qt.
Here are some useful links:

Before starting

There are 2 locations where custom widgets can be. You first have to decide where your custom widget should go:

  • CTK: Qt and/or VTK only
  • Slicer3/Libs/qMRMLWidgets: MRML-aware widgets

A good practice is to split (as possible) your custom widget in 2: a QT-only widget and a MRML-aware widget where the MRML-aware widgets inherits from the Qt-only widget. An intermediate level can be added for Qt and VTK widgets.
It is important to keep the widgets basic. If you think someone else can use your custom widget differently, or if someone would need less features than the custom widget currently have, it is probably a good idea to split the widget in 2 widgets linked with an IS-A relationship.

Coding style

The name of the custom widgets must follow the pattern: qMRML[customName][baseQtWidget], where [baseQtWidget] is the name of the qt widget your custom widget inherits from (i.e. ComboBox, Slider, Dialog, Widget...), and [customName] is a descriptive name. Examples:

  • qMRMLLinearTransformSlider
  • qMRMLMatrixqMRMLMatrixWidget

Note

Step by step

  • Create your widget files (.h and .cxx) in the Lib/qMRMLWidgets directory.
    • If you use a .ui file, add it in the Libs/qMRMLWidgets/Resources/UI directory
    • If you use icon files, add them in the Libs/qMRMLWidgets/Resources/Icons directory and update the Libs/qMRMLWidgets/Resources/qMRMLWidgets.qrc file accordingly
  • Update the Libs/qMRMLWidgets/CMakeLists.txt file with your widget files.
    • add the .h and .cxx files in the qMRMLWidgets_SRCS variable
    • add the .h file in the qMRMLWidgets_MOC_SRCS variable
    • if any, add the .ui file in the qMRMLWidgets_UI_SRCS</code variable
  • compile with your favorite compiler.

Congratulations, you just added a custom widget in Slicer. The widget can now be used anywhere in Slicer.
However the widget doesn't show up in QDesigner, let's create the associated widget plugin for QDesigner.

Create a widget for the designer

For a general presentation to create custom widgets for Qt Designer, see http://doc.trolltech.com/designer-creating-custom-widgets.html

Step by step

  • Copy paste this template in the Libs/qMRMLWidgets/Plugins directory.
qMRMLWidgets
#ifndef __qMRMLCustomWidgetPlugin_h
#define __qMRMLCustomWidgetPlugin_h

#include "qMRMLWidgetsAbstractPlugin.h"

class QMRML_WIDGETS_PLUGIN_EXPORT qMRMLCustomWidgetPlugin : public QObject,
                                public qMRMLWidgetsAbstractPlugin
{
  Q_OBJECT

public:
  qMRMLCustomWidgetPlugin(QObject *_parent = 0);
  
  QWidget *createWidget(QWidget *_parent);
  QString domXml() const;
  QString includeFile() const;
  bool isContainer() const;
  QString name() const;
  
};

#endif

Rename the file qMRML[name of your widget]Plugin.h

  • Replace CustomWidget with the name of your widget
  • Copy-paste the following template in the Libs/qMRMLWidgets/Plugins directory.
qMRMLWidgets
#include "qMRMLCustomWidgetPlugin.h"
#include "qMRMLCustomWidget.h"

qMRMLCustomWidgetPlugin::qMRMLCustomWidgetPlugin(QObject *_parent)
        : QObject(_parent)
{

}

QWidget *qMRMLCustomWidgetPlugin::createWidget(QWidget *_parent)
{
  qMRMLCustomWidget* _widget = new qMRMLCustomWidget(_parent);
  return _widget;
}

QString qMRMLCustomWidgetPlugin::domXml() const
{
  return "<widget class=\"qMRMLCustomWidget\" \
          name=\"MRMLCustomWidget\">\n"
          "</widget>\n";
}

QString qMRMLCustomWidgetPlugin::includeFile() const
{
  return "qMRMLCustomWidget.h";
}

bool qMRMLCustomWidgetPlugin::isContainer() const
{
  return false;
}

QString qMRMLCustomWidgetPlugin::name() const
{
  return "qMRMLCustomWidget";
}

Rename the file qMRML[name of your widget]Plugin.cxx

  • Replace CustomWidget by the name of your widget
  • If the widget is a container, return true</code in the method isContainer().
  • You can customize how the widget is instantiated in the designer by reimplementing the method domxml().
  • Update Libs/qMRMLWidgets/Plugins/CMakeLists.txt by adding your widget plugin files.
    • add the files qMRMLCustomWidgetPlugin.h and qMRMLCustomWidgetPlugin.cxx in the variable qMRMLWidgetsPlugin_SRCS
    • add the file qMRMLCustomWidgetPlugin.h in the variable qMRMLWidgetsPlugin_MOC_SRCS
  • Finally add your widget in the method qMRMLWidgetsPlugins::customWidgets() of the file Libs/qMRMLWidgets/Plugins/qMRMLWidgetsPlugins.h
  • compile

Congratulations, you just added a custom widget in a plugin that can be read by QDesigner. Read the next tutorial to learn how you can use the widget into Qt Designer.
Coding style: For a better code readability, make sure you respect the alphabetical order of the files in CMakeLists.txt.