Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Action.h 24.3 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
5
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#ifndef ACTION_H
#define ACTION_H

// -- Core stuff
#include "CamiTKAPI.h"
31
#include <HistoryItem.h>
saubatn's avatar
saubatn committed
32
#include <Application.h>
33
34
35
36
37
38
39
40
41
42

#include <QSet>
#include <QWidget>
#include <QString>
#include <QAction>


namespace camitk {

class ActionExtension;
43
class Property;
44
45

/**
46
 * @ingroup group_sdk_libraries_core_action
47
 *
48
 * @brief
49
50
51
52
53
54
55
56
 * Action class is an abstract class that enables you to build a action (generally on a component).
 * At least two classes have to be reimplemented to enable the action: ActionExtension + Action
 *
 * This is the list of attributes you need to consider when creating a new action
 * - extension : the ActionExtension class where to register your action;
 * - name: name of the action;
 * - description: tag used to describe the action (also used for tooltip and whatsThis of the corresponding QAction);
 * - component: the name of the component class on which this action can be applied or "" (default) for generic actions;
57
 * - family: families of actions allows one to group different actions of the same kind under one name;
58
59
60
61
62
63
64
 * - tags: tags is a list of words used to define an action. These words can be used to find an action.
 * - widget: a default widget (instance of ActionWidget) is build by default (see below if this is not what you need);
 * - isEmbedded: this boolean defines if the gui widget is embedded in a given parent widget / action widget container (true by default)
 * - icon: the icon used for the visually distinguish the action (used by the corresponding QAction)
 *
 * An Action has a corresponding QAction, see getQAction(), that makes it easy to trigger an action from any Qt GUI (menus, toolbar,
 * push buttons...)
65
 *
66
67
68
 * If the component class is defined (non empty string), an Action is applied on the currently selected components.
 * If there are no component defined (i.e. you specifies setComponent("")), it means that your action does not
 * need any inputs.
69
 *
70
71
72
73
74
75
76
77
 * Two steps have to be considered when using an action:
 * - Step 1, trigger(): the action is either directly applied (if it does not have any GUI) or it's GUI is shown (using getWidget())
 * - Step 2, apply(): only the action algorithm is applied, i.e., the data are processed
 *
 * The targets can have changed between the time the action is first triggered and the time the action is applied.
 * getWidget() is always called when the targets are updated.
 * Therefore whenever getWidget() is called, you should make sure to update the the action GUI consequently.
 * getTargets() is always updated in trigger() and available.
78
 *
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 * \note
 * trigger() and apply() are public slots. They can be called either directly (classic C++ method invocation) or
 * by connecting them to a QWidget signal.
 *
 * When an action is triggered (e.g., by right clicking in the context menu), the following algorithm applies, see trigger():
 * - 1. Prepare targetComponents (available with getTargets()): only select the compatible components from the selected components
 * - 2. If the action is embedded, get the widget and show it in a parent/container (if parent is not specified, show it in the action viewer)
 * - 3. If the action in not embedded, show it as a dialog
 * - 4. If the action does not have any widget, directly call apply()
 *
 * This means that, if there is a widget, the action algorithm is controlled by the action widget, i.e. apply() is not
 * called by trigger() but should be called by one of the action widget's button.
 *
 * An Action generally is used to wrap an algorithm in CamiTK. If this algorithm has parameters, it is very easy to
 * get these parameters accessible to the user through the ActionWidget.
 * These parameters are in fact defined as Qt dynamic properties.
 *
 * By default an action has a widget, instance of ActionWidget.
97
 * If ActionWidget does not correspond to what you need, just create a new class inheriting from QWidget, or
98
 * even better, inheriting from ActionWidget.
99
 *
100
101
102
103
104
105
106
 * These are the use cases for using the default behaviour (i.e. an instance of ActionWidget):
 * - your action has some parameters and you need the user to review the default or modify their values before the action is applied,
 * - or your action has no parameters but you still want the user to be applied only if/when the user click on an apply button.
 *
 * ActionWidget should be good enough in most of the cases.
 * The default widget contains a description, a reminder of the current target component names,
 * and an applyable/revertable ObjectController that allows you to edit/modify properties.
107
 *
108
109
110
111
112
 * \note
 * The recommanded architecture is for the action widget to call the action's apply method.
 * The widget should only manage user interaction.
 *
 * Here are some notes about the rest of the properties:
113
 *
114
115
116
117
118
119
120
121
 * extensionName is automatically given during the action registration in the ActionExtension.
 *
 * The component property determines on which type of component your action can be applied.
 * Generic actions are action that have an empty component name. Therefore generic actions
 * can be called to generate/synthetize data or initialize resources.
 *
 * You can add any number of tags using the method addTag().
 *
122
 * If ActionWidget is not what your need, a typical getWidget() method should use the lazy instantiation pattern to instantiate
123
124
125
126
127
128
129
130
 * MyVerySpecialActionWidget the first time it is called, and call the MyVerySpecialActionWidget instance's updateTargets() method
 * for any subsequent calls. Something like:
 * \code
 * QWidget *MyAction::getWidget() {
 *    // build or update the widget
 *    if (!myWidget)
 *        myWidget = new MyVerySpecialActionWidget(this);
 *    else
131
132
 *        // MyVerySpecialActionWidget should have an update() method
 *        myWidget->update();
133
134
135
136
137
138
 *
 *    return myWidget;
 * }
 * \endcode
 * But of course you can also use any kind of widget you like. ActionWidget is just defining
 * a default widget for an action.
Emmanuel Promayon's avatar
Emmanuel Promayon committed
139
 * If your action does not have any GUI/parameters, just the getWidget() method in order to return NULL.
140
141
142
143
144
 *
 * By default the properties/parameters are not automatically updated when the user change the default widget,
 * they are updated only when the user click on the apply button of the default widget.
 * Use setAutoUpdateProperties(true) to automatically update the action's properties.
 *
145
 * \note if your action needs to react immediately to a change of value in one of its dynamic properties,
Emmanuel Promayon's avatar
Emmanuel Promayon committed
146
 * you need to the <tt>virtual bool event(QEvent* e)</tt> method. For instance:
147
148
149
150
151
152
153
154
155
156
157
 * \code
 * // ---------------------- event ----------------------------
 * bool MyAction::event(QEvent * e) {
 *    if (e->type() == QEvent::DynamicPropertyChange) {
 *        e->accept();
 *        QDynamicPropertyChangeEvent *changeEvent = dynamic_cast<QDynamicPropertyChangeEvent *>(e);*
 *
 *        if (!changeEvent)
 *            return false;
 *
 *        // do something depending of the property that has changed
158
 *        CAMITK_INFO(tr("[%1] changed to [%2]").arg(changeEvent->propertyName(),property(changeEvent->propertyName()).toString()))
159
 *
160
161
162
163
 *        return true;
 *    }
 *
 *    // this is important to continue the process if the event is a different one
164
 *    return QObject::event(e);
165
166
 *}
 * \endcode
167
 *
168
169
 * \note if you use your own MyVerySpecialActionWidget class, make sure it conforms to this behaviour
 * (you can get the desired behaviour by calling getAutoUpdateProperty()
170
 *
171
172
173
174
175
176
 * By default the action's widget is embedded. If you do not want to embed your action's widget, use setEmbedded(false)
 * in the constructor.
 * When embedded, the parent widget has to be given at triggered time.
 * If there is no parent given for an embedded action, then the action is embedded in the ActionViewer by default.
 *
 * The method apply() must be implemented in your Action.
177
 *
178
179
180
181
 * \note at any moment, the selected components on which the action needs to be applied
 * are available by getTargets().
 * targetComponents is filtered so that it only contains compatible components (i.e.,
 * instances of getComponent()).
182
 *
183
184
 * \note About registering your action in the history of the application.
 * Consider registering your action within the application's history once applied. The history of action features
185
186
 * a stack of processed action. The application's history of actions allows one to export the saved actions as an XML file for
 * scripting or replaying it. To do so, implement the apply() method in your code, then launch the method applyAndRegister(),
187
188
189
190
 * which simply wraps the apply() method with the preProcess() and postProcess() methods. You may also connect a SIGNAL to it,
 * as the applyAndRegister() method is a Qt SLOT.
 *
 * \note About creating a pipeline of actions
191
 * A pipeline of actions is a state machine where each state stands for an action with inputs and output components. The transitions
192
193
194
 * between the states are done by processing the state's action (i.e. by calling the corresponding action's apply() method).
 * Interpreting an pipeline of action is simpler than simply executing the action since the user doesn't need to manually
 * set the inputs and outputs of each action (it is done automatically).
195
196
 * If you are willing to write such a pipeline, simply implements the apply() method of each of your action and called the
 * applyInPipeline() (instead of simply apply()). The method applyInPipeline() performs some pre- and post-processing around
197
 * the method apply(). It has to be used within a pipeline (a chain of actions) where setInputComponents()
198
 * and getOutputComponents() are needed. preProcessInPipeline() only selects the right components,
199
 * and postProcess() sets output components and record history.
200
201
 *
 *
202
 * @see RenderingOption For a simple example of an embedded action
203
 * @see RigidTransform For a simple example of a non-embedded action
204
 * @see ChangeColor For a simple example of an action with no widget (but with a GUI)
205
206
207
*/

class CAMITK_API Action : public QObject {
208
    Q_OBJECT
209
210
211

public:

212
    /// Default Constructor: the ActionExtension is needed
213
    Action(ActionExtension*);
214

215
    /// Destructor
Emmanuel Promayon's avatar
Emmanuel Promayon committed
216
    ~Action() override;
217

218
219
220
221
#ifdef ERROR
#define CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD ERROR
#undef ERROR
#endif
222
223
224
225
226
227
228
229
    /// \enum ApplyStatus describes what happened during the application of an algorithm (i.e. results of the apply method)
    enum ApplyStatus {
        SUCCESS,    ///< everything went according to plan
        ERROR,      ///< apply() failed : an error occured (usually it means that the apply() was interrupted)
        WARNING,    ///< some (partial) error occured during the application of the algorithm
        ABORTED,    ///< the action was aborted before completion
        TRIGGERED  ///< the action was triggered only, but not applied
    };
230

231
232
233
234
#ifdef CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD
#define ERROR CAMITK_WINDOWS_SYSTEM_ERROR_SAFEGUARD
#endif

235
236
237
    ///@return the QString equivalement of the given status
    static QString getStatusAsString(ApplyStatus);

238
public slots:
239
    /**
240
241
242
     * This method triggers the action.
     * The parent widget is used if the action is embedded, see class description for more information about the algorithm.
     * This method cannot be redefined in inherited class.
243
     */
Emmanuel Promayon's avatar
Emmanuel Promayon committed
244
    ApplyStatus trigger(QWidget* parent = nullptr);
245

246
    /**
247
248
249
250
251
252
253
254
255
     * This method is called when the action has to be applied on the target list (get the target lists using getTargets())
     *  It calls the algorithm of your action on the target list of components
     * \note it should never be empty!
     * \note if you wish to call your action and register it within the application history, prefer
     * using the \see Action::applyAndRegister()
     * method
     * @return The status of the apply method.
     */
    virtual ApplyStatus apply() = 0;
256
257

    /**
258
259
260
     * This method is called whenever the action has to be applied on the target list (like the apply()) method
     * AND registered within the application history of actions.
     * \note This is the default behaviour of applying and action.
261
     * The application's history of actions allows one to export the saved actions as an XML file for scripting or replaying it.
262
263
264
265
266
     * @return The status of the apply method.
     **/
    ApplyStatus applyAndRegister();

public:
267

268
269
270
271
272
273
274
275
276
277
278
    /// @name Pipeline execuction of the Action
    ///@{
    /** This method encapsulates the apply() method.
     *  It has to be called within a pipeline (a chain of actions), where a
     *  script or another programm calls setInputComponents() and/or getOutputComponents.
     *  It is not needed in the case of graphical interface which trigger the Action's widget
     *  and applies the action on selected components.
     *  When there is no GUI, preProcessInPipeline() and postProcessInPipeline() methods select the right component(s).
     *  As the method apply() is called between preProcessInPipeline() and postProcessInPipeline(), the returned value
     *  is the returned value of apply().
     */
279
280
    ApplyStatus applyInPipeline();
    //@}
281

282
283
284
    /// Specify the input Component(s)
    /// Only applyInPipeline() should be called with this method (maybe apply),
    ///  but not trigger() as its first intruction is to clear the target components list !!!
285
    void setInputComponents(ComponentList inputs);
286
287

    /// Specify the input Components in case of only one Component.
288
    void setInputComponent(Component* input);
289
290

    /// Returns the output Component(s)
291
    ComponentList getOutputComponents();
292
293

    /// Returns the output Components in case of only one Component.
294
    Component* getOutputComponent();
295
296
297
298
299
300
301
302
303
304
305
306
307
    //@}

    /// @name Generic action getters
    /// These methods can not be redefined in subclasses.
    /// @{
    /** Get the corresponding QAction.
     *  The corresponding QAction has its triggered() signal connected to the trigger() slot of the action.
     *  It shares the action icon (as the QAction's icon) and name (as the QAction's text).
     *  It also use the descriptions of the action for the tooltip/whatsThis text.
     *
     *  To add a shortcut, simply call getQAction()->setShortcut(..) in the action constructor.
     *  To make this shortcut available for any windows of the application, call getQAction()->setShortcutContext(Qt::ApplicationShortcut);
     */
308
    QAction* getQAction();
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356

    /// get the name of the action
    QString getName() const {
        return name;
    };

    /// the description of the action
    QString getDescription() const {
        return description;
    };

    /// the name of the component class that can be used by this action
    QString getComponent() const {
        return component;
    };

    /// the name of the family in which this action is associated
    QString getFamily() const {
        return family;
    };

    /// the name of the extension in the family in which this action is associated
    QString getExtensionName() const;

    /// the name of the tag called this action
    QStringList getTag() const {
        return tags;
    };

    /// argument use to know if the widget is embedded or not
    bool getEmbedded() const {
        return isEmbedded;
    };
    ///@}

    /// @name Method specific to an action.
    /// @{
    /** This method has to be redefined in your Action only if:
      * - you do not have any widget to control your action (i.e. getWidget() will have to return NULL),
      * - you do not use the default ActionWidget but another one.
      *
      * In the second case, it is strongly recommanded to have a code similar to this:
      * \code
      * QWidget *MyAction::getWidget() {
      *    // build or update the widget
      *    if (!myWidget)
      *        myWidget = new MyVerySpecialActionWidget(this);
      *    else
357
358
      *        // MyVerySpecialActionWidget should have an update() method
      *        myWidget->update();
359
360
361
362
363
      *
      *    return myWidget;
      * }
      * \endcode
      *
364
      * The update() method in MyVerySpecialActionWidget is used in case the selection has changed since the
365
      * last time the widget was shown (a change in the selection often means the targets or the
366
      * parameter values have changed, the UI should be refreshed as well).
367
      */
368
    virtual QWidget* getWidget();
369
370
371
372
373
374
375
376

    /// the icon to personalize the action (no icon by default)
    virtual QPixmap getIcon();

    /// the currently selected and valid (regarding the component property) components, for which this action is called
    const ComponentList getTargets() const;
    ///@}

saubatn's avatar
saubatn committed
377
378
    /// @name Property management
    /// @{
379
380
381
382
383
384
    /// auto update properties
    bool getAutoUpdateProperties() const;

    /// are the properties to be udpated every time the user makes a change in the widget (default is false)?
    void setAutoUpdateProperties(bool);

385
386
387
    /** Get a Property given its name
     *  @param name the property name
     *  @return NULL if the name does not match any property name
388
     *
389
390
391
     *  @see Property
     */
    Q_INVOKABLE virtual Property* getProperty(QString name);
392

393
394
    /** Add a new parameter to the action, using the CamiTK property class.
     * If the parameter already exist, it will just change its value.
395
     *
396
397
398
399
400
     * \note
     * The action takes ownership of the Property instance.
     *
     * @return false if the Qt Meta Object property was added by this method (otherwise the property was already defined and true is returned if it was successfully updated)
     */
401
    virtual bool addParameter(Property*);
saubatn's avatar
saubatn committed
402
    ///}@
403

saubatn's avatar
saubatn committed
404
405
406
407
408
409
410
411
    /// @name Frame management
    /// @{
    /**
     * Change the target frame according to the default frame policy regarding the input's one.
     * @param input The component refering to for the computation of the frame.
     * @param target The component on which we apply the frame policy.
     */
    void applyTargetPosition(Component* input, Component* target);
412

saubatn's avatar
saubatn committed
413
414
415
416
417
418
    /**
     * Change the target frame according to a given frame policy regarding the input's one.
     * @param input The component refering to for the computation of the frame.
     * @param target The component on which we apply the frame policy.
     * @param policy The frame policy to use on the target.
     */
419
    void applyTargetPosition(Component* input, Component* target, Application::TargetPositionningPolicy policy);
saubatn's avatar
saubatn committed
420
    /// }@
421

422
protected:
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    /** @name Generic action attributes setters
     *  These methods can not be redefined in subclasses but have to used to ensure name/description unicity
     *  among CamiTK.
     */
    /// @{
    /// set the name of the action class
    void setName(QString name);

    /// the description of the action
    void setDescription(QString description);

    /// the name of the component class that can be used by this action
    void setComponent(QString component);

    /// the name of the family in which this action is associated
    void setFamily(QString family);

    /// add a tag to the tags list of this action
    void addTag(QString tag);

    /// set the embedded property (an action is embedded by default, unless specified otherwise by explicitly calling this method with false)
    void setEmbedded(bool isEmbedded);

    /// set the Pixmap
    void setIcon(QPixmap);

    /// the action widget
450
    QWidget* actionWidget;
451
452
453
454


    ///@}

455

456
457

private:
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    /// the name of the action
    QString name;

    /// the descriptionof the action
    QString description;

    /// the name of the component class that can be used by this action
    QString component;

    /// the name of the family in which this action is associated
    QString family;

    /// the name of the tag called this action
    QStringList tags;

    /// is the widget embedded or not
    bool isEmbedded;

    /// the extension in which this action is declared and registered
477
    ActionExtension* extension;
478
479
480
481
482

    /// the Action pixmap icon
    QPixmap icon;

    /// the corresponding QAction
483
    QAction* qAction;
484

485
486
487
    /// list of CamiTK property decorating the dynamic properties (action parameters)
    QMap<QString, Property*> parameterMap;

488
489
490
491
492
493
494
495
496
497
498
499
500
    /**
     *  The list of valid (regarding the component property) components for which this action is called.
     *  This list is private (use getTargets() in subclasses).
     *  This list may by filled
     *   - either by the trigger() method which takes the currently selected and valid components
     *     (the method trigger() then calls the apply method or the apply() method can be called by the action's widget
     *   - or by the setInputComponent(Component *)/setInputComponents(ComponentList) methods, but then the method
     *     applyInPipeline() should be called (and not directly the apply() method).
     */
    ComponentList targetComponents;

    /// Should the properties/parameters of this action be automatically updated when the user change something in the GUI
    bool autoUpdateProperties;
501
502

    /// @name Action history registration
503
504
505
    ///@{
    /**
     * This properties and methods helps registering an applied action in the history of the application.
506
     * The application's history of actions allows one to export the saved actions as an XML file for scripting or replaying it.
507
     **/
508

509
510
    /**
     * The @class{HistoryItem} associated to this action.
511
     **/
512
    HistoryItem* item;
513

514
515
    /** The list of top level selected components before running the action
     *  This list is used to deduce the number of top level components, modified through applying the action
516
     **/
517
    ComponentList topLevelSelectedComponents;
518

519
520
    /**
     * Save the number of top level components loaded in memory before applying the action.
521
     * This allows one to deduce the number of created / deleted components lauching the action.
522
     */
523
524
    void preProcess();

525
    /**
526
     * Register the action in the history. The history item registered features the input and output components,
527
528
529
530
     * which are deduced with the preProcess() and postProcess() functions.
     */
    void postProcess();
    ///@}
531
532
533

    /// @name Pipeline execuction of the Action
    ///@{
534
535
536
537
    /**
     * In case of a pipeline application of the Action (i.e. a chain of actions with no gui),
     * the following properties and methods are used:
     */
538
539

    /**
540
541
     * List of alive component before the application of the action
     * (to be compared with the list after and deduce outputComponents).
542
     **/
543
    ComponentList aliveBeforeComponents;
544

545
546
547
    /**
     * List returned by getOutputComponents()
     **/
548
    ComponentList outputComponents;
549
550
551


    /**
552
553
554
555
556
557
     * Selects the right component(s) (the one that has been set by setInputComponents() ),
     * so that the apply method uses the right component(s) through getTargets().
     * If setInputComponents where not called, does not select any component.
     */
    void preProcessInPipeline();

558
    /**
559
560
561
562
     * Set the right output component list so that the method getOutputComponents() can be called.
     * Also, register the action in the history.
     */
    void postProcessInPipeline();
563
564


565
566

    ///@}
567
568


569
570
571
572
573
574

};

}

// -------------------- declare the interface for QPluginLoader --------------------
Emmanuel Promayon's avatar
Emmanuel Promayon committed
575
Q_DECLARE_INTERFACE(camitk::Action, "TIMC-IMAG.Action/2.1")
576
577
578
579
580
581

#endif // ACTION_H