8.8.13. Plugin class - Algo

An algorithm plugin can provide an arbitrary number of filter-methods and widgets, hence external windows, dialogs… which can be displayed by itom.

Filter-Method

A filter-method is any algorithm, which is parameterized with a set of mandatory and optional parameters and may return a set of output parameters. Similar to the flexible initialization of plugins of type dataIO or actuator, the default-values of these parameters are given by the plugin itself and can also be different for each filter-method. Please consider that a filter-method can finally be called from different threads, therefore it is not allowed to directly use any GUI-elements in such plugins. If the appropriate method is called by the python function filter, it is executed in the python thread, if the method is called form any toolbox or dialog, it is usually called in their context (main thread) but it can also be, that the filter-method is executed by any other worker thread. If a filter-method only consists of ordinary algorithmic components, you should not get any problems with these kind of flexibility.

Widgets

The widget-methods of the plugin will be called in the same way than filter-methods with a set of mandatory and optional parameters. Then, the widget-method creates a new instance of a widget, window or any other GUI element, derived from QWidget is returned and can then be displayed. This method will always be called in the context of the main thread (GUI-thread).

For the usage of such filters via Python, see Getting started with algorithm plugins (denoted as filters).

8.8.13.1. Plugin-Structure

Like all the other types of plugins, the Algo-plugin must at least consist of two classes. The first (here: MyAlgoPluginInterface) is the interface, or factory class, necessary for the successful load of the plugin by the Qt-plugin system. The “real” plugin class (here: MyAlgoPlugin) is then accessed by itom through appropriate methods in class MyAlgoPluginInterface.

8.8.13.2. Factory-Class

The structure and exemplary implementation of a factory class MyAlgoPluginInterface is mainly explained in Plugin interface class. Since the main class MyAlgoPlugin is mainly behaving like a static, singleton class, it will never be instatiated by the user, like it is the case for plugins of type dataIO or actuator. Therefore it makes no sense to define some default mandatory and optional parameters for the constructor of MyAlgoPlugin, such that the programmer should the vectors m_initParamsMand and m_initParamsOpt be unchanged.

8.8.13.3. Plugin-Class

The raw scheme for your plugin-class is as follows:

Header-File (myPluginAlgo.h)

 1#ifndef MYALGOPLUGIN_H
 2#define MYALGOPLUGIN_H
 3
 4#include "../../common/addInInterface.h" //or similar path
 5
 6class MyPluginAlgoInterface : public ito::AddInInterfaceBase
 7{
 8... //see in documentation for interface-class
 9};
10
11class MyPluginAlgo : public ito::AddInAlgo
12{
13    Q_OBJECT
14
15protected:
16    MyPluginAlgo(int uniqueID);
17    ~MyPluginAlgo() {};
18
19public:
20    friend class MyAlgoPluginInterface;
21
22    //filter-method1
23    static ito::RetVal filter1(QVector<ito::ParamBase> *paramsMand, QVector<ito::ParamBase> *paramsOpt, QVector<ito::ParamBase> *paramsOut);
24    static ito::RetVal filter1Params(QVector<ito::Param> *paramsMand, QVector<ito::Param> *paramsOpt, QVector<ito::Param> *paramsOut);
25
26    //for every further filter-method define another pair like above
27
28    //widget-method1
29    static QWidget* widget1(QVector<ito::ParamBase> *paramsMand, QVector<ito::ParamBase> *paramsOpt, ito::RetVal &retValue);
30    static ito::RetVal widget1Params(QVector<ito::Param> *paramsMand, QVector<ito::Param> *paramsOpt, QVector<ito::Param> *paramsOut);
31
32    //for every futher widget-method define another pair like above
33
34public slots:
35    ito::RetVal init(QVector<ito::ParamBase> *paramsMand, QVector<ito::ParamBase> *paramsOpt, ItomSharedSemaphore *waitCond = NULL);
36    ito::RetVal close(ItomSharedSemaphore *waitCond);
37};
38
39#endif

Source File (myPluginAlgo.cpp)

1#define ITOM_IMPORT_API
2#define ITOM_IMPORT_PLOTAPI
3
4#include "myPluginAlgo.h"
5
6//implement your code here

First of all, our algorithm plugin class is derived from the class AddInAlgo from within the ito-namespace. This base class is defined in the addInInterface.h header that has to be included. Again, our plugin is ultimately derived from QObject and the Q_OBJECT macro must appear in the class definition in order to be able to use any services provided by Qt’s meta-object system, such as the signal slot mechanisms. Additionally our plugin class has to be a friend of its interface class (see section Plugin interface class), such that the factory (interface) class is allowed to access the protected constructor.

Every filter method as well as widget method consist of two different methods. One is the real algorithm or function itself (here: named with filter1 and widget1). The second method is a small parameter-method which generates the default vectors for the mandatory, optional and output (filters only) parameters. This method is necessary, since the itom base application and the python scripts have no way of knowing what parameters the filter methods or widget generation methods expect.

These default-parameter methods have the following implementation:

 1ito::RetVal MyAlgoPlugin::filter1Params(QVector<ito::Param> *paramsMand, QVector<ito::Param> *paramsOpt, QVector<ito::Param> *paramsOut)
 2{
 3    ito::Param param;
 4    ito::RetVal retval = ito::retOk;
 5    retval += prepareParamVectors(paramsMand,paramsOpt,paramsOut);
 6    if(retval.containsError()) return retval;
 7
 8    param = ito::Param("mand1", ito::ParamBase::DObjPtr | ito::ParamBase::In, NULL, tr("description").toLatin1().data());
 9    paramsMand->append(param);
10    param = ito::Param("mand2", ito::ParamBase::String | ito::ParamBase::In, NULL, tr("description").toLatin1().data());
11    paramsMand->append(param);
12    param = ito::Param("opt1",ito::ParamBase::Double | ito::ParamBase::In, 0.0, 1.0, 0.0, tr("description").toLatin1().data());
13    paramsOpt->append(param);
14
15    param = ito::Param("return1", ito::ParamBase::Int | ito::ParamBase::Out, NULL, tr("description").toLatin1().data());
16    paramsOut->append(param);
17    param = ito::Param("return2", ito::ParamBase::String | ito::ParamBase::Out, NULL, tr("description").toLatin1().data());
18    paramsOut->append(param);
19
20    return retval;
21}

In this exemplary case, the method filter1 requires two mandatory parameters, where the first one is a dataObject-pointer and the second one is a string. Additionally one can give one optional parameter, namely a double-value. This filter does not have output-parameters.

Note

Please don’t forget to specify your parameters with the flags In, Out or In|Out. Mandatory and optional parameters can not be Out only, however output parameters must be Out only. Please consider that pure Out values is only allowed for non-pointer-type values (hence: Char, Int, Double, String, IntArray, CharArray and DoubleArray are allowed). For widget-methods, the output-value default implementation is ignored and therefore the corresponding vector should not be changed.

In line 3 of the method above, the three arguments are checked for not being NULL and are cleared within the method prepareParamVectors, which is already defined in class ito::AddInAlgo. Next, the default implementations of parameters (see Parameter-Container class of itom) are created and appended to the corresponding vector. Finally the method is called in order to get the three default vectors. Then, the values are merged with the values given by the user or other plugins and the real filter- or widget-method is called with the same vectors, however each parameter is now casted from Param to ParamBase.

Note

In order to have an efficient call, the parameter method is only called once at startup of itom and the default implementations are hashed by ito::AddInManager. The consequence for the plugin programmer is, that it is not allowed to have time- or situation-dependent changes in the default-values. This might not be considered. Only the implementation at startup is relevant and must not be changed!

The plugin-methods give the user additionally the possibility to get a readable output of the set of desired parameters including their descriptions and types in the command line by using the following python-command:

filterHelp("MyAlgoPlugin")

If the argument string does not fit to any specific filter, an enumeration of all filters containing this string will be printed.

Now the filter-method or widget-method itself can be implemented. While the widget-method only provide one method definition, the filter method can have two different possible definitions (since itom 3.3).:

8.8.13.4. Filter-Methods (Without status information and / or cancellation feature)

This is the default definition of filters and does not provide the possibility to pass runtime status information (like the current progress) or a way to let the user cancel the execution of the algorithm.

After that you implemented the parameter-method in order to generate default parameters for your filter-method (see section above), you can now implement the filter-method itsself. This first implementation might follow this scheme:

 1ito::RetVal MyAlgoPlugin::filter1(QVector<ito::ParamBase> *paramsMand,
 2                                  QVector<ito::ParamBase> *paramsOpt,
 3                                  QVector<ito::ParamBase> *paramsOut)
 4{
 5    ito::RetVal retval = ito::retOk;
 6
 7    //1. Section. Getting typed in or in/out parameters from paramsMand and paramsOpt
 8    //  Make sure that you access only parameters, that have been defined in the corresponding parameter-method.
 9    //  The order and type is important.
10
11    //possibility 1 (index-based access):
12    const ito::DataObject *dObj = (*paramsMand)[0].getVal<const ito::DataObject*>();
13    const char *filename = (*paramsMand)[1].getVal<char*>(); //don't delete this pointer (borrowed)
14    double opt1 = (*paramsOpt)[0].getVal<double>();
15
16    //possibility 2 (name-based access):
17    const ito::DataObject *dObj2 = \
18        (const ito::DataObject*)ito::getParamByName(paramsMand, "mand1", &retval)->getVal<void*>();
19    const char *filename2 = ito::getParamByName(paramsMand, "mand2", &retval)->getVal<char*>();
20    double opt2 = ito::getParamByName(paramsOpt, "opt1", &retval)->getVal<double>();
21
22    //2. Section. Algorithm.
23    //  include here your algorithm. make sure, that you only change values of pointer-based parameters
24    //  that you have defined with the flags In|Out.
25
26    //3. Section. Optionally put results into the paramsOut-vector
27    //  Make sure that you defined the corresponding parameter with right type in the corresponding parameter-method.
28    //  The implementation in the next lines is only one example and has not be defined before. Be aware of that.
29    (*paramsOut)[0].setVal<int>(2);
30    (*paramsOut)[1].setVal<char*>("we are done");
31
32    return retval;
33}

For the parsing of the given input parameters, you can (like stated above) either directly access them using their index in the vector or you can use the high-level method getParamByName, which is provided in the file helperCommon.h and helperCommon.cpp in the common-folder. If you want to use this method, integrate both files in your project and include the header file in your plugin file.

Note

Always make sure, that if you are access (read or write) any parameter in any of the three vectors, you must have the specific parameter defined in the corresponding parameter method. If you defined there a parameter of certain type and appended it to one of the vectors, you can be sure, that a parameter of same type is available at the same position in the arguments of the filter-method call.

Note

If you want to use methods provided by the itom-API, see itom API and consider the additional lines of code in your implementation.

Note

Never use the reserved name _observer for a mandatory or optional parameter name, since this is used to call a filter from the Python method itom.filter() with a given progress observer (see itom.progressObserver).

8.8.13.5. Filter-Methods 2 (With status information and / or cancellation feature)

This 2nd definition of a filter method is introduced with itom 3.3 on. Its advantage is, that another argument QSharedPointer<ito::FunctionCancellationAndObserver> observer is passed to the filter. The class ito::FunctionCancellationAndObserver is used to provide mechanisms, such that the filter algorithm can continuously report its current progress. Additionally, this class has an interrupt flag, which can be set via itom (e.g. via Python, if a Python script is interrupted or from a calling C++ method). It is the responsibility of the filter developer to regularly check the state of this interrupt flag. Once it is set, try to stop the algorithm as soon as possible and return with an error (ito::retError) set.

The filter definition for this 2nd case is as follows:

 1ito::RetVal MyAlgoPlugin::filter2(QVector<ito::ParamBase> *paramsMand,
 2                                  QVector<ito::ParamBase> *paramsOpt,
 3                                  QVector<ito::ParamBase> *paramsOut,
 4                                  QSharedPointer<ito::FunctionCancellationAndObserver> observer)
 5{
 6    ito::RetVal retval = ito::retOk;
 7
 8    //indicate the start of the algorithm to the observer (if given)
 9    if (observer)
10    {
11        observer->setProgressValue(observer->progressMinimum());
12        observer->setProgressText("Start of algorithm");
13    }
14
15    //1. Section. Getting typed in or in/out parameters from paramsMand and paramsOpt
16    //  Make sure that you access only parameters, that have been defined in
17    //  the corresponding parameter-method. The order and type is important.
18
19    //possibility 1 (index-based access):
20    const ito::DataObject *dObj = (*paramsMand)[0].getVal<const ito::DataObject*>();
21    const char *filename = (*paramsMand)[1].getVal<char*>(); //don't delete this pointer (borrowed)
22    double opt1 = (*paramsOpt)[0].getVal<double>();
23
24    //possibility 2 (name-based access):
25    const ito::DataObject *dObj2 = \
26        (const ito::DataObject*)ito::getParamByName(paramsMand, "mand1", &retval)->getVal<void*>();
27    const char *filename2 = ito::getParamByName(paramsMand, "mand2", &retval)->getVal<char*>();
28    double opt2 = ito::getParamByName(paramsOpt, "opt1", &retval)->getVal<double>();
29
30    //2. Section. Algorithm.
31    //  The following algorithm needs around 10seconds to finish. It reports its progress every second and
32    //  if the interrupt flag of the observer is set, it quits the execution earlier:
33    QElapsedTimer timer;
34    timer.start();
35    qint64 nextProgressReport = 1000; //every second
36
37    while (timer.elapsed() < 10000)
38    {
39        if (observer)
40        {
41            if (timer.elapsed() >= nextProgressReport)
42            {
43                //always pass the value between the given minimum / maximum of the observer
44                int value = observer->progressMinimum() + timer.elapsed() * \
45                    (observer->progressMaximum() - observer->progressMinimum()) / 10000;
46                observer->setProgressValue(value);
47                observer->setProgressText(QString("This algorithm run %1 from 10.0 seconds"). \
48                    arg(timer.elapsed() / 1000));
49                nextProgressReport += 1000;
50            }
51
52            if (observer->isCancelled())
53            {
54                retval += ito::RetVal(ito::retError, 0, "algorithm cancelled");
55                break;
56            }
57        }
58
59        QThread::msleep(100);
60
61    }
62    //  that you have defined with the flags In|Out.
63
64    //3. Section. Optionally put results into the paramsOut-vector
65    //  Make sure that you defined the corresponding parameter with right type in the corresponding parameter-method.
66    //  The implementation in the next lines is only one example and has not be defined before. Be aware of that.
67    (*paramsOut)[0].setVal<int>(2);
68    (*paramsOut)[1].setVal<char*>("we are done");
69
70    return retval;
71}

8.8.13.6. Widget-Method (GUI-Extensions)

If you want to provide a user-defined window, dialog or widget (which is then rendered into a dialog), you have to implement an appropriate method which follows this base structure:

 1QWidget* MyAlgoPlugin::widget1(QVector<ito::ParamBase> *paramsMand,
 2                               QVector<ito::ParamBase> *paramsOpt,
 3                               ito::RetVal &retValue)
 4{
 5    //1. Section. Getting typed in or in/out parameters from paramsMand and paramsOpt
 6    //   Make sure that you access only parameters, that have been defined in
 7    //   the corresponding parameter-method. The order and type is important. Do it
 8    //   like in the method 'filter1' above.
 9
10    //2. Pre-requisite: You have in your plugin project a class, which is derived
11    //   from QMainWindow, QDialog or QWidget. This class can also include an ui-file,
12    //   which has been designed using the QtDesigner. The class name is Widget1.
13
14    // Create an instance of that class and return it.
15    // The instance is deleted by the caller of the method 'widget1'.
16    Widget1 *win = new Widget( /* your parameters */ );
17    QWidget *widget = qobject_cast<QWidget*>(win); //cast it to QWidget, if it isn't already.
18    if(widget == NULL)
19    {
20        retValue += ito::RetVal(ito::retError,0,tr("The widget could not be loaded").toLatin1().data());
21    }
22    return widget; //NULL in case of error
23}

The widget will then be shown if the user created it by the GUI or the widget is wrapped by an instance of the python class ui (part of module itom). Then you can interact with elements of the widget using python like you can do it with every user interface created with QtDesigner. You can also connect some python methods with signals of your widget or call slots of your widget. This is also the same behaviour like dialogs have, which only have been created with QtDesigner and then loaded by an instance of class ui using python.

8.8.13.7. Publish Filter- and Widget-Methods at Initialization

The most important step in the developement of an algorithm plugin is to publish all created filter- and widget-methods. By that process, the methods will be made available to itom, such that they can be used by the python scripting language, the GUI or other plugins. The publishing is done in the method init of your plugin. It is important to say, that the two different signatures of a filter method (with or without observer) need to be published in two different ways. A exemplary implemention is as follows:

 1ito::RetVal MyAlgoPlugin::init(QVector<ito::ParamBase> *paramsMand,
 2                               QVector<ito::ParamBase> *paramsOpt,
 3                               ItomSharedSemaphore *waitCond)
 4{
 5    ItomSharedSemaphoreLocker locker(waitCond);
 6
 7    ito::RetVal retval = ito::retOk;
 8    FilterDef *filter = NULL;
 9    AlgoWidgetDef *widget = NULL;
10
11    //publish your filter-methods here, an example for a default filter definition (without status and cancellation):
12    filter = new FilterDef(WLIfilter, WLIfilterParams,
13                tr("description").toLatin1().data(),  //description
14                ito::AddInAlgo::catNone,              //category
15                ito::AddInAlgo::iNotSpecified,        //interface
16                QString());                           //meta information for interface (e.g. file pattern to be loadable)
17    m_filterList.insert("filterName", filter);
18
19    //here an example for the 2nd filter definition (with status observer and cancellation):
20    filter = new FilterDefExt(WLIfilterCancellable,
21                WLIfilterParams,
22                tr("description").toLatin1().data(), //description
23                ito::AddInAlgo::catNone,             //category
24                ito::AddInAlgo::iNotSpecified,       //interface
25                QString(),                           //meta information for interface (e.g. file pattern to be loadable)
26                true,                                //true if filter provides status information, else false
27                true);                               //true if filter listens to the interrupt flag
28                                                   //of the observer and allows to be interrupted earlier, else false
29    m_filterList.insert("filterName", filter);
30
31
32    //publish your dialogs, main-windows, widgets... here, example:
33    widget = new AlgoWidgetDef(widget1, widget1Params, tr("description").toLatin1().data(),
34                                ito::AddInAlgo::catNone, ito::AddInAlgo::iNotSpecified);
35    m_algoWidgetList.insert("widgetName", widget);
36
37    if (waitCond)
38    {
39        waitCond->returnValue = retval;
40        waitCond->release();
41    }
42
43    return retval;
44}

For registering filter- and widget-methods, you have to create a new instance of the classes FilterDef / FilterDefExt (with observer) or AlgoWidgetDef respectively and insert these newly created instances to the maps m_filterList or m_algoWidgetList respectively. Their name in the map finally is the name of the filter or widget and must be unique within itom; else the filter-method or widget-method can not be loaded at startup of itom.

The constructor of class FilterDef has the following arguments:

  • AddInAlgo::t_filter filterFunc is the pointer to the static filter-method (here: filter1)

  • AddInAlgo::t_filterParam filterParamFunc is the pointer to the corresponding parameter method (here: filter1Params)

  • QString description is a description string of the filter

  • ito::AddInAlgo::tAlgoCategory category is an optional value of the category enumeration, the filter belongs too (default: ito::AddInAlgo::catNone)

  • ito::AddInAlgo::tAlgoInterface interf is an optional value of the interface enumeration, the filter fits to (default: ito::AddInAlgo::iNotSpecified)

  • QString interfaceMeta is depending on the chosen interface an additional meta string (default: QString())

For a full reference of class FilterDef, see FilterDef.

The constructor of class FilterDefExt has the following arguments:

  • AddInAlgo::t_filterExt filterFuncExt is the pointer to the static filter2-method (with observer) (here: filter2)

  • AddInAlgo::t_filterParam filterParamFunc is the pointer to the corresponding parameter method (here: filter1Params)

  • QString description is a description string of the filter

  • ito::AddInAlgo::tAlgoCategory category is an optional value of the category enumeration, the filter belongs too (default: ito::AddInAlgo::catNone)

  • ito::AddInAlgo::tAlgoInterface interf is an optional value of the interface enumeration, the filter fits to (default: ito::AddInAlgo::iNotSpecified)

  • QString interfaceMeta is depending on the chosen interface an additional meta string (default: QString())

  • bool hasStatusInfo indicates if the filter implements code to regularily report its progress to the passed observer

  • bool isCancellable indicates if the filter implements code to regularily check the interrupt flag of the observer and stop the algorithm as soon as it has been set (e.g. via Python)

For a full reference of class FilterDef, see FilterDefExt.

The constructor of class AlgoWidgetDef has the following arguments:

  • AddInAlgo::t_algoWidget algoWidgetPFunc is the pointer to the static widget-method (here: widget1)

  • AddInAlgo::t_filterParam algoWidgetParamFunc is the pointer to the corresponding parameter method (here: widget1Params)

  • QString description is a description string of the widget or GUI-element.

  • ito::AddInAlgo::tAlgoCategory category is an optional value of the category enumeration, the widget-method belongs too (default: ito::AddInAlgo::catNone)

  • ito::AddInAlgo::tAlgoInterface interf is an optional value of the interface enumeration, the widget-method fits to (default: ito::AddInAlgo::iNotSpecified)

  • QString interfaceMeta is depending on the chosen interface an additional meta string (default: QString())

For a full reference of class AlgoWidgetDef, see AlgoWidgetDef.

For information about available algorithm categories and interfaces, see

The method-types t_filter, t_algoWidget and t_filterParam are defined by the following typedef:

typedef ito::RetVal (* t_filter)(QVector<ito::ParamBase> *paramsMand, QVector<ito::ParamBase> *paramsOpt, QVector<ito::ParamBase> *paramsOut);
typedef QWidget*    (* t_algoWidget)(QVector<ito::ParamBase> *paramsMand, QVector<ito::ParamBase> *paramsOpt, ito::RetVal &retValue);
typedef ito::RetVal (* t_filterParam)(QVector<ito::Param> *paramsMand, QVector<ito::Param> *paramsOpt, QVector<ito::Param> *paramsOut);

8.8.13.8. Finish and close plugin

Finally, the close method can be implemented, those structure is usually unchanged:

 1ito::RetVal MyAlgoPlugin::close(ItomSharedSemaphore *waitCond)
 2{
 3    ItomSharedSemaphoreLocker locker(waitCond);
 4    ito::RetVal retval = ito::retOk;
 5
 6    if (waitCond)
 7    {
 8        waitCond->returnValue = retval;
 9        waitCond->release();
10    }
11
12    return retval;
13}

The close-method does nothing but immediately releasing the parameer waitCond. For more information about ItomSharedSemaphore, see ItomSharedSemaphore.