Commit 4934d011 authored by Ian Humphrey's avatar Ian Humphrey
Browse files

Updated TargetGetInfoWorkOrder, SetActiveImageList, ImportImagesWorkOrder for...

Updated TargetGetInfoWorkOrder, SetActiveImageList, ImportImagesWorkOrder for re-design and documentation. Fixes #4737.

git-svn-id: http://subversion.wr.usgs.gov/repos/prog/isis3/branches/ipce_FY17-Sprint1@7556 41f8697f-d340-4b68-9986-7bafba869bb8
parent 2a8d1bb1
Loading
Loading
Loading
Loading
+110 −23
Original line number Diff line number Diff line
@@ -36,8 +36,15 @@

namespace Isis {

  /**
   * @brief Creates an asynchronous WorkOrder for importing images to the project.
   *
   * @param Project *project Project to import images into.
   */
  ImportImagesWorkOrder::ImportImagesWorkOrder(Project *project) :
      WorkOrder(project) {
    // This is an asynchronous work order
    m_isSynchronous = false;
    m_newImages = NULL;

    QAction::setText(tr("Import &Images..."));
@@ -46,43 +53,44 @@ namespace Isis {
  }


  /**
   * @brief Copies the WorkOrder.
   *
   * @param ImportImagesWorkOrder &other The other work order to copy state from.
   */
  ImportImagesWorkOrder::ImportImagesWorkOrder(const ImportImagesWorkOrder &other) :
      WorkOrder(other) {
    m_newImages = NULL;
  }


  /**
   * @brief Destructor.
   *
   * Releases the memory for the m_newImages member.
   */
  ImportImagesWorkOrder::~ImportImagesWorkOrder() {
    delete m_newImages;
    m_newImages = NULL;
  }


  ImportImagesWorkOrder *ImportImagesWorkOrder::clone() const {
    return new ImportImagesWorkOrder(*this);
  }


  /**
   * @brief Indicates that this work order is an asynchronous work order.
   *
   * @description Indicates that ImportImagesWorkOrder is not a synchronous work order; i.e.,
   *              it is asynchronous.
   * Creates a clone of this work order.
   *
   * @see WorkOrder::isSynchronous()
   *
   * @return Returns false, as this is an asynchronous work order.
   * @see WorkOrder::clone()
   *
   * @return ImportImagesWorkOrder* Returns a pointer to the newly cloned work order.
   */
  bool ImportImagesWorkOrder::isSynchronous() const {
    return false;
  ImportImagesWorkOrder *ImportImagesWorkOrder::clone() const {
    return new ImportImagesWorkOrder(*this);
  }


  /**
   * @brief Sets up this work order before being executed.
   *
   * @description First invokes WorkOrder's setupExecution(). Prompts the user for cubes and image
   * First invokes WorkOrder's setupExecution(). Prompts the user for cubes and image
   * list files to import and stores them via a setInternalData() call. If there are more than 100
   * images to import, the user is prompted if they want to save their project before the import
   * occurs. If yes, a SaveProjectWorkOrder will be executed. This setup is considered successful
@@ -156,6 +164,7 @@ namespace Isis {
      QUndoCommand::setText(tr("Import %1").arg(fileNames.first()));
    }

    // The internal data will look like: [ copy|nocopy, img1, img2, ... ]
    setInternalData(stateToSave);

    bool doImport = fileNames.count() > 0 && saveProjectAnswer != QMessageBox::Cancel &&
@@ -168,7 +177,7 @@ namespace Isis {
  /**
   * @brief Undoes the work order's execute.
   *
   * @description After this ImportImagesWorkOrder has executed and finished (all the images have
   * After this ImportImagesWorkOrder has executed and finished (all the images have
   * been read), this removes the images from this import from disk in the project's directory.
   * This was renamed from asyncUndo() to undoExecution() according to the WorkOrder redesign.
   *
@@ -183,7 +192,7 @@ namespace Isis {
  /**
   * @brief Cleans up memory (images) after the undo execution occurs.
   *
   * @description After the undoExecution() occurs, this cleans up memory that was allocated for
   * After the undoExecution() occurs, this cleans up memory that was allocated for
   * the images from this import. This was renamed from postSyncUndo() to postUndoExecution()
   * according to the WorkOrder redesign.
   *
@@ -202,7 +211,7 @@ namespace Isis {
  /**
   * @brief Executes the work order.
   *
   * @description This actually "does" the work order task. In this case, this imports the images
   * This actually "does" the work order task. In this case, this imports the images
   * into memory and copies any necessary data to disk. This was renamed from asyncRedo() to
   * execute() according to the WorkOrder redesign.
   *
@@ -210,7 +219,10 @@ namespace Isis {
   * @see WorkOrder::execute()
   */
  void ImportImagesWorkOrder::execute() {
    QObject tmpObj;
    if (internalData().count() > 0) {
      // Recall in setupExecution() that first element in internal data is copy|nocopy,
      // and rest of elements are the expanded names of images to import.
      importConfirmedImages(internalData().mid(1), (internalData()[0] == "copy"));
    }
  }
@@ -219,8 +231,8 @@ namespace Isis {
  /**
   * @brief Associates the imported images to the project.
   *
   * @description After execute finishes, associates the imported images to the project. This will
   * also notifies the project if there are any warnings that occurred related to the import. This
   * After execute finishes, associates the imported images to the project. This will
   * also notify the project if there are any warnings that occurred related to the import. This
   * was renamed from postSyncRedo() to postExecution() according to the WorkOrder redesign.
   *
   * @see Project::addImages(Imagelist newImages)
@@ -240,6 +252,16 @@ namespace Isis {
  }


  /**
   * @brief Creates the internal functor.
   *
   * This functor is used for copying an image to be imported into the project.
   *
   * @param QThread *guiThread Pointer to the thread that this ImportImagesWorkOrder lives in (was
   * created in).
   * @param QDir destinationFolder Where to copy the image to.
   * @param bool copyDnData Indicates whether or not to copy the image data or just the labels.
   */
  ImportImagesWorkOrder::OriginalFileToProjectCubeFunctor::OriginalFileToProjectCubeFunctor(
      QThread *guiThread, QDir destinationFolder, bool copyDnData) : m_errors(new IException),
      m_numErrors(new int(0)) {
@@ -249,6 +271,11 @@ namespace Isis {
  }


  /**
   * Copies the other functor object (copy constructor).
   *
   * @param OriginalFileToProjectCubeFunctor &other The other functor to copy state from.
   */
  ImportImagesWorkOrder::OriginalFileToProjectCubeFunctor::OriginalFileToProjectCubeFunctor(
      const OriginalFileToProjectCubeFunctor &other) : m_errors(other.m_errors),
      m_numErrors(other.m_numErrors) {
@@ -258,6 +285,11 @@ namespace Isis {
  }


  /**
   * @brief Destructor.
   *
   * Resets the internal members to default values.
   */
  ImportImagesWorkOrder::OriginalFileToProjectCubeFunctor::~OriginalFileToProjectCubeFunctor() {
    m_destinationFolder = QDir();
    m_copyDnData = false;
@@ -265,10 +297,26 @@ namespace Isis {
  }


  /**
   * @brief Overloads the callable operator to invoke this functor.
   *
   * Copies an image to be imported for this ImportImagesWorkOrder into the
   * associated project. If we are not copying the image data, the a .ecub file will be created that
   * points to the original cube. Otherwise, a .cub will be copyied into the project and a .ecub
   * will be created in the project that references the copied cube.
   * Note that if too many errors occur, the copying
   * will not proceed for remaining images in the import and a NULL pointer will be returned.
   *
   * @param FileName &original Original file name of the image to be copied.
   *
   * @return Cube* Returns a copy of the original cube.
   */
  Cube *ImportImagesWorkOrder::OriginalFileToProjectCubeFunctor::operator()(
      const FileName &original) {
    Cube *result = NULL;

    // As long as we haven't encountered 20 errors related to importing images, we can continue
    // to import images.
    if (*m_numErrors < 20) {
      try {
        QString destination = QFileInfo(m_destinationFolder, original.name())
@@ -295,6 +343,8 @@ namespace Isis {

        result = projectImage;
      }
      // When we encounter an exception, update the m_errors and m_numErrors to with the exception
      // that occurred.
      catch (IException &e) {
        m_errorsLock.lock();

@@ -309,6 +359,15 @@ namespace Isis {
  }


  /**
   * @brief Indicates if any errors occurred during the import.
   *
   * Returns an IException that details any errors that occurred during the import.
   * Note that if there have been 20 or more errors, the exception returned will indicate that the
   * import was aborted because too many errors have occurred.
   *
   * @return IExecption Returns an IException indicating what errors occured during the import.
   */
  IException ImportImagesWorkOrder::OriginalFileToProjectCubeFunctor::errors() const {
    IException result;

@@ -325,15 +384,17 @@ namespace Isis {


  /**
   * Creates a project image folder and copies the cubes into it. This will create the *.ecub and
   *   *.cub files inside of the project.
   * @brief Imports the images.
   *
   * Creates a project image folder and copies the cubes into it. This will create
   * the *.ecub and *.cub files inside of the project.
   * This can be thought of as:
   * <pre>
   *   mkdir project/images/import1
   *   cp in1.cub in2.cub project/images/import1
   * </pre>
   *
   * This should be called in a non-GUI thread
   * This should be called in a non-GUI thread.
   *
   * @param confirmedImages This is a list of cube file names outside of the project folder
   * @param copyDnData If this is true, this will create both the *.cub and *.ecub files in the
@@ -346,6 +407,10 @@ namespace Isis {

      setProgressRange(0, confirmedImages.count());

      // We are creating a new QObject within an asynchronous execute(), which means that this
      // variable, m_newImages, has thread affinity with a thread in the gloabal thread pool
      // (i.e. m_newImages lives in a thread in the global thread pool).
      // see WorkOrder::redo().
      m_newImages = new ImageList;
      m_newImages->reserve(confirmedImages.count());

@@ -357,6 +422,7 @@ namespace Isis {

        confirmedImagesFileNames.append(fileNameAndId.first());

        // Determine if there was already a unique id provided for the file.
        if (fileNameAndId.count() == 2) {
          confirmedImagesIds.append(fileNameAndId.last());
        }
@@ -366,20 +432,30 @@ namespace Isis {
      }

      OriginalFileToProjectCubeFunctor functor(thread(), folder, copyDnData);
      // Start concurrently copying the images to import.
      QFuture<Cube *> future = QtConcurrent::mapped(confirmedImagesFileNames, functor);

      // The new internal data will store the copied files as well as their associated unique id's.
      QStringList newInternalData;
      newInternalData.append(internalData().first());

      // By releasing a thread from the global thread pool, we are effectively temporarily
      // increasing the max number of available threads. This is useful when a thread goes to sleep
      // waiting for more work, so we can allow other threads to continue.
      // See Qt's QThreadPool::releaseThread() documentation.
      QThreadPool::globalInstance()->releaseThread();
      for (int i = 0; i < confirmedImages.count(); i++) {
        setProgressValue(i);

        // This will wait for the result at i to finish (the functor invocation finishes) and
        // get the cube.
        Cube *cube = future.resultAt(i);

        if (cube) {
          // Create a new image from the result in the thread spawned in WorkOrder::redo().
          Image *newImage = new Image(future.resultAt(i));

          // Either use a unique id that was already provided or create one for the new image.
          if (confirmedImagesIds[i].isEmpty()) {
            confirmedImagesIds[i] = newImage->id();
          }
@@ -395,16 +471,27 @@ namespace Isis {

          m_newImages->append(newImage);

          // Move the new image back and its display properities to the GUI thread.
          // Note: thread() returns the GUI thread because this ImportImagesWorkOrder lives
          // (was created) in the GUI thread.
          newImage->moveToThread(thread());
          newImage->displayProperties()->moveToThread(thread());

          newImage->closeCube();
        }
      }
      // Since we temporarily increased the max thread count (by releasing a thread), make sure
      // to re-reserve the thread for the global thread pool's accounting.
      // See Qt's QThreadPool::reserveThread().
      QThreadPool::globalInstance()->reserveThread();

      m_warning = functor.errors().toString();

      // Recall that m_newImages has thread affinity with a thread in the global thread pool.
      // Move it to the GUI-thread because these threads in the pool do not run in an event loop,
      // so they cannot process events.
      // See https://doc.qt.io/qt-5/threads-qobject.html#per-thread-event-loop
      // See http://doc.qt.io/qt-5/threads-technologies.html#comparison-of-solutions
      m_newImages->moveToThread(thread());

      setInternalData(newInternalData);
+18 −15
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ namespace Isis {
  /**
   * @brief Add cubes to a project
   *
   * @description Asks the user for a list of cube file names and whether they should be copied into
   * Asks the user for a list of cube file names and whether they should be copied into
   * the project. The cubes are then converted to external cube label files inside the project (and
   * cube files if the user said to copy the DN data). These files are then handed off to the
   * project.
@@ -62,6 +62,9 @@ namespace Isis {
   *                           undoExecution(), postSyncRedo() to postExecution(), and
   *                           postSyncUndo() to postUndoExecution(). Added isSynchronous(). This is
   *                           related to the WorkOrder redesign. Fixes #4732.
   *   @history 2017-04-11 Ian Humphrey - Removed isSynchronous() and instead set inherited member
   *                           m_isSynchronous to false in constructor to indicate this is an
   *                           asynchronous work order. Updated documentation. References #4732.
   */
  class ImportImagesWorkOrder : public WorkOrder {
      Q_OBJECT
@@ -72,14 +75,14 @@ namespace Isis {

      virtual ImportImagesWorkOrder *clone() const;

      bool isSynchronous() const;
      virtual bool setupExecution();

      bool setupExecution();
      virtual void execute();

      void execute();
      void undoExecution();
      void postExecution();
      void postUndoExecution();
    protected:
      virtual void undoExecution();
      virtual void postExecution();
      virtual void postUndoExecution();

    private:
      ImportImagesWorkOrder &operator=(const ImportImagesWorkOrder &rhs);
@@ -108,21 +111,21 @@ namespace Isis {
          //! Not implemented
          OriginalFileToProjectCubeFunctor &operator=(const OriginalFileToProjectCubeFunctor &rhs);

          QDir m_destinationFolder;
          bool m_copyDnData;
          QThread *m_guiThread;
          QDir m_destinationFolder; //! Directory where to import the images to.
          bool m_copyDnData; //! Indicates whether the cube data will be copied to the project.
          QThread *m_guiThread; //! Pointer to the GUI thread. Not used?

          QMutex m_errorsLock;
          QSharedPointer<IException> m_errors;
          QSharedPointer<int> m_numErrors;
          QMutex m_errorsLock; //! Mutex lock for appending errors and incrementing error count.
          QSharedPointer<IException> m_errors; //! Stores any errors that occur during import.
          QSharedPointer<int> m_numErrors; //! Number of errors that occur during import.
      };

    private:
      void importConfirmedImages(QStringList confirmedImages, bool copyDnData);

    private:
      ImageList *m_newImages;
      QString m_warning;
      ImageList *m_newImages; //! List of images that are being imported in this work order.
      QString m_warning; //! String of any errors/warnings that occurred during import.
  };
}
#endif // ImportImagesWorkOrder_H
+5 −13
Original line number Diff line number Diff line
@@ -30,11 +30,13 @@
namespace Isis {

/**
   * @brief Creates a WorkOrder that will set the active ImageList in the project.
   * @brief Creates a not undable WorkOrder that will set the active ImageList in the project.
   * @param project  The Project that this work order should be interacting with.
   */
  SetActiveImageListWorkOrder::SetActiveImageListWorkOrder(Project *project) :
      WorkOrder(project) {
    // This work order is not undoable
    m_isUndoable = false;

    QAction::setText(tr("Set Active Image List") );
    QUndoCommand::setText(tr("Set Active Image List"));
@@ -83,17 +85,7 @@ namespace Isis {


  /**
   * @description Indicates whether this work order is undoable.
   *
   * @return bool Returns false, indicating that setting the active image list is not undoable.
   */
  bool SetActiveImageListWorkOrder::isUndoable() const {
    return false;
  }


  /**
   * @description Simply calls the parent WorkOrder::setupExecution(). There is nothing specific
   * Simply calls the parent WorkOrder::setupExecution(). There is nothing specific
   * that this work order needs to set up before execution. This was separated from execute() as
   * part of the WorkOrder redesign.
   *
@@ -109,7 +101,7 @@ namespace Isis {
  /**
   * @brief Executes this work order.
   *
   * @description Sets the active image list for the project.
   * Sets the active image list for the project.
   */
  void SetActiveImageListWorkOrder::execute() {
    project()->setActiveImageList(imageList()->name());
+6 −3
Original line number Diff line number Diff line
@@ -27,7 +27,9 @@ namespace Isis {

  /**
   * @brief  This is a child of class WorkOrder which is used for anything that performs
   *  an action in a Project.  This work order allows the user to set the active ImageList in the
   *  an action in a Project.
   *
   *  This work order allows the user to set the active ImageList in the
   *  project.  Views that need to operate on a common ImageList, ie. footprint2dview,
   *  ControlPointEditView, etc. can get the active ImageList from project.
   *
@@ -37,7 +39,9 @@ namespace Isis {
   *   @history 2017-04-05 Ian Humphrey - Added isUndoable() implementation to indicate that this
   *                           work order is not undoable. Separated setup and execution into
   *                           setupExecution() and execute(). Fixes #4734.
   *
   *   @history 2017-04-11 Ian Humphrey - Removed isUndoable() and instead set inherited
   *                           m_isUndoable to false in constructor to indicate it is not undoable.
   *                           References #4734.
   */

  class SetActiveImageListWorkOrder : public WorkOrder {
@@ -50,7 +54,6 @@ namespace Isis {
      virtual SetActiveImageListWorkOrder *clone() const;

      virtual bool isExecutable(ImageList *imageList);
      virtual bool isUndoable() const;

      virtual bool setupExecution();
      virtual void execute();
+29 −24
Original line number Diff line number Diff line
@@ -36,16 +36,22 @@ namespace Isis {

/**
   * @brief Creates a WorkOrder that will retrieve Target info.
   *
   * Facilitates creating a view for the target information. This work order is not
   * undoable.
   *
   * @param project  The Project that this work order should be interacting with.
   */
  TargetGetInfoWorkOrder::TargetGetInfoWorkOrder(Project *project) :
      WorkOrder(project) {
    // This work order is not undoable
    m_isUndoable = false;
    QAction::setText(tr("Get Info...") );
  }


  /**
   * @brief Copies the 'other' WorkOrdeer instance into this new instance.
   * @brief Copies the 'other' WorkOrder instance into this new instance.
   * @param other The WorkOrder being copied.
   */
  TargetGetInfoWorkOrder::TargetGetInfoWorkOrder(const TargetGetInfoWorkOrder &other) :
@@ -70,10 +76,14 @@ namespace Isis {


  /**
   * @brief Determines if we already have a view for the target.  If we do, then we
   * @brief Determines if we can get target info.
   *
   * Determines if we already have a view for the target.  If we do, then we
   * do not need to redisplay the object.
   * @param targetBody
   * @return  @b bool True if a view already exists, False otherwise.
   *
   * @param targetBody The target body to check for.
   *
   * @return bool Returns true if a view for the target already exists, false otherwise.
   */
  bool TargetGetInfoWorkOrder::isExecutable(TargetBodyQsp targetBody) {
    if (!targetBody)
@@ -91,8 +101,11 @@ namespace Isis {


  /**
   * @brief Attempt to retrieve the Target info and view it.
   * @return @b bool True if successful, False otherwise.
   * @brief Attempt to retrieve the Target info.
   *
   * Attempts to retrieve the target body information to prepare for execution.
   *
   * @return bool Returns true if successful, false otherwise.
   */
  bool TargetGetInfoWorkOrder::setupExecution() {
    bool success = WorkOrder::setupExecution();
@@ -111,24 +124,14 @@ namespace Isis {


  /**
   * @brief Determines whether another WorkOrder depends upon TargetGetInfoWorkOrder.
   * @param other  The WorkOrder being checked for dependency.
   * @return @b bool  True if there is a dependency, False otherwise.
   */
  bool TargetGetInfoWorkOrder::dependsOn(WorkOrder *other) const {
    // depend on types of ourselves.
    return dynamic_cast<TargetGetInfoWorkOrder *>(other);
  }


  /**
   * @brief  Redisplays the Target info.
   * @brief Executes this work order.
   *
   * Adds a target info view to the project; i.e., displays the target info widget.
   */
  void TargetGetInfoWorkOrder::syncRedo() {
  void TargetGetInfoWorkOrder::execute() {
    TargetInfoWidget *targetInfoWidget =
        project()->directory()->addTargetInfoView(targetBody());


    if (!targetInfoWidget) {
      QString msg = "error displaying target info";
      throw IException(IException::Programmer, msg, _FILEINFO_);
@@ -137,10 +140,12 @@ namespace Isis {


  /**
   * @brief Deletes the last view. Currently this function is not implemented.
   * @brief Determines whether another WorkOrder depends upon TargetGetInfoWorkOrder.
   * @param other  The WorkOrder being checked for dependency.
   * @return @b bool  True if there is a dependency, False otherwise.
   */
  void TargetGetInfoWorkOrder::syncUndo() {
    //delete project()->directory()->cnetEditorViews().last();
  bool TargetGetInfoWorkOrder::dependsOn(WorkOrder *other) const {
    // depend on types of ourselves.
    return dynamic_cast<TargetGetInfoWorkOrder *>(other);
  }
}
Loading