ctkWorkflow.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. #ifndef __ctkWorkflow_h
  15. #define __ctkWorkflow_h
  16. // Qt includes
  17. #include <QObject>
  18. // CTK includes
  19. #include "ctkPimpl.h"
  20. #include "ctkCoreExport.h"
  21. class ctkWorkflowStep;
  22. class ctkWorkflowPrivate;
  23. class QAbstractState;
  24. /// \ingroup Core
  25. /// \brief ctkWorkflow is the basis for a workflow engine, i.e. a state
  26. /// machine with enhancements to support ctkWorkflowStep.
  27. class CTK_CORE_EXPORT ctkWorkflow : public QObject
  28. {
  29. Q_OBJECT
  30. Q_ENUMS(TransitionDirectionality)
  31. Q_PROPERTY(bool isRunning READ isRunning DESIGNABLE false)
  32. Q_PROPERTY(bool goBackToOriginStepUponSuccess READ goBackToOriginStepUponSuccess WRITE setGoBackToOriginStepUponSuccess)
  33. Q_PROPERTY(bool verbose READ verbose WRITE setVerbose)
  34. public:
  35. typedef QObject Superclass;
  36. explicit ctkWorkflow(QObject* parent = 0);
  37. virtual ~ctkWorkflow();
  38. /// \brief Start the workflow.
  39. /// The workflow will always start in the initial step, even if it is stopped and restarted).
  40. /// \note Calls onEntry() for the initial step.
  41. Q_INVOKABLE virtual void start();
  42. /// \brief Returns whether the workflow is currently running
  43. bool isRunning()const;
  44. /// \brief Stops the workflow.
  45. /// \note Calls onExit() for the current step.
  46. Q_INVOKABLE virtual void stop();
  47. /// \brief Transition directionalities.
  48. ///
  49. /// The direction of transitions between an origin step and a destination step can be either:
  50. /// <ul>
  51. /// <li>\a Bidirectional: A transition from the origin to the destination, and a transition from
  52. /// the destination to the origin</li>
  53. /// <li>\a Forward: A transition from the origin to the destination only</li>
  54. /// <li>\a Backward: A transition from the destination to the origin only</li>
  55. /// </ul>
  56. enum TransitionDirectionality
  57. {
  58. Bidirectional = 0,
  59. Forward,
  60. Backward
  61. };
  62. /// \brief Creates a transition between two steps, and adds the two steps to the workflow if they
  63. /// have not been previously added. (Cannot add two steps with the same id).
  64. ///
  65. /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
  66. /// \a destination step is meant to appear after the \a origin step.
  67. ///
  68. /// To add a single step, \a destination can be set to 0.
  69. ///
  70. /// Returns true/false indicating whether the method was successful.
  71. Q_INVOKABLE virtual bool addTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  72. const QString& branchId = QString(),
  73. const ctkWorkflow::TransitionDirectionality directionality
  74. = ctkWorkflow::Bidirectional);
  75. /// \brief Determine whether a transition has already been added
  76. /// <ul>
  77. /// <li>If a branch id is not given or is empty: a transition exists if a transition has been
  78. /// previously added with the same origin, destination and directionality</li>
  79. /// <li>If a non-empty branch id is given: a transition exists if the transition has been previously
  80. /// added with the same origin, destination and directionality, OR if a transition has been
  81. /// previously added with the same origin and branch id (for forward transitions) or
  82. /// with the same destination and branch id (for backward transitions)</li>
  83. /// </ul>
  84. Q_INVOKABLE bool hasTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  85. const QString& branchId = QString(),
  86. const ctkWorkflow::TransitionDirectionality directionality
  87. = ctkWorkflow::Bidirectional);
  88. /// \brief Set/get the initial step.
  89. /// \note In not specified, the first step added will be considered as the initialStep
  90. /// \sa currentStep(), step(), hasStep(), steps()
  91. Q_INVOKABLE ctkWorkflowStep* initialStep()const;
  92. Q_INVOKABLE virtual void setInitialStep(ctkWorkflowStep* step);
  93. /// Get the current step of the state machine
  94. /// \sa initialStep(), step(), hasStep(), steps()
  95. Q_INVOKABLE ctkWorkflowStep* currentStep()const;
  96. /// Check to see if there is a step with a given id in the workflow.
  97. /// \sa step(), currentStep(), steps()
  98. Q_INVOKABLE bool hasStep(const QString& id)const;
  99. /// Return the step with matching \a id if any, 0 otherwise.
  100. /// \sa hasStep(), currentStep(), steps()
  101. Q_INVOKABLE ctkWorkflowStep* step(const QString& id)const;
  102. /// Returns whether or not we can go forward: i.e. there exists a step that directly follows the
  103. /// given step.
  104. ///
  105. /// If no step is given, then the workflow's current step will be used.
  106. Q_INVOKABLE bool canGoForward(ctkWorkflowStep* step=0)const;
  107. /// Returns whether or not we can go backward: i.e. there exists a step that directly preceeds the
  108. /// given step.
  109. ///
  110. /// If no step is given, then the workflow's current step will be used.
  111. Q_INVOKABLE bool canGoBackward(ctkWorkflowStep* step=0)const;
  112. /// Returns whether or not we can go to the goal step from the origin step: i.e. there is a path
  113. /// in the workflow from the current step to the given step.
  114. ///
  115. /// If no step is designated as the 'origin', then the workflow's current step will be used
  116. /// Note: does not currently work in branching workflows if the origin and target steps are not on
  117. /// the same branch
  118. Q_INVOKABLE bool canGoToStep(const QString& targetId, ctkWorkflowStep* step=0)const;
  119. /// Get the steps that directly follow the given step.
  120. ///
  121. /// More specifically, the returned list of steps will be the destination steps for which
  122. /// addTransition() has been called with the given step as the origin step and directionality set
  123. /// to ctkWorkflow::Bidirectional or ctkWorkflow::Forward.
  124. ///
  125. /// If no step is given, then the workflow's current step will be used.
  126. Q_INVOKABLE QList<ctkWorkflowStep*> forwardSteps(ctkWorkflowStep* step=0)const;
  127. /// Get the steps that directly preceed the given step.
  128. ///
  129. /// More specifically, the returned list of steps will be the origin steps for which
  130. /// addTransition() has been called with the given step as the destination step and directionality
  131. /// set to ctkWorkflow::Bidirectional or ctkWorkflow::Backward.
  132. ///
  133. /// If no step is given, then the workflow's current step will be used.
  134. Q_INVOKABLE QList<ctkWorkflowStep*> backwardSteps(ctkWorkflowStep* step=0)const;
  135. /// Get the steps that are 'finish' steps (i.e. have no step following them)
  136. Q_INVOKABLE QList<ctkWorkflowStep*> finishSteps()const;
  137. /// Returns list of steps managed by the workflow
  138. Q_INVOKABLE QList<ctkWorkflowStep*> steps()const;
  139. // Returns the distance of a given to step to another step.
  140. // The directionality used here is ctkWorkflow::Bidirectional or ctkWorkflow::Backward.
  141. // By default, step is the current step and origin the initial step.
  142. //
  143. // This is different from the other method as it's not limited to the backward or forward steps
  144. // but actually performs a recursive search.
  145. Q_INVOKABLE int backwardDistanceToStep(ctkWorkflowStep* fromStep = 0, ctkWorkflowStep* origin = 0)const;
  146. /// Configures the behavior of goToStep(targetId).
  147. ///
  148. /// If set to true, goToStep(targetId) goes back to the origin step after
  149. /// the attempt of going to the target step succeeded.
  150. /// If set to false, goToStep(targetId) stays at the target step when the attempt
  151. /// succeeded.
  152. bool goBackToOriginStepUponSuccess()const;
  153. void setGoBackToOriginStepUponSuccess(bool flag);
  154. /// If set debug messages will be displayed on standard output.
  155. bool verbose()const;
  156. void setVerbose(bool value);
  157. public Q_SLOTS:
  158. /// Use this to trigger evaluation of the processing state of the current step, and subsequent
  159. /// conditional transition to the next step.
  160. virtual void goForward(const QString& desiredBranchId = QString());
  161. /// Use this to trigger transition to the previous step (does not require validation)
  162. virtual void goBackward(const QString& desiredBranchId = QString());
  163. /// Go to the given step by iteratively calling goForward() until we reach it.
  164. virtual void goToStep(const QString& targetId);
  165. /// \brief Receives the result of a step's validate(const QString&) function.
  166. ///
  167. /// If the validation is successful, then this slot begins the transition to the next step.
  168. virtual void evaluateValidationResults(bool validationSucceeded, const QString& branchId);
  169. protected:
  170. void goToNextStepAfterSuccessfulValidation(const QString& branchId);
  171. void goToProcessingStateAfterValidationFailed();
  172. /// \brief Processing that occurs after the attempt to go to a 'goTo' step succeeds
  173. virtual void goToStepSucceeded();
  174. /// \brief Processing that occurs after the attempt to go to a 'goTo' step fails
  175. virtual void goToStepFailed();
  176. /// \brief Goes to the step from which the attempt to go to the 'goTo' step was initiated
  177. void goFromGoToStepToStartingStep();
  178. protected Q_SLOTS:
  179. /// On an attempt to go to the next step, calls the current step's
  180. /// validate(const QString&) function to validate the processing step.
  181. void attemptToGoToNextStep();
  182. /// \brief Called when transitioning to the next step upon successful validation, or
  183. /// when transitioning to the previous step.
  184. /// Calls onExit() of the transition's origin step and then onEntry() of
  185. /// the transition's destination step.
  186. /// \note Must be sent by a ctkWorkflowTransition.
  187. void performTransitionBetweenSteps();
  188. Q_SIGNALS:
  189. /// Emitted when the current step has changed, after the step's onEntry() has completed.
  190. /// \note This signal is not emitted in the process of going to a goToStep
  191. void currentStepChanged(ctkWorkflowStep* currentStep);
  192. /// Emitted when a step is registered with this workflow
  193. /// \sa addTransition
  194. void stepRegistered(ctkWorkflowStep* step);
  195. protected:
  196. QScopedPointer<ctkWorkflowPrivate> d_ptr;
  197. private:
  198. Q_DECLARE_PRIVATE(ctkWorkflow);
  199. Q_DISABLE_COPY(ctkWorkflow);
  200. };
  201. #endif