Bladeren bron

Updated ctkCmdLineModuleDirectoryWatcher to watch individual files outside the current list of directories

MattClarkson 13 jaren geleden
bovenliggende
commit
5da55f58a6

+ 111 - 30
Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.cpp

@@ -76,9 +76,23 @@ QStringList ctkCmdLineModuleDirectoryWatcher::directories() const
 
 
 //-----------------------------------------------------------------------------
-QStringList ctkCmdLineModuleDirectoryWatcher::files() const
+void ctkCmdLineModuleDirectoryWatcher::setAdditionalModules(const QStringList& modules)
 {
-  return d->files();
+  d->setAdditionalModules(modules);
+}
+
+
+//-----------------------------------------------------------------------------
+QStringList ctkCmdLineModuleDirectoryWatcher::additionalModules() const
+{
+  return d->additionalModules();
+}
+
+
+//-----------------------------------------------------------------------------
+QStringList ctkCmdLineModuleDirectoryWatcher::commandLineModules() const
+{
+  return d->commandLineModules();
 }
 
 
@@ -114,6 +128,15 @@ void ctkCmdLineModuleDirectoryWatcherPrivate::setDebug(bool debug)
 
 
 //-----------------------------------------------------------------------------
+void ctkCmdLineModuleDirectoryWatcherPrivate::setDirectories(const QStringList& directories)
+{
+  QStringList validDirectories = this->filterInvalidDirectories(directories);
+  this->setModules(validDirectories);
+  this->updateWatchedPaths(validDirectories, this->MapFileNameToReference.keys());
+}
+
+
+//-----------------------------------------------------------------------------
 QStringList ctkCmdLineModuleDirectoryWatcherPrivate::directories() const
 {
   return this->FileSystemWatcher->directories();
@@ -121,34 +144,67 @@ QStringList ctkCmdLineModuleDirectoryWatcherPrivate::directories() const
 
 
 //-----------------------------------------------------------------------------
-QStringList ctkCmdLineModuleDirectoryWatcherPrivate::files() const
+QStringList ctkCmdLineModuleDirectoryWatcherPrivate::commandLineModules() const
 {
+  // So, the commandLineModules() method returns all files registered with
+  // QFileSystemWatcher, which means we must filter out any invalid ones before
+  // asking QFileSystemWatcher to watch them.
   return this->FileSystemWatcher->files();
 }
 
 
 //-----------------------------------------------------------------------------
-void ctkCmdLineModuleDirectoryWatcherPrivate::setDirectories(const QStringList& directories)
+QStringList ctkCmdLineModuleDirectoryWatcherPrivate::additionalModules() const
 {
-  QStringList validDirectories = this->filterInvalidDirectories(directories);
-  this->setModuleReferences(validDirectories);
-  this->updateWatchedPaths(validDirectories, this->MapFileNameToReference.keys());
+  // So, in comparison to commandLineModules(), we store the list of
+  // modules that are watched in addition to the directories.
+  return this->AdditionalModules;
+}
+
+
+//-----------------------------------------------------------------------------
+void ctkCmdLineModuleDirectoryWatcherPrivate::setAdditionalModules(const QStringList& executables)
+{
+  QStringList filteredFileNames = this->filterFilesNotInCurrentDirectories(executables);
+  QStringList filteredAdditionalModules = this->filterFilesNotInCurrentDirectories(this->AdditionalModules);
+
+  this->unloadModules(filteredAdditionalModules);
+  QList<ctkCmdLineModuleReference> refs = this->loadModules(filteredFileNames);
+
+  QStringList validFileNames;
+
+  for (int i = 0; i < refs.size(); ++i)
+  {
+    if (refs[i])
+    {
+      validFileNames << refs[i].location().toLocalFile();
+    }
+  }
+
+  this->AdditionalModules = validFileNames;
+  this->updateWatchedPaths(this->directories(), this->MapFileNameToReference.keys());
+
+  if (this->Debug) qDebug() << "ctkCmdLineModuleDirectoryWatcherPrivate::setAdditionalModules watching:" << this->AdditionalModules;
 }
 
 
 //-----------------------------------------------------------------------------
 void ctkCmdLineModuleDirectoryWatcherPrivate::updateWatchedPaths(const QStringList& directories, const QStringList& files)
 {
+  // This method is the main interface to QFileSystemWatcher. The input parameters
+  // directories, and files are quite simply what is being watched. So all directories
+  // and all files must be valid examples of things to watch.
+
   QStringList currentDirectories = this->directories();
-  QStringList currentFiles = this->files();
+  QStringList currentCommandLineModules = this->commandLineModules();
 
   if (currentDirectories.size() > 0)
   {
     this->FileSystemWatcher->removePaths(currentDirectories);
   }
-  if (currentFiles.size() > 0)
+  if (currentCommandLineModules.size() > 0)
   {
-    this->FileSystemWatcher->removePaths(currentFiles);
+    this->FileSystemWatcher->removePaths(currentCommandLineModules);
   }
 
   if (directories.size() > 0)
@@ -159,6 +215,11 @@ void ctkCmdLineModuleDirectoryWatcherPrivate::updateWatchedPaths(const QStringLi
   {
     this->FileSystemWatcher->addPaths(files);
   }
+
+  if (this->Debug)
+  {
+    qDebug() << "ctkCmdLineModuleDirectoryWatcherPrivate::updateWatchedPaths watching directories:\n" << directories << "\n and files:\n" << files;
+  }
 }
 
 //-----------------------------------------------------------------------------
@@ -184,27 +245,21 @@ QStringList ctkCmdLineModuleDirectoryWatcherPrivate::filterInvalidDirectories(co
 
 
 //-----------------------------------------------------------------------------
-QStringList ctkCmdLineModuleDirectoryWatcherPrivate::extractCurrentlyWatchedFilenamesInDirectory(const QString& path) const
+QStringList ctkCmdLineModuleDirectoryWatcherPrivate::filterFilesNotInCurrentDirectories(const QStringList& filenames) const
 {
-  QStringList result;
+  QStringList currentDirectories = this->directories();
+  QStringList filteredFileNames;
 
-  QDir dir(path);
-  if (dir.exists())
+  for (int i = 0; i < filenames.size(); i++)
   {
-    QList<QString> keys = this->MapFileNameToReference.keys();
+    QFileInfo fileInfo(filenames[i]);
 
-    QString fileName;
-    foreach(fileName, keys)
+    if (fileInfo.exists() && !(currentDirectories.contains(fileInfo.absolutePath())))
     {
-      QFileInfo fileInfo(fileName);
-      if (fileInfo.absolutePath() == dir.absolutePath())
-      {
-        result << fileInfo.absoluteFilePath();
-      }
+      filteredFileNames << fileInfo.absoluteFilePath();
     }
   }
-
-  return result;
+  return filteredFileNames;
 }
 
 
@@ -234,9 +289,34 @@ QStringList ctkCmdLineModuleDirectoryWatcherPrivate::getExecutablesInDirectory(c
 
 
 //-----------------------------------------------------------------------------
-void ctkCmdLineModuleDirectoryWatcherPrivate::setModuleReferences(const QStringList &directories)
+QStringList ctkCmdLineModuleDirectoryWatcherPrivate::extractCurrentlyWatchedFilenamesInDirectory(const QString& path) const
+{
+  QStringList result;
+
+  QDir dir(path);
+  if (dir.exists())
+  {
+    QList<QString> keys = this->MapFileNameToReference.keys();
+
+    QString fileName;
+    foreach(fileName, keys)
+    {
+      QFileInfo fileInfo(fileName);
+      if (fileInfo.absolutePath() == dir.absolutePath())
+      {
+        result << fileInfo.absoluteFilePath();
+      }
+    }
+  }
+
+  return result;
+}
+
+
+//-----------------------------------------------------------------------------
+void ctkCmdLineModuleDirectoryWatcherPrivate::setModules(const QStringList &directories)
 {
-  // Note: This method, is called from setDirectories and updateModuleReferences,
+  // Note: This method, is called from setDirectories and updateModules,
   // so the input directories list may be longer or shorter than the currently watched directories.
   // In addition, within those directories, programs may have been added/removed.
 
@@ -306,9 +386,9 @@ void ctkCmdLineModuleDirectoryWatcherPrivate::setModuleReferences(const QStringL
 
 
 //-----------------------------------------------------------------------------
-void ctkCmdLineModuleDirectoryWatcherPrivate::updateModuleReferences(const QString &directory)
+void ctkCmdLineModuleDirectoryWatcherPrivate::updateModules(const QString &directory)
 {
-  // Note: If updateModuleReferences is only called from onDirectoryChanged which is only called
+  // Note: If updateModules is only called from onDirectoryChanged which is only called
   // when an EXISTING directory is updated, then this if clause should never be true.
 
   QStringList currentlyWatchedDirectories = this->directories();
@@ -316,7 +396,8 @@ void ctkCmdLineModuleDirectoryWatcherPrivate::updateModuleReferences(const QStri
   {
     currentlyWatchedDirectories << directory;
   }
-  this->setModuleReferences(currentlyWatchedDirectories);
+  this->setModules(currentlyWatchedDirectories);
+  this->updateWatchedPaths(currentlyWatchedDirectories, this->MapFileNameToReference.keys());
 }
 
 
@@ -373,7 +454,7 @@ void ctkCmdLineModuleDirectoryWatcherPrivate::onDirectoryChanged(const QString &
 
   if (validDirectories.size() > 0)
   {
-    updateModuleReferences(path);
+    updateModules(path);
 
     if (this->Debug) qDebug() << "Reloaded modules in" << path;
   }

+ 41 - 9
Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.h

@@ -33,15 +33,34 @@ class ctkCmdLineModuleDirectoryWatcherPrivate;
 
 /**
  * \class ctkCmdLineModuleDirectoryWatcher
- * \brief Provides directory scanning to automatically load new modules
- * into a ctkCmdLineModuleManager.
+ * \brief Provides directory scanning and file watching via QFileSystemWatcher to
+ * automatically load new modules into a ctkCmdLineModuleManager.
+ *
  * \ingroup CommandLineModulesCore_API
  * \author m.clarkson@ucl.ac.uk
  *
- * This class provides directory scanning and automatic loading of command
- * line modules. The client should call setDirectories() to set the list of
- * directories, and listen to the signal modulesChanged to know when to
- * re-build the GUI representation.
+ * This class can be used in 3 ways.
+ *
+ * 1. The user can provide a set of directories by calling setDirectories().
+ * These directories are scanned for valid command line executables, which
+ * are registered with the ctkCmdLineModuleManager. The QFileSystemWatcher
+ * then watches for any changes in these directories and files.
+ *
+ * OR
+ *
+ * 2. The user can directly provide a list of files, which should be
+ * valid command line executables, which are registered with the ctkCmdLineModuleManager
+ * and the QFileSystemWatcher then watches for changes in these files.
+ *
+ * OR
+ *
+ * 3. Both of the above. In this case, the set of files specified must
+ * not be contained within the set of directories specified. For this reason, we have
+ * "setDirectories", and then "setAdditionalModules", as the list of files should
+ * be considered as being "in addition" to any directories we are watching.
+ *
+ * If either directories or files are invalid (not existing, not executable etc),
+ * they are filtered out and ignored.
  */
 class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleDirectoryWatcher
 : public QObject
@@ -61,7 +80,7 @@ public:
 
   /**
    * \brief Set the directories to be watched.
-   * \param directories a StringList of directory names. If any of these are
+   * \param directories a list of directory names. If any of these are
    * invalid, they will be filtered out and ignored.
    */
   void setDirectories(const QStringList& directories);
@@ -72,10 +91,23 @@ public:
   QStringList directories() const;
 
   /**
-   * \brief Returns the list of files (command line apps)
+   * \brief Sets an additional list of command line executables to watch.
+   * \param files a list of file names. If any of these file names are
+   * not valid command line executables, they will be filtered out and ignored.
+   */
+  void setAdditionalModules(const QStringList& files);
+
+  /**
+   * \brief Gets the list of additional command line executable, where
+   * "additional" means "in addition to those directories we are watching".
+   */
+  QStringList additionalModules() const;
+
+  /**
+   * \brief Returns the complete list of files (command line executables)
    * currently being watched.
    */
-  QStringList files() const;
+  QStringList commandLineModules() const;
 
 private:
 

+ 44 - 14
Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher_p.h

@@ -33,7 +33,9 @@ class QFileSystemWatcher;
 
 /**
  * \class ctkCmdLineModuleDirectoryWatcherPrivate
- * \brief Private implementation class implementing directory scanning to load new modules into a ctkCmdLineModuleManager.
+ * \brief Private implementation class implementing directory/file watching to
+ * load new modules into a ctkCmdLineModuleManager.
+ *
  * \ingroup CommandLineModulesCore_API
  * \author m.clarkson@ucl.ac.uk
  */
@@ -63,10 +65,19 @@ public:
   QStringList directories() const;
 
   /**
-   * \see ctkCmdLineModuleDirectoryWatcher::files
+   * \see ctkCmdLineModuleDirectoryWatcher::setAdditionalModules
    */
-  QStringList files() const;
+  void setAdditionalModules(const QStringList& files);
 
+  /**
+   * \see ctkCmdLineModuleDirectoryWatcher::additionalModules
+   */
+  QStringList additionalModules() const;
+
+  /**
+   * \see ctkCmdLineModuleDirectoryWatcher::commandLineModules
+   */
+  QStringList commandLineModules() const;
 
 public Q_SLOTS:
 
@@ -84,6 +95,8 @@ private:
 
   /**
    * \brief Used to update the QFileSystemWatcher with the right list of directories and files to watch.
+   * This is the main method, called by others to update what is being watched in terms of both files and directories.
+   *
    * \param directories list of absolute directory paths
    * \param files list of absolute file paths
    */
@@ -92,46 +105,62 @@ private:
   /**
    * \brief Takes a list of directories, and only returns ones that are valid,
    * meaning that the directory name is non-null, non-empty, and the directory exists.
+   *
    * \param directories a list of directories, relative or absolute.
-   * \return a list of directories, denoted by their absolute path.
+   * \return a list of valid directories, denoted by their absolute path.
    */
   QStringList filterInvalidDirectories(const QStringList& directories) const;
 
   /**
-   * \brief Uses the MapFileNameToReference to work out a list of valid command line modules in a given directory.
-   * \param directory the absolute or relative path of a directory.
-   * \return a list of executables, denoted by their absolute path.
+   * \brief Takes a list of filenames, and only returns ones that are not contained within
+   * the current list of directories that are being watched.
+   *
+   * \param filenames a list of filenames, relative or absolute.
+   * \return a list of valid filenames, denoted by absolute path, that are not contained within
+   * the current list of directories being scanned.
    */
-  QStringList extractCurrentlyWatchedFilenamesInDirectory(const QString& directory) const;
+  QStringList filterFilesNotInCurrentDirectories(const QStringList& filenames) const;
 
   /**
    * \brief Returns a list of executable files (not necessarily valid command line clients) in a directory.
-   * \param directory A directory
-   * \return QStringList a list of absolute path names to executable files
+   *
+   * \param directory the absolute or relative path of a directory.
+   * \return a list of absolute path names to executable files
    */
   QStringList getExecutablesInDirectory(const QString& directory) const;
 
   /**
-   * \brief Main method to update the current list of watched directories and files.
+   * \brief Uses the MapFileNameToReference to work out a list of valid command line modules in a given directory.
+   *
+   * \param directory the absolute or relative path of a directory.
+   * \return a list of executables, denoted by their absolute path.
+   */
+  QStringList extractCurrentlyWatchedFilenamesInDirectory(const QString& directory) const;
+
+  /**
+   * \brief Main method to update the current list of watched executables in a given set of directories.
    * \param directories a list of directories, denoted by their absolute path.
    */
-  void setModuleReferences(const QStringList &directories);
+  void setModules(const QStringList &directories);
 
   /**
-   * \brief Called from the onDirectoryChanged slot to update the current list by calling back to setModuleReferences.
+   * \brief Called from the onDirectoryChanged slot to update the current list of modules
+   * by calling back to setModules.
    * \param directory denoted by its absolute path.
    */
-  void updateModuleReferences(const QString &directory);
+  void updateModules(const QString &directory);
 
   /**
    * \brief Uses the ctkCmdLineModuleManager to try and add the executables to the list
    * of executables, and if successful it is added to this->MapFileNameToReference.
+   *
    * \param executables A list of paths to executable files, denoted by an absolute path.
    */
   QList<ctkCmdLineModuleReference> loadModules(const QStringList& executables);
 
   /**
    * \brief Removes the executables from both the ctkCmdLineModuleManager and this->MapFileNameToReference.
+   *
    * \param executables path to an executable file, denoted by its absolute path.
    */
   void unloadModules(const QStringList& executables);
@@ -139,6 +168,7 @@ private:
   QHash<QString, ctkCmdLineModuleReference> MapFileNameToReference;
   ctkCmdLineModuleManager* ModuleManager;
   QFileSystemWatcher* FileSystemWatcher;
+  QStringList AdditionalModules;
   bool Debug;
 };