Commit caa93f3e authored by John Bonn's avatar John Bonn
Browse files

Added WorkOrder documentation.

parent 7369f2e9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -230,6 +230,11 @@ namespace Isis {

      int undoNeededToRestoreDiskState = undoStackIndex;

      // Go back through the stack looking for the last "clean" state
      // for the project.  If workorders are found that change the disk
      // state undo all workorders up to and including those workorders.
      // Ideally the project has been saved by one of the workorders
      // (state set to clean) and nothing needs to be undone.
      for (int i = undoStackIndex - 1; i >= undoStackCleanIndex; i--) {
        const WorkOrder *currentWorkOrder =
              dynamic_cast<const WorkOrder *>( m_undoStack.command(i) );
+22 −8
Original line number Diff line number Diff line
@@ -1098,16 +1098,26 @@ namespace Isis {
   *        input.
   *  
   * This method is designed to be implemented by children work orders, but they need
   * to call this version inside of their setupExecution (at the beginning).

   * State should only be set in the parent WorkOrder class in this method. You can set arbitrary
   *   state using setInternalData(). This method is always executed in the GUI thread and is the
   * to call the base class setupExecution (at the beginning).
   *
   * State should only be set in the WorkOrder class in this method. You can set arbitrary
   * state using setInternalData(). Call setData(ImageList),
   * setInternalData(QStringList), etc... with all of the data/state necessary to perform the
   * work order. This could be a list of file names, an ImageList of images you're viewing,
   * or really anything else. This method is always executed in the GUI thread and is the
   * only place to ask the user questions.
   *
   * The actual work is done in execute(), using only state (data) stored in the
   * WorkOrder class. You do not have to call execute() - this is done for you
   * by WorkOrder::redo().  WorkOrder::redo() is called from Project::addToProject() when the
   * workOrder is pushed onto the undo stack.
   *
   * If this method returns false the workorder will be cancelled and will not be executed.
   *
   * @return @b bool Returns True upon successful preparation of the WorkOrder, False to
   *         cancel the workorder.
   * @return @b bool Returns True upon successful preparation of the WorkOrder, False if this
   *            operation should be cancelled (the user clicked cancel, the operation turns
   *            out to be impossible, etc). This prevents the work order from executing and
   *            it will not be entered into the history.
   */
  bool WorkOrder::setupExecution() {
    // We're finished at this point if we save/open a project, we're not finished if we need to do
@@ -1160,6 +1170,10 @@ namespace Isis {

  /**
   * @brief Sets the internal data for this WorkOrder.
   *
   * WorkOrders may not use member variables to store data.  Any data
   * needed for the workorder should be saved in to the base WorkOrder
   * using setInternalData.
   * @param data The data to set the internal data to.
   */
  void WorkOrder::setInternalData(QStringList data) {
@@ -1415,7 +1429,7 @@ namespace Isis {


  /**
   * @brief Attempts to execute an action on the action action queue.
   * @brief Attempts to execute an action on the action queue.
   */
  void WorkOrder::attemptQueuedAction() {
    QueuedWorkOrderAction queued = m_queuedAction;
+108 −49
Original line number Diff line number Diff line
@@ -2,8 +2,6 @@
#define WorkOrder_H
/**
 * @file
 * $Revision: 1.19 $
 * $Date: 2010/03/22 19:44:53 $
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are
 *   public domain. See individual third-party library and package descriptions
@@ -53,28 +51,119 @@ namespace Isis {
  class XmlStackedHandlerReader;

  /**
   * @brief Parent class for anything that performs an action in Project
   * @brief Provide Undo/redo abilities, serialization, and history for an operation.
   *
   * This class should be used for any operation that affects a Project. This provides history,
   *   undo/redo capabilities (which need to be implemented correctly), and the ability for the
   *   project to guarantee a good state on disk.
   *
   * State between the end of setupExecution() and the beginning of the execute() method must be saved
   *   via the parent (WorkOrder) class. This is to ensure serializability. State between the execute()
   *   method and undoExecution() should work the same way. Child implementations may only save state
   *   (have member variables) that store state between execute() and postExecution()
   *   OR between undoExecution() and postUndoExecution(). Other forms of state will cause the
   *   work order to not function properly when saved/restored from disk.
   *   This class should be used for operations that affect a Project and need to provide history
   *   and/or undo/redo capabilities, and the ability for the project to guarantee a
   *   good state on disk. It follows the Command Pattern using Qt's QUndoCommand framework.
   *   Not all actions require workorders - many of the
   *   actions performed in the various widgets may not use workorders.
   *
   *   The order of execution for work orders is:
   *   setupExecution() - GUI thread, can ask user for input
   *   execute() - run on either the GUI thread or a non-GUI thread as specified by the m_isSynchronous flag
   *   postExecution() -
   *   postExecution() - perform any cleanup after execute.
   *
   *
   *   undoExecution() - run on either the GUI thread or a non-GUI thread as specified by the
   *                     m_isSynchronous flag
   *   postUndoExecution() -
   *   postUndoExecution() - perform any cleanup after undoExecution()
   *
   *
   *   ** Adding a new Workorder **
   *
   *   The workorder will need to be determined to be either synchronous/asynchronous and
   *   whether it is undoable.  These are decisions determined by the use case.  Asynchronous
   *   workorders will not block the GUI thread while running and are typically used for
   *   long-running operations.  Note that workorders are not reentrant - a new one is created
   *   for each action.
   *
   *   The constructor for the workorder must set m_isUndoable and m_isSynchronous to the appropriate
   *   values. The constructor must call the base WorkOrder constructor.  The default is
   *   synchronous and undoable.
   *
   *   All information required to execute the workorder should be saved in the workorder
   *   in the setupExecution() method.  Since workorders may be serialized and may run on
   *   non-GUI threads there are restrictions on how the workorder may save state.  To allow
   *   serialization the workorders  must save state to the base WorkOrder class using
   *   WorkOrder::setInternalData() in the following calls:
   *   setupExecution(), postExecution(), postUndoExecution().
   *   Workorders may use member variables to pass data between the execute()->postExecution()
   *   and undoExecution()->undoPostExecution() methods since since serialization can not
   *   happen between these calls.  For asynchronous  workorders the execute()/postExecution()
   *   and undoExecution()/undoPostExecution() methods are on different threads so any
   *   allocated memory moved between the non-GUI and GUI threads between methods.
   *
   *   Serialization is handled by the base WorkOrder class.  Since all state is saved
   *   into the base class using setInternalData() the derived workorders do not contain
   *   any data that needs to be serialized.  The times when workorders are allowed to use
   *   member variables are periods when the workorder can not be serialized.
   *
   *   There are 5 key methods in the flow of the workorder as shown in the WorkOrder Flow
   *   diagram below.
   *
   *   *setupExecution*
   *   The setupExecution() method gathers all required information to run the workorder but
   *   does not execute it.  The gathered information is stored in the workorder.
   *   SetupExecution() is optional but typically required. It can bring up GUI elements to
   *   prompt the user for any necessary information.  SetupExecution() is not called when
   *   a workorder is redone.
   *
   *   *execute*
   *   execute() needs to be implemented perform the workorder.
   *   All information neccessary to run the workorder should already be stored in the workorder.
   *   The data necessary for the  workorder can be retrieved via internalData()
   *   Execute() can not use any GUI elements. Each time a
   *   workorder is redone execute() is called to redo the workorder.
   *
   *   For synchronous workorders the execute()
   *   method runs on the GUI thread and there are no special requirements on object ownership.
   *
   *   For asynchronous workorders any memory allocations that aren't deallocated within
   *   execute() will need to be moved to the GUI thread.  @see ImportImagesWorkOrder::execute
   *   for an example of an asynchronous workorder that allocates memory. setProgressValue()
   *   can be used to update the progress bar in the GUI.
   *
   *   *postExecution*
   *   postExecution() runs on the GUI thread so it should not perform any long running operations.
   *   It is intended for any cleanup or GUI updates after execute().  Typically it would only be
   *   needed for asynchronous workorders where they need to update the GUI and do cleanup.  It is
   *   not required to implement this method.
   *
   *   *undoExecution*
   *   undoExecution() is only required for undoable workorders.  undoExecution() should undo the
   *   effects of the execute() only using state stored in the
   *   workorder.  It will run on the GUI thread if synchronous or a non-GUI thread if asynchronous.
   *   The same restrictions as execute() apply to this method.
   *
   *   *postUndoExecution()*
   *   This is not required. If needed, it should perform any cleanup after undoExecution().
   *   postUndoExecution() has the same restrictions as postExecution().
   *
   *   Other methods the WorkOrder may need to implement are:
   *
   *   *isExecutable(<various type>)*
   *   IsExecutable() determines if the workorder should show up in the context menus (this has no
   *   bearing on how the main menu is populated).  Note that isExecutable will need to be
   *   implemented for each type of parameter this WorkOrder should show in the context menu.
   *
   *   *dependsOn*
   *   This is currently not implemented properly for most workorders.  In theory this should determine
   *   if the workOrder parameter passed in must be completed prior to this workOrder completing.   Most
   *   current WorkOrders just check if the WorkOrder parameter is the same type.
   *
   *   *setCreatesCleanState*
   *   This is used to indicate the workorder has set the state back to an unchanged state from which
   *   it was originally opened.  This is used by open, save, and close project workorders. Unlikely
   *   to be needed by other WorkOrders.
   *
   *   *setModifiesDiskState*
   *   WorkOrders should call this to indicate they modify the disk state.  This is used to indicate
   *   the disk state should be restored back to the original state if the project is closed without
   *   saving.  The WorkOrder should implement the undoExecution method if this is set to true.
   *
   *
   *   **WorkOrder Diagrams**
   *
   * @startuml {workOrderFlow.png} "WorkOrder Flow"
   * |GUI thread|
@@ -118,7 +207,6 @@ namespace Isis {
   * participant Project
   * participant HistoryTreeWidget
   * 
   * 
   * User -> WorkOrder: Menuclick
   * 
   * activate WorkOrder
@@ -138,26 +226,19 @@ namespace Isis {
   * 
   *  Project -> WorkOrder : setNextWorkorder
   *
   * 
   * Project -> HistoryTreeWidget : << signal:workOrderStarting >>
   * Project <-- HistoryTreeWidget : slot:addToHistory
   * 
   * 
   * Project -> WorkOrder: **execute**
   * activate WorkOrder
   * WorkOrder -> Project
   * deactivate WorkOrder
   * 
   * 
   * 
   * 
   * Project  -> WorkOrder
   * deactivate Project
   * 
   * deactivate WorkOrder
   * 
   * 
   * 
   * WorkOrder -> HistoryTreeWidget : << signal:destroyed >>
   * WorkOrder <-- HistoryTreeWidget : slot:removeFromHistory
   * 
@@ -303,29 +384,7 @@ namespace Isis {
      void statusChanged(WorkOrder *);

    public slots:
      /**
       * The (child) implementation of this method should prompt the user/gather state by any means
       *   necessary. Prompts for file names, questions, warnings, etc.. should be done here.
       *
       * Once the work order has enough data to execute, this method needs to set the
       *   state in the parent (this) WorkOrder class. Call setData(ImageList),
       *   setInternalData(QStringList), etc... with all of the data/state necessary to perform the
       *   work order. This could be a list of file names, an ImageList of images you're viewing,
       *   or really anything else.
       *
       * Finally, the actual work needs done in execute(), using only state (data) stored by the
       *   parent (this) WorkOrder class. You do not have to call execute() - this is done for you
       *   by WorkOrder::redo().  WorkOrder::redo() is called from Project::addToProject() when the
       *   workOrder is pushed onto the undo stack.
       *
       * We do it this way to ensure saving/restoring from history
       *   can be done automatically/simply and implemented only once per data type. This also gives
       *   us full undo/redo functionality.
       *
       * @return False if this operation should be cancelled (the user clicked cancel, the operation
       *           turns out to be impossible, etc). This prevents the work order from making it
       *           into the history and redo will never be called.
       */

      virtual bool setupExecution();

      virtual void execute();