Skip to content

How to add Qt Widgets as additional action parameters for actions created using the CamiTK wizard

CamiTK wizard is a great tool to get you started quickly implementing an algorithm on image volumes, meshes or other custom data structures. Yet, there are a few restrictions of using the wizard. Among them, the inability to use the power of Qt UI widgets in the current CamiTK wizard (version 3.1) is a major one. By using the CamiTK wizard we could only use 3 standard data types (bool, double and int) and 7 Qt data types (QColor, QDate, QPoint, QPointF, QString and QVector3D) as input variables. In this tutorial you will learn how to add Qt Widgets as additional action parameters for actions created using the CamiTK wizard.

There are two main ways of getting this done.

The two options have their own pros and cons which will be discussed in their respective sections.

Prerequisites

It is assumed that you already have a working knowledge about the use of CamiTK for development purposes and that you know how to create an action using the CamiTK wizard.

Learning by doing

The tutorial will be presented by the use of an example action. Let’s assume we want to provide an action where we want to achieve two main goals.

  • Change the output type from the input type
  • Change the size of the output

These features will help to reduce a very large input (image volume/mesh or something else) into a manageable output. As an example a DICOM image volume of size 512 x 512 x 400 of signed short type can be reduced to an image volume of size 128 x 128 x 400 of unsigned char type, effectively reducing the size of the input by a factor of 32 (of course at the cost of losing valuable information). Let’s call this action “Resampling Action”.

To represent the output type we will use a combo box and populate it with 6 enums (Same As Input,Char, Unsigned Char, Short, Unsigned Short, Int and Unsigned Int).

Combo box enums scalar type

To represent the output sizes we will use three spin boxes with a default value of 256.

Spin boxes of ouptut size

As you have already guessed, we can’t use CamiTK wizard to generate any of these three Qt types (QComboBox, QEnum or QSpinBox). Hence we will generate the new Resample action using the CamiTK wizard without specifying any input parameters. The source files generated by the CamiTK wizard can be found here.

The following is the dock widget that belongs to the action. As you can see, there are no input parameters created by the wizard since we didn’t ask it to create any.

Action overview with no widget

Let’s get started with creating our Qt widgets to set the input parameters.

Method 01: By coding-in the required functionality of Qt Widgets yourself

The advantage of using QT properties is that the action will be for sure used in the script feature of CamiTK, which might not be the case with QT widget (.ui file) However you might not design very precisely your action parameters (size, position and so on..), and some widgets might not be available with this method.

Spin Box

To get an spin box, with the label " Size X (nb of Voxels) ", and with 256 as a default value, you just have to add the line

1
setProperty("Size X (nb of Voxels)", QVariant(256));

in the constructor of your action, which is in a file ResampleAction.cpp for example. You don’t need to add things in the .h file. When you compile then you have the result as shown at the end of the paragraph. Your action constructor should look like this :

1
2
3
4
5
6
7
ResampleAction::ResampleAction(ActionExtension * extension) : Action(extension) {
    /*
     * Set the name and description of your action before...
     */
    setProperty("Size X (nb of Voxels)", QVariant(256));
    setProperty("Size Y (nb of Voxels)", QVariant(256));
    setProperty("Size Z (nb of Voxels)", QVariant(256));

Combo box

To get a combo box, it is a little bit longer: you have to modify both the .h and .cpp file. The idea is to define a enumeration in the .h file and add also a new data member in the class, that corresponds to the label of your widget.

Modifications on .h file:

  • Define in the public section an enumeration corresponding to the different values of your list box :
1
 enum Enum_Scalar_Type{ SameAsInput, UnsignedChar, Char, UnsignedShort, Short, UnsignedInt, Int};
  • Define a data member in the private section, which is the type of your enumeration. For our example, we would put
1
 Enum_Scalar_Type Scalar_Type;
  • Define a setter and a getter that will set and get the value of the data member. This functions must be in the public part.
1
2
    Enum_Scalar_Type getScalarType();
    void setScalarType(Enum_Scalar_Type Scalar_Type);
  • Define the following QT Macros at the top of the class :
1
2
3
 Q_OBJECT
    Q_ENUMS(Enum_Scalar_Type)
    Q_PROPERTY( Enum_Scalar_Type Scalar_Type READ getScalarType WRITE setScalarType)

Finally your .h file should look like this :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ResampleAction : public camitk::Action {
    Q_OBJECT
    Q_ENUMS(Enum_Scalar_Type)
    Q_PROPERTY( Enum_Scalar_Type Scalar_Type READ getScalarType WRITE setScalarType)

public:
    enum Enum_Scalar_Type{ SameAsInput, UnsignedChar, Char, UnsignedShort, Short, UnsignedInt, Int};

    /// Default Constructor
    ResampleAction(camitk::ActionExtension *);

    /// Default Destructor
    virtual ~ResampleAction();

    /// Setter and Getter
    Enum_Scalar_Type getScalarType();
    void setScalarType(Enum_Scalar_Type Scalar_Type);

public slots:
    virtual ApplyStatus apply();

private:
    Enum_Scalar_Type Scalar_Type;
    virtual void process(camitk::ImageComponent *);
};

Modifications on .cpp file:

  • In your constructor, define the default value of your list box. For example we should put “SameAsInput” :
1
 this->Scalar_Type = SameAsInput;
  • Define the setter and the getter functions :
1
2
3
 ResampleAction::Enum_Scalar_Type ResampleAction::getScalarType() { return this->Scalar_Type;}
    void ResampleAction::setScalarType(ResampleAction::Enum_Scalar_Type Scalar_Type) {
        this->Scalar_Type = Scalar_Type;}

What should I see ?

The result you will have should look like this :

Result with Qt properties

How to get the user’s inputs ?

To get the users input, it depends on which widget you have. If you have defined a spin box, then you will have to call the function

1
 property("Name of your label").toDouble();

and load the return of the function in a variable. You can obviously use the toInt() or toFloat function to convert the input value in the type you want.

If you have defined a combo box, then use the already defined getter class member.

Method 02: By using the Qt Designer

For you information, QtDesigner is WYSIWYG application to easily design Qt interface.

  • Go to the your action class .cpp file.
  • Find the getWidget() method. This method is called by the action viewer to display the user interface.
1
2
3
QWidget * myAction::getWidget() {
    return NULL;
}

By default this one doesn’t return anything (NULL), thus the action viewer doesn’t display anything. You will learn how to change this code to display a new user interface for your action within the action viewer.

  • Open QtDesigner and create a new component inherited from the QWidget class.

Select **Widget** in the dialog box

  • Save this file under a .ui extension file in your action project folder. Note, in the source directory, not the build one.
  • Re configure your project using CMake. It will automatically detect this .ui file and will generate a Qt Meta Object (moc) file, needed to correctly map the user designed (.ui) file with a corresponding c++ file.
  • Reopen your project in your IDE, you normally have an new header file of the same name of your .ui designed interface. This file is now automatically generated when compiling your project. You will include this file in your code to consider the interface designed using QtDesigner.
  • Include this file and declare an instance of it in your action class (thus you can get access to its declared components).

This reference will let you communicating within your code to any QtDesigner designed components of your interface.

Finally you can easily control your CamiTK action. Simply edit your .ui interface file using QtDesigner. And create the corresponding components c++ functions in your action class.