123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- /*=========================================================================
- Library: CTK
- Copyright (c) Kitware Inc.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.txt
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- =========================================================================*/
-
- #ifndef __ctkWorkflow_h
- #define __ctkWorkflow_h
- // Qt includes
- #include <QObject>
- // CTK includes
- #include "ctkPimpl.h"
- #include "ctkCoreExport.h"
- class ctkWorkflowStep;
- class ctkWorkflowPrivate;
- class QAbstractState;
- /// \ingroup Core
- /// \brief ctkWorkflow is the basis for a workflow engine, i.e. a state
- /// machine with enhancements to support ctkWorkflowStep.
- class CTK_CORE_EXPORT ctkWorkflow : public QObject
- {
- Q_OBJECT
- Q_ENUMS(TransitionDirectionality)
- Q_PROPERTY(bool isRunning READ isRunning DESIGNABLE false)
- Q_PROPERTY(bool goBackToOriginStepUponSuccess READ goBackToOriginStepUponSuccess WRITE setGoBackToOriginStepUponSuccess)
- public:
- typedef QObject Superclass;
- explicit ctkWorkflow(QObject* parent = 0);
- virtual ~ctkWorkflow();
- /// \brief Start the workflow.
- /// The workflow will always start in the initial step, even if it is stopped and restarted).
- /// \note Calls onEntry() for the initial step.
- Q_INVOKABLE virtual void start();
- /// \brief Returns whether the workflow is currently running
- bool isRunning()const;
- /// \brief Stops the workflow.
- /// \note Calls onExit() for the current step.
- Q_INVOKABLE virtual void stop();
- /// \brief Transition directionalities.
- ///
- /// The direction of transitions between an origin step and a destination step can be either:
- /// <ul>
- /// <li>\a Bidirectional: A transition from the origin to the destination, and a transition from
- /// the destination to the origin</li>
- /// <li>\a Forward: A transition from the origin to the destination only</li>
- /// <li>\a Backward: A transition from the destination to the origin only</li>
- /// </ul>
- enum TransitionDirectionality
- {
- Bidirectional = 0,
- Forward,
- Backward
- };
- /// \brief Creates a transition between two steps, and adds the two steps to the workflow if they
- /// have not been previously added. (Cannot add two steps with the same id).
- ///
- /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
- /// \a destination step is meant to appear after the \a origin step.
- ///
- /// To add a single step, \a destination can be set to 0.
- ///
- /// Returns true/false indicating whether the method was successful.
- Q_INVOKABLE virtual bool addTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
- const QString& branchId = QString(),
- const ctkWorkflow::TransitionDirectionality directionality
- = ctkWorkflow::Bidirectional);
- /// \brief Determine whether a transition has already been added
- /// <ul>
- /// <li>If a branch id is not given or is empty: a transition exists if a transition has been
- /// previously added with the same origin, destination and directionality</li>
- /// <li>If a non-empty branch id is given: a transition exists if the transition has been previously
- /// added with the same origin, destination and directionality, OR if a transition has been
- /// previously added with the same origin and branch id (for forward transitions) or
- /// with the same destination and branch id (for backward transitions)</li>
- /// </ul>
- Q_INVOKABLE bool hasTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
- const QString& branchId = QString(),
- const ctkWorkflow::TransitionDirectionality directionality
- = ctkWorkflow::Bidirectional);
- /// \brief Set/get the initial step.
- /// \note In not specified, the first step added will be considered as the initialStep
- Q_INVOKABLE ctkWorkflowStep* initialStep()const;
- Q_INVOKABLE virtual void setInitialStep(ctkWorkflowStep* step);
- /// \brief Register standard steps for cleaning purpose
- Q_INVOKABLE virtual void registerWorkflowStep(ctkWorkflowStep* step);
- /// Get the current step of the state machine
- Q_INVOKABLE ctkWorkflowStep* currentStep()const;
- /// Check to see if there is a step with a given id in the workflow.
- Q_INVOKABLE bool hasStep(const QString& id)const;
- /// Returns whether or not we can go forward: i.e. there exists a step that directly follows the
- /// given step.
- ///
- /// If no step is given, then the workflow's current step will be used.
- Q_INVOKABLE bool canGoForward(ctkWorkflowStep* step=0)const;
- /// Returns whether or not we can go backward: i.e. there exists a step that directly preceeds the
- /// given step.
- ///
- /// If no step is given, then the workflow's current step will be used.
- Q_INVOKABLE bool canGoBackward(ctkWorkflowStep* step=0)const;
- /// Returns whether or not we can go to the goal step from the origin step: i.e. there is a path
- /// in the workflow from the current step to the given step.
- ///
- /// If no step is designated as the 'origin', then the workflow's current step will be used
- /// Note: does not currently work in branching workflows if the origin and target steps are not on
- /// the same branch
- Q_INVOKABLE bool canGoToStep(const QString& targetId, ctkWorkflowStep* step=0)const;
- /// Get the steps that directly follow the given step.
- ///
- /// More specifically, the returned list of steps will be the destination steps for which
- /// addTransition() has been called with the given step as the origin step and directionality set
- /// to ctkWorkflow::Bidirectional or ctkWorkflow::Forward.
- ///
- /// If no step is given, then the workflow's current step will be used.
- Q_INVOKABLE QList<ctkWorkflowStep*> forwardSteps(ctkWorkflowStep* step=0)const;
- /// Get the steps that directly preceed the given step.
- ///
- /// More specifically, the returned list of steps will be the origin steps for which
- /// addTransition() has been called with the given step as the destination step and directionality
- /// set to ctkWorkflow::Bidirectional or ctkWorkflow::Backward.
- ///
- /// If no step is given, then the workflow's current step will be used.
- Q_INVOKABLE QList<ctkWorkflowStep*> backwardSteps(ctkWorkflowStep* step=0)const;
- /// Get the steps that are 'finish' steps (i.e. have no step following them)
- Q_INVOKABLE QList<ctkWorkflowStep*> finishSteps()const;
- /// Configures the behavior of goToStep(targetId).
- ///
- /// If set to true, goToStep(targetId) goes back to the origin step after
- /// the attempt of going to the target step succeeded.
- /// If set to false, goToStep(targetId) stays at the target step when the attempt
- /// succeeded.
- bool goBackToOriginStepUponSuccess()const;
- void setGoBackToOriginStepUponSuccess(bool flag);
- public slots:
- /// Use this to trigger evaluation of the processing state of the current step, and subsequent
- /// conditional transition to the next step.
- virtual void goForward(const QString& desiredBranchId = QString());
- /// Use this to trigger transition to the previous step (does not require validation)
- virtual void goBackward(const QString& desiredBranchId = QString());
- /// Go to the given step by iteratively calling goForward() until we reach it.
- virtual void goToStep(const QString& targetId);
- /// \brief Receives the result of a step's validate(const QString&) function.
- ///
- /// If the validation is successful, then this slot begins the transition to the next step.
- virtual void evaluateValidationResults(bool validationSucceeded, const QString& branchId);
- protected:
- void goToNextStepAfterSuccessfulValidation(const QString& branchId);
- void goToProcessingStateAfterValidationFailed();
- /// \brief Processing that occurs after the attempt to go to a 'goTo' step succeeds
- virtual void goToStepSucceeded();
- /// \brief Processing that occurs after the attempt to go to a 'goTo' step fails
- virtual void goToStepFailed();
-
- /// \brief Goes to the step from which the attempt to go to the 'goTo' step was initiated
- void goFromGoToStepToStartingStep();
-
- protected slots:
- /// On an attempt to go to the next step, calls the current step's
- /// validate(const QString&) function to validate the processing step.
- void attemptToGoToNextStep();
- /// \brief Called when transitioning to the next step upon successful validation, or
- /// when transitioning to the previous step.
- /// Calls onExit() of the transition's origin step and then onEntry() of
- /// the transition's destination step.
- /// \note Must be sent by a ctkWorkflowTransition.
- void performTransitionBetweenSteps();
- signals:
- /// Emitted when the current step has changed, after the step's onEntry() has completed.
- /// \note This signal is not emitted in the process of going to a goToStep
- void currentStepChanged(ctkWorkflowStep* currentStep);
- protected:
- QScopedPointer<ctkWorkflowPrivate> d_ptr;
- private:
- Q_DECLARE_PRIVATE(ctkWorkflow);
- Q_DISABLE_COPY(ctkWorkflow);
- };
- #endif
|