diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/files.cmake b/Plugins/org.mitk.gui.qt.cmdlinemodules/files.cmake index bd086df2f0..5b8c83a816 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/files.cmake +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/files.cmake @@ -1,65 +1,65 @@ set(SRC_CPP_FILES QmitkCmdLineModuleMenuComboBox.cpp ) set(INTERNAL_CPP_FILES QmitkDataStorageComboBoxWithSelectNone.cpp QmitkDirectoryListWidget.cpp QmitkFileListWidget.cpp QmitkCmdLineModuleGui.cpp QmitkCmdLineModuleFactoryGui.cpp QmitkUiLoader.cpp org_mitk_gui_qt_cmdlinemodules_Activator.cpp CommandLineModulesViewConstants.cpp CommandLineModulesViewControls.cpp CommandLineModulesPreferencesPage.cpp CommandLineModulesView.cpp - QmitkCmdLineModuleProgressWidget.cpp + QmitkCmdLineModuleRunner.cpp ) set(UI_FILES src/internal/QmitkPathListWidget.ui src/internal/CommandLineModulesViewControls.ui src/internal/QmitkCmdLineModuleProgressWidget.ui ) set(MOC_H_FILES src/QmitkCmdLineModuleMenuComboBox.h src/internal/QmitkDataStorageComboBoxWithSelectNone.h src/internal/QmitkDirectoryListWidget.h src/internal/QmitkFileListWidget.h src/internal/QmitkCmdLineModuleGui.h src/internal/QmitkUiLoader.h src/internal/org_mitk_gui_qt_cmdlinemodules_Activator.h src/internal/CommandLineModulesViewControls.h src/internal/CommandLineModulesPreferencesPage.h src/internal/CommandLineModulesView.h - src/internal/QmitkCmdLineModuleProgressWidget.h + src/internal/QmitkCmdLineModuleRunner.h ) # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench set(CACHED_RESOURCE_FILES resources/icon.xpm resources/run.png resources/stop.png plugin.xml ) # list of Qt .qrc files which contain additional resources # specific to this plugin set(QRC_FILES resources/CommandLineModulesResources.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.cpp b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.cpp index 96598270de..049842aaff 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.cpp +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.cpp @@ -1,553 +1,552 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) University College London (UCL). All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include #include // Qmitk #include "CommandLineModulesView.h" #include "CommandLineModulesViewConstants.h" #include "CommandLineModulesViewControls.h" #include "CommandLineModulesPreferencesPage.h" #include "QmitkCmdLineModuleFactoryGui.h" #include "QmitkCmdLineModuleGui.h" -#include "QmitkCmdLineModuleProgressWidget.h" +#include "QmitkCmdLineModuleRunner.h" // Qt #include #include #include #include #include #include #include #include // CTK #include #include #include #include #include #include #include #include #include #include //----------------------------------------------------------------------------- CommandLineModulesView::CommandLineModulesView() : m_Controls(NULL) , m_Parent(NULL) , m_Layout(NULL) , m_ModuleManager(NULL) , m_ModuleBackend(NULL) , m_DirectoryWatcher(NULL) , m_TemporaryDirectoryName("") , m_MaximumConcurrentProcesses(4) , m_CurrentlyRunningProcesses(0) , m_DebugOutput(false) , m_XmlTimeoutSeconds(30) // 30 seconds = QProcess default timeout. { } //----------------------------------------------------------------------------- CommandLineModulesView::~CommandLineModulesView() { if (m_ModuleManager != NULL) { delete m_ModuleManager; } if (m_ModuleBackend != NULL) { delete m_ModuleBackend; } if (m_DirectoryWatcher != NULL) { delete m_DirectoryWatcher; } if (m_Layout != NULL) { delete m_Layout; } for (int i = 0; i < m_ListOfModules.size(); i++) { delete m_ListOfModules[i]; } } //----------------------------------------------------------------------------- void CommandLineModulesView::SetFocus() { this->m_Controls->m_ComboBox->setFocus(); } //----------------------------------------------------------------------------- void CommandLineModulesView::CreateQtPartControl( QWidget *parent ) { m_Parent = parent; if (!m_Controls) { // We create CommandLineModulesViewControls, which derives from the Qt generated class. m_Controls = new CommandLineModulesViewControls(parent); - // Create a layout to contain a display of QmitkCmdLineModuleProgressWidget. + // Create a layout to contain a display of QmitkCmdLineModuleRunner. m_Layout = new QVBoxLayout(m_Controls->m_RunningWidgets); m_Layout->setContentsMargins(0,0,0,0); m_Layout->setSpacing(0); // This must be done independent of other preferences, as we need it before // we create the ctkCmdLineModuleManager to initialise the Cache. this->RetrieveAndStoreTemporaryDirectoryPreferenceValues(); this->RetrieveAndStoreValidationMode(); // Start to create the command line module infrastructure. m_ModuleBackend = new ctkCmdLineModuleBackendLocalProcess(); m_ModuleManager = new ctkCmdLineModuleManager(m_ValidationMode, m_TemporaryDirectoryName); // Set the main object, the ctkCmdLineModuleManager onto all the objects that need it. m_Controls->m_ComboBox->SetManager(m_ModuleManager); m_DirectoryWatcher = new ctkCmdLineModuleDirectoryWatcher(m_ModuleManager); connect(this->m_DirectoryWatcher, SIGNAL(errorDetected(QString)), this, SLOT(OnDirectoryWatcherErrorsDetected(QString))); m_ModuleManager->registerBackend(m_ModuleBackend); // Setup the remaining preferences. this->RetrieveAndStorePreferenceValues(); // Connect signals to slots after we have set up GUI. connect(this->m_Controls->m_RunButton, SIGNAL(pressed()), this, SLOT(OnRunButtonPressed())); connect(this->m_Controls->m_RestoreDefaults, SIGNAL(pressed()), this, SLOT(OnRestoreButtonPressed())); connect(this->m_Controls->m_ComboBox, SIGNAL(actionChanged(QAction*)), this, SLOT(OnActionChanged(QAction*))); connect(this->m_Controls->m_TabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(OnTabCloseRequested(int))); connect(this->m_Controls->m_ClearXMLCache, SIGNAL(pressed()), this, SLOT(OnClearCache())); connect(this->m_Controls->m_ReloadModules, SIGNAL(pressed()), this, SLOT(OnReloadModules())); this->UpdateRunButtonEnabledStatus(); } } //----------------------------------------------------------------------------- berry::IBerryPreferences::Pointer CommandLineModulesView::RetrievePreferences() { berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); assert( prefService ); std::string id = "/" + CommandLineModulesViewConstants::VIEW_ID; berry::IBerryPreferences::Pointer prefs = (prefService->GetSystemPreferences()->Node(id)) .Cast(); assert( prefs ); return prefs; } //----------------------------------------------------------------------------- void CommandLineModulesView::RetrieveAndStoreTemporaryDirectoryPreferenceValues() { berry::IBerryPreferences::Pointer prefs = this->RetrievePreferences(); QString fallbackTmpDir = QDir::tempPath(); m_TemporaryDirectoryName = QString::fromStdString( prefs->Get(CommandLineModulesViewConstants::TEMPORARY_DIRECTORY_NODE_NAME, fallbackTmpDir.toStdString())); } //----------------------------------------------------------------------------- void CommandLineModulesView::RetrieveAndStoreValidationMode() { berry::IBerryPreferences::Pointer prefs = this->RetrievePreferences(); int value = prefs->GetInt(CommandLineModulesViewConstants::XML_VALIDATION_MODE, 0); if (value == 0) { m_ValidationMode = ctkCmdLineModuleManager::STRICT_VALIDATION; } else if (value == 1) { m_ValidationMode = ctkCmdLineModuleManager::SKIP_VALIDATION; } else { m_ValidationMode = ctkCmdLineModuleManager::WEAK_VALIDATION; } } //----------------------------------------------------------------------------- void CommandLineModulesView::RetrieveAndStorePreferenceValues() { berry::IBerryPreferences::Pointer prefs = this->RetrievePreferences(); QString fallbackHomeDir = QDir::homePath(); m_OutputDirectoryName = QString::fromStdString( prefs->Get(CommandLineModulesViewConstants::OUTPUT_DIRECTORY_NODE_NAME, fallbackHomeDir.toStdString())); m_MaximumConcurrentProcesses = prefs->GetInt(CommandLineModulesViewConstants::MAX_CONCURRENT, 4); m_XmlTimeoutSeconds = prefs->GetInt(CommandLineModulesViewConstants::XML_TIMEOUT_SECS, 30); m_ModuleManager->setTimeOutForXMLRetrieval(m_XmlTimeoutSeconds * 1000); // preference is in seconds, underlying CTK library in milliseconds. // Get the flag for debug output, useful when parsing all the XML. m_DebugOutput = prefs->GetBool(CommandLineModulesViewConstants::DEBUG_OUTPUT_NODE_NAME, false); m_DirectoryWatcher->setDebug(m_DebugOutput); // Show/Hide the advanced widgets this->m_Controls->SetAdvancedWidgetsVisible(prefs->GetBool(CommandLineModulesViewConstants::SHOW_ADVANCED_WIDGETS_NAME, false)); bool loadApplicationDir = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_APPLICATION_DIR, false); bool loadApplicationDirCliModules = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_APPLICATION_DIR_CLI_MODULES, true); bool loadHomeDir = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_HOME_DIR, false); bool loadHomeDirCliModules = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_HOME_DIR_CLI_MODULES, false); bool loadCurrentDir = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_CURRENT_DIR, false); bool loadCurrentDirCliModules = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_CURRENT_DIR_CLI_MODULES, false); bool loadAutoLoadDir = prefs->GetBool(CommandLineModulesViewConstants::LOAD_FROM_AUTO_LOAD_DIR, false); // Get some default application paths. // Here we can use the preferences to set up the builder, ctkCmdLineModuleDefaultPathBuilder builder; if (loadApplicationDir) builder.addApplicationDir(); if (loadApplicationDirCliModules) builder.addApplicationDir("cli-modules"); if (loadHomeDir) builder.addHomeDir(); if (loadHomeDirCliModules) builder.addHomeDir("cli-modules"); if (loadCurrentDir) builder.addCurrentDir(); if (loadCurrentDirCliModules) builder.addCurrentDir("cli-modules"); if (loadAutoLoadDir) builder.addCtkModuleLoadPath(); // and then we ask the builder to set up the paths. QStringList defaultPaths = builder.getDirectoryList(); // We get additional directory paths from preferences. QString pathString = QString::fromStdString(prefs->Get(CommandLineModulesViewConstants::MODULE_DIRECTORIES_NODE_NAME, "")); QStringList additionalPaths = pathString.split(";", QString::SkipEmptyParts); // Combine the sets of directory paths. QStringList totalPaths; totalPaths << defaultPaths; totalPaths << additionalPaths; QString additionalModulesString = QString::fromStdString(prefs->Get(CommandLineModulesViewConstants::MODULE_FILES_NODE_NAME, "")); QStringList additionalModules = additionalModulesString.split(";", QString::SkipEmptyParts); // OnPreferencesChanged can be called for each preference in a dialog box, so // when you hit "OK", it is called repeatedly, whereas we want to only call these only once. if (m_DirectoryPaths != totalPaths) { m_DirectoryPaths = totalPaths; m_DirectoryWatcher->setDirectories(totalPaths); } if (m_ModulePaths != additionalModules) { m_ModulePaths = additionalModules; m_DirectoryWatcher->setAdditionalModules(additionalModules); } } //----------------------------------------------------------------------------- void CommandLineModulesView::OnPreferencesChanged(const berry::IBerryPreferences* /*prefs*/) { this->RetrieveAndStoreTemporaryDirectoryPreferenceValues(); this->RetrieveAndStoreValidationMode(); this->RetrieveAndStorePreferenceValues(); } //----------------------------------------------------------------------------- ctkCmdLineModuleReference CommandLineModulesView::GetReferenceByFullName(QString fullName) { ctkCmdLineModuleReference result; QList references = this->m_ModuleManager->moduleReferences(); ctkCmdLineModuleReference ref; foreach(ref, references) { QString name = ref.description().categoryDotTitle(); if (name == fullName) { result = ref; } } return result; } //----------------------------------------------------------------------------- void CommandLineModulesView::OnActionChanged(QAction* action) { QString fullName = action->objectName(); ctkCmdLineModuleReference ref = this->GetReferenceByFullName(fullName); // ref should never be invalid, as the menu was generated from each ctkCmdLineModuleReference. // But just to be sure ... if invalid, don't do anything. if (ref) { // Check if we already have the reference. int tabIndex = -1; for (int i = 0; i < m_ListOfModules.size(); i++) { ctkCmdLineModuleReference tabsReference = m_ListOfModules[i]->moduleReference(); if (ref.location() == tabsReference.location()) { tabIndex = i; break; } } // i.e. we found a matching tab, so just switch to it. if (tabIndex != -1) { m_Controls->m_TabWidget->setCurrentIndex(tabIndex); } else // i.e. we did not find a matching tab { // In this case, we need to create a new tab, and give // it a GUI for the user to setup the parameters with. QmitkCmdLineModuleFactoryGui factory(this->GetDataStorage()); ctkCmdLineModuleFrontend *frontEnd = factory.create(ref); QmitkCmdLineModuleGui *theGui = dynamic_cast(frontEnd); // Add to list and tab wigdget m_ListOfModules.push_back(frontEnd); int tabIndex = m_Controls->m_TabWidget->addTab(theGui->getGui(), ref.description().title()); m_Controls->m_TabWidget->setTabToolTip(tabIndex, ref.description().title() + ":" + ref.xmlValidationErrorString()); // Here lies a small caveat. // // The XML may specify a default output file name. // However, this will probably have no file path, so we should probably add one. // Otherwise you will likely be trying to write in the application installation folder // eg. C:/Program Files (Windows) or /Applications/ (Mac) // // Also, we may find that 3rd party apps crash when they can't write. // So lets plan for the worst and hope for the best :-) QString parameterName; QList parameters; parameters = frontEnd->parameters("image", ctkCmdLineModuleFrontend::Output); parameters << frontEnd->parameters("file", ctkCmdLineModuleFrontend::Output); parameters << frontEnd->parameters("geometry", ctkCmdLineModuleFrontend::Output); foreach (ctkCmdLineModuleParameter parameter, parameters) { parameterName = parameter.name(); QString outputFileName = frontEnd->value(parameterName, ctkCmdLineModuleFrontend::DisplayRole).toString(); QFileInfo outputFileInfo(outputFileName); if (outputFileInfo.absoluteFilePath() != outputFileName) { QDir defaultOutputDir(m_OutputDirectoryName); QFileInfo replacementFileInfo(defaultOutputDir, outputFileName); frontEnd->setValue(parameterName, replacementFileInfo.absoluteFilePath(), ctkCmdLineModuleFrontend::DisplayRole); } } } } } //----------------------------------------------------------------------------- void CommandLineModulesView::OnTabCloseRequested(int tabNumber) { ctkCmdLineModuleFrontend *frontEnd = m_ListOfModules[tabNumber]; m_Controls->m_TabWidget->removeTab(tabNumber); m_ListOfModules.removeAt(tabNumber); delete frontEnd; } //----------------------------------------------------------------------------- void CommandLineModulesView::AskUserToSelectAModule() const { QMessageBox msgBox; msgBox.setText("Please select a module!"); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); } //----------------------------------------------------------------------------- void CommandLineModulesView::OnRestoreButtonPressed() { int tabNumber = m_Controls->m_TabWidget->currentIndex(); if (tabNumber >= 0) { ctkCmdLineModuleFrontend *frontEnd = m_ListOfModules[tabNumber]; frontEnd->resetValues(); } else { this->AskUserToSelectAModule(); } } //----------------------------------------------------------------------------- void CommandLineModulesView::OnRunButtonPressed() { int tabNumber = m_Controls->m_TabWidget->currentIndex(); if (tabNumber >= 0) { - // 1. Create a new QmitkCmdLineModuleProgressWidget to represent the running widget. - QmitkCmdLineModuleProgressWidget *widget = new QmitkCmdLineModuleProgressWidget(m_Controls->m_RunningWidgets); + // 1. Create a new QmitkCmdLineModuleRunner to represent the running widget. + QmitkCmdLineModuleRunner *widget = new QmitkCmdLineModuleRunner(m_Controls->m_RunningWidgets); widget->SetDataStorage(this->GetDataStorage()); widget->SetManager(m_ModuleManager); - widget->SetTemporaryDirectory(m_TemporaryDirectoryName); widget->SetOutputDirectory(m_OutputDirectoryName); // 2. Create a new front end. QmitkCmdLineModuleFactoryGui factory(this->GetDataStorage()); ctkCmdLineModuleFrontend *frontEndOnCurrentTab = m_ListOfModules[tabNumber]; QmitkCmdLineModuleGui *frontEndGuiOnCurrentTab = dynamic_cast(frontEndOnCurrentTab); ctkCmdLineModuleReference currentTabFrontendReferences = frontEndGuiOnCurrentTab->moduleReference(); ctkCmdLineModuleFrontend *newFrontEnd = factory.create(currentTabFrontendReferences); QmitkCmdLineModuleGui *newFrontEndGui = dynamic_cast(newFrontEnd); widget->SetFrontend(newFrontEndGui); m_Layout->insertWidget(0, widget); // 3. Copy parameters. This MUST come after widget->SetFrontEnd newFrontEndGui->copyParameters(*frontEndGuiOnCurrentTab); newFrontEndGui->setParameterContainerEnabled(false); // 4. Connect widget signals to here, to count how many jobs running. connect(widget, SIGNAL(started()), this, SLOT(OnJobStarted())); connect(widget, SIGNAL(finished()), this, SLOT(OnJobFinished())); // 5. GO. widget->Run(); } else { this->AskUserToSelectAModule(); } } //----------------------------------------------------------------------------- void CommandLineModulesView::UpdateRunButtonEnabledStatus() { if (m_CurrentlyRunningProcesses >= m_MaximumConcurrentProcesses) { m_Controls->m_RunButton->setEnabled(false); } else { m_Controls->m_RunButton->setEnabled(true); } } //----------------------------------------------------------------------------- void CommandLineModulesView::OnJobStarted() { m_CurrentlyRunningProcesses++; this->UpdateRunButtonEnabledStatus(); } //----------------------------------------------------------------------------- void CommandLineModulesView::OnJobFinished() { m_CurrentlyRunningProcesses--; this->UpdateRunButtonEnabledStatus(); } //----------------------------------------------------------------------------- void CommandLineModulesView::OnDirectoryWatcherErrorsDetected(const QString& errorMsg) { ctkCmdLineModuleUtils::messageBoxForModuleRegistration(errorMsg); } //----------------------------------------------------------------------------- void CommandLineModulesView::OnClearCache() { if (this->m_DebugOutput) { qDebug() << "CommandLineModulesView::OnClearCache(): starting"; } m_ModuleManager->clearCache(); if (this->m_DebugOutput) { qDebug() << "CommandLineModulesView::OnClearCache(): finishing"; } } //----------------------------------------------------------------------------- void CommandLineModulesView::OnReloadModules() { QList urls; QList moduleRefs = m_ModuleManager->moduleReferences(); foreach (ctkCmdLineModuleReference ref, moduleRefs) { urls.push_back(ref.location()); } if (this->m_DebugOutput) { qDebug() << "CommandLineModulesView::OnReloadModules(): unloading:" << urls; } foreach (ctkCmdLineModuleReference ref, moduleRefs) { m_ModuleManager->unregisterModule(ref); } if (this->m_DebugOutput) { qDebug() << "CommandLineModulesView::OnReloadModules(): reloading."; } QList refResults = QtConcurrent::blockingMapped(urls, ctkCmdLineModuleConcurrentRegister(m_ModuleManager, m_DebugOutput)); if (this->m_DebugOutput) { qDebug() << "CommandLineModulesView::OnReloadModules(): finished."; } QString errorMessages = ctkCmdLineModuleUtils::errorMessagesFromModuleRegistration(refResults, m_ModuleManager->validationMode()); ctkCmdLineModuleUtils::messageBoxForModuleRegistration(errorMessages); } diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.h b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.h index a626d0abb7..51e05c279f 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.h +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesView.h @@ -1,255 +1,255 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) University College London (UCL). All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef CommandLineModulesView_h #define CommandLineModulesView_h #include #include #include #include #include #include #include class ctkCmdLineModuleBackendLocalProcess; class ctkCmdLineModuleDirectoryWatcher; class CommandLineModulesViewControls; -class QmitkCmdLineModuleProgressWidget; +class QmitkCmdLineModuleRunner; class QAction; class QVBoxLayout; namespace berry { struct IBerryPreferences; } /*! * \class CommandLineModulesView * \brief Provides basic GUI interface to the CTK command line modules. * \author Matt Clarkson (m.clarkson@ucl.ac.uk) * \ingroup org_mitk_gui_qt_cmdlinemodules_internal * \sa QmitkAbstractView */ class CommandLineModulesView : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: CommandLineModulesView(); virtual ~CommandLineModulesView(); /** * \brief Main method, called by framework to create the GUI at the right time. * \param parent The parent QWidget, as this class itself is not a QWidget subclass. */ virtual void CreateQtPartControl(QWidget *parent); /** * \brief Called by the framework to indicate that the preferences have changed. * \param prefs not used, as we call RetrievePreferenceValues(). */ void OnPreferencesChanged(const berry::IBerryPreferences* prefs); protected Q_SLOTS: /** * \brief Called when the ctkMenuComboBox has the menu selection changed, * and causes the corresponding GUI to be displayed. */ void OnActionChanged(QAction*); /** * \brief Slot that is called when the restore defaults button is pressed, * to reset the current GUI form to the default values, if the XML specifies defaults. */ void OnRestoreButtonPressed(); /** * \brief Slot that is called when the Run button is pressed to run the current module. */ void OnRunButtonPressed(); /** * \brief Alerts the user of any errors comming out of the directory watcher. */ void OnDirectoryWatcherErrorsDetected(const QString&); protected: /** * \brief Called by framework to set the focus on the right widget * when this view has focus, so currently, thats the ctkMenuCombo box. */ virtual void SetFocus(); private slots: /** * \brief Called when the user clicks to close a tab, and removes the front end from m_ListOfModules */ void OnTabCloseRequested(int tabNumber); /** * \brief Called from QmitkCmdLineModuleProgressWidget to indicate a job has started. */ void OnJobStarted(); /** * \brief Called from QmitkCmdLineModuleProgressWidget to indicate a job has finished. */ void OnJobFinished(); /** * \brief Called when the user hits the 'clear XML cache' button. */ void OnClearCache(); /** * \brief Called when the user hits the 'reload modules' button. */ void OnReloadModules(); private: /** * \brief Called on startup and by OnPreferencesChanged to load all * preferences except the temporary folder into member variables. */ void RetrieveAndStorePreferenceValues(); /** * \brief Called on startup and by OnPreferencesChanged to load the temporary folder * preference into member variable m_TemporaryDirectoryName. */ void RetrieveAndStoreTemporaryDirectoryPreferenceValues(); /** * \brief Called on startup and by OnPreferencesChanged to set the validation mode, but will require a restart. */ void RetrieveAndStoreValidationMode(); /** * \brief Called to get hold of the actual preferences node. */ berry::IBerryPreferences::Pointer RetrievePreferences(); /** * \brief Search all modules for the one matching the given identifier. * \param fullName The "fullName" is the . from the XML. * \return ctkCmdLineModuleReference the reference corresponding to the fullName, or an invalid reference if non found. */ ctkCmdLineModuleReference GetReferenceByFullName(QString fullName); /** * \brief Raises a message box asking the user to select a module first. */ void AskUserToSelectAModule() const; /** * \brief Enables or Disables the Run Button. */ void UpdateRunButtonEnabledStatus(); /** * \brief The GUI controls contain a reset and run button, and a QWidget container, and the GUI component * for each command line module is added to the QWidget dynamically at run time. */ CommandLineModulesViewControls *m_Controls; /** * \brief We store the parent, passed in via CommandLineModulesView::CreateQtPartControl, * as this class itself is not a QWidget. */ QWidget *m_Parent; /** * \brief We keep a local layout, and arrange a display of QmitkCmdLineModuleProgressWidget, * where each QmitkCmdLineModuleProgressWidget represents a single running job. */ QVBoxLayout *m_Layout; /** * \brief The manager is responsible for loading and instantiating command line modules. */ ctkCmdLineModuleManager *m_ModuleManager; /** * \brief We are using a back-end that runs locally installed command line programs. */ ctkCmdLineModuleBackendLocalProcess *m_ModuleBackend; /** * \brief The ctkCmdLineModuleDirectoryWatcher maintains the list of directories * we are using to load modules, to provide automatic updates. */ ctkCmdLineModuleDirectoryWatcher *m_DirectoryWatcher; /** * \brief We store a temporary folder name, accessible via user preferences. */ QString m_TemporaryDirectoryName; /** * \brief We store an output folder name, accessible via user preferences for when * the file specified in a default output path is not within a writable directory. */ QString m_OutputDirectoryName; /** * \brief Cache the list of directory paths locally to avoid repeatedly trying to update Directory Watcher. */ QStringList m_DirectoryPaths; /** * \brief Cache the list of module/executable paths locally to avoid repeatedly trying to update Directory Watcher. */ QStringList m_ModulePaths; /** * \brief We store the validation mode, accessisble via user preferences. */ ctkCmdLineModuleManager::ValidationMode m_ValidationMode; /** * \brief We store the maximum number of concurrent processes, and disable the run button accordingly. */ int m_MaximumConcurrentProcesses; /** * \brief Counts the number of currently running processes. */ int m_CurrentlyRunningProcesses; /** * \brief Member variable, taken from preference page. */ bool m_DebugOutput; /** * \brief Member variable, taken from preferences page. */ int m_XmlTimeoutSeconds; /** * \brief We keep a list of front ends to match the m_TabWidget. */ QList<ctkCmdLineModuleFrontend*> m_ListOfModules; }; #endif // CommandLineModulesView_h diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.cpp b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.cpp similarity index 76% rename from Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.cpp rename to Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.cpp index e928ad843b..7168720ff5 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.cpp +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.cpp @@ -1,641 +1,655 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) University College London (UCL). All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "QmitkCmdLineModuleProgressWidget.h" +#include "QmitkCmdLineModuleRunner.h" #include "ui_QmitkCmdLineModuleProgressWidget.h" // Qt #include <QFile> #include <QFileInfo> #include <QDir> #include <QMessageBox> #include <QVBoxLayout> #include <QLayoutItem> #include <QTextBrowser> #include <QByteArray> #include <QApplication> #include <QRegExp> // CTK #include <ctkCmdLineModuleFuture.h> #include <ctkCmdLineModuleFutureWatcher.h> #include <ctkCmdLineModuleManager.h> #include <ctkCmdLineModuleFrontend.h> #include <ctkCmdLineModuleDescription.h> -#include <ctkCmdLineModuleParameter.h> #include <ctkCollapsibleGroupBox.h> // MITK #include <mitkIOUtil.h> #include <mitkDataStorage.h> -#include <mitkDataNode.h> +#include <mitkExceptionMacro.h> #include <QmitkCustomVariants.h> #include "QmitkCmdLineModuleGui.h" //----------------------------------------------------------------------------- -QmitkCmdLineModuleProgressWidget::QmitkCmdLineModuleProgressWidget(QWidget *parent) +QmitkCmdLineModuleRunner::QmitkCmdLineModuleRunner(QWidget *parent) : QWidget(parent) , m_ModuleManager(NULL) , m_DataStorage(NULL) -, m_TemporaryDirectoryName("") , m_UI(new Ui::QmitkCmdLineModuleProgressWidget) , m_Layout(NULL) , m_ModuleFrontEnd(NULL) , m_FutureWatcher(NULL) { m_UI->setupUi(this); m_UI->m_RemoveButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarCloseButton)); m_Layout = new QVBoxLayout(); m_Layout->setContentsMargins(0,0,0,0); m_Layout->setSpacing(0); m_UI->m_ParametersGroupBox->setLayout(m_Layout); qRegisterMetaType<ctkCmdLineModuleReference>(); connect(m_UI->m_RemoveButton, SIGNAL(clicked()), this, SLOT(OnRemoveButtonClicked())); // Due to Qt bug 12152, we cannot listen to the "paused" signal because it is // not emitted directly when the QFuture is paused. Instead, it is emitted after // resuming the future, after the "resume" signal has been emitted... we use // a polling approach instead. connect(&m_PollPauseTimer, SIGNAL(timeout()), SLOT(OnCheckModulePaused())); m_PollPauseTimer.setInterval(300); m_PollPauseTimer.start(); } //----------------------------------------------------------------------------- -QmitkCmdLineModuleProgressWidget::~QmitkCmdLineModuleProgressWidget() +QmitkCmdLineModuleRunner::~QmitkCmdLineModuleRunner() { if (m_ModuleFrontEnd != NULL) { delete m_ModuleFrontEnd; } this->ClearUpTemporaryFiles(); delete m_UI; } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::SetManager(ctkCmdLineModuleManager* manager) +void QmitkCmdLineModuleRunner::SetManager(ctkCmdLineModuleManager* manager) { this->m_ModuleManager = manager; } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::SetDataStorage(mitk::DataStorage* dataStorage) +void QmitkCmdLineModuleRunner::SetDataStorage(mitk::DataStorage* dataStorage) { this->m_DataStorage = dataStorage; } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::SetTemporaryDirectory(const QString& directoryName) -{ - this->m_TemporaryDirectoryName = directoryName; -} - - -//----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::SetOutputDirectory(const QString& directoryName) +void QmitkCmdLineModuleRunner::SetOutputDirectory(const QString& directoryName) { this->m_OutputDirectoryName = directoryName; } //----------------------------------------------------------------------------- -QString QmitkCmdLineModuleProgressWidget::GetTitle() +QString QmitkCmdLineModuleRunner::GetTitle() { assert(m_ModuleFrontEnd); ctkCmdLineModuleReference reference = m_ModuleFrontEnd->moduleReference(); ctkCmdLineModuleDescription description = reference.description(); return description.title(); } //----------------------------------------------------------------------------- -QString QmitkCmdLineModuleProgressWidget::GetFullName() const +QString QmitkCmdLineModuleRunner::GetFullName() const { assert(m_ModuleFrontEnd); ctkCmdLineModuleReference reference = m_ModuleFrontEnd->moduleReference(); ctkCmdLineModuleDescription description = reference.description(); return description.categoryDotTitle(); } //----------------------------------------------------------------------------- -QString QmitkCmdLineModuleProgressWidget::GetValidNodeName(const QString& nodeName) +QString QmitkCmdLineModuleRunner::GetValidNodeName(const QString& nodeName) const { QString outputName = nodeName; // We will allow A-Z, a-z, 0-9, period, hyphen and underscore in the output file name. // This method is parsing a node name, and other bits of code add on a file extension .nii. // So, in the output string from this function, we should not allow period, so that // the second recommendation on this page: // http://www.boost.org/doc/libs/1_43_0/libs/filesystem/doc/portability_guide.htm // is still true. QRegExp rx("[A-Z|a-z|0-9|-|_]{1,1}"); QString singleLetter; for (int i = 0; i < outputName.size(); i++) { if (i == 0 && outputName[i] == '-') { outputName[i] = '_'; } singleLetter = outputName[i]; if (!rx.exactMatch(singleLetter)) { outputName[i] = '-'; } } return outputName; } //----------------------------------------------------------------------------- -bool QmitkCmdLineModuleProgressWidget::IsStarted() const +bool QmitkCmdLineModuleRunner::IsStarted() const { bool isStarted = false; if (m_FutureWatcher != NULL && m_FutureWatcher->isStarted()) { isStarted = true; } return isStarted; } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnCheckModulePaused() +void QmitkCmdLineModuleRunner::OnCheckModulePaused() { if (!this->IsStarted()) { return; } if (this->m_FutureWatcher->future().isPaused()) { if (!m_UI->m_PauseButton->isChecked()) { m_UI->m_PauseButton->setChecked(true); } } else { if (m_UI->m_PauseButton->isChecked()) { m_UI->m_PauseButton->setChecked(false); } } } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnPauseButtonToggled(bool toggled) +void QmitkCmdLineModuleRunner::OnPauseButtonToggled(bool toggled) { this->m_FutureWatcher->setPaused(toggled); if (toggled) { this->m_UI->m_ProgressTitle->setText(this->GetTitle() + ": paused"); } else { this->m_UI->m_ProgressTitle->setText(this->GetTitle() + ": resumed"); } } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnRemoveButtonClicked() +void QmitkCmdLineModuleRunner::OnRemoveButtonClicked() { this->deleteLater(); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleStarted() +void QmitkCmdLineModuleRunner::OnModuleStarted() { this->m_UI->m_ProgressBar->setMaximum(0); QString message = "started."; this->PublishMessage(message); emit started(); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleCanceled() +void QmitkCmdLineModuleRunner::OnModuleCanceled() { QString message = "cancelling."; this->PublishMessage(message); this->m_UI->m_PauseButton->setEnabled(false); this->m_UI->m_PauseButton->setChecked(false); this->m_UI->m_CancelButton->setEnabled(false); this->m_UI->m_RemoveButton->setEnabled(true); this->m_UI->m_ParametersGroupBox->setCollapsed(true); this->m_UI->m_ConsoleGroupBox->setCollapsed(true); this->m_UI->m_ProgressTitle->setText(this->GetTitle() + ": cancelled"); message = "cancelled."; this->PublishMessage(message); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleFinished() +void QmitkCmdLineModuleRunner::OnModuleFinished() { this->m_UI->m_PauseButton->setEnabled(false); this->m_UI->m_PauseButton->setChecked(false); this->m_UI->m_CancelButton->setEnabled(false); this->m_UI->m_RemoveButton->setEnabled(true); if (!this->m_FutureWatcher->isCanceled()) { QString message = "finishing."; this->PublishMessage(message); // If no incremental results from stdout, try getting hold of the whole buffer and printing it. if (m_OutputCount == 0) { message = "Output channel is:"; this->PublishMessage(message); this->PublishByteArray(this->m_FutureWatcher->readAllOutputData()); } // If no incremental results from stderr, try getting hold of the whole buffer and printing it. if (m_ErrorCount == 0) { message = "Error channel is:"; this->PublishMessage(message); this->PublishByteArray(this->m_FutureWatcher->readAllErrorData()); } this->m_UI->m_ProgressTitle->setText(this->GetTitle() + ": finished"); this->LoadOutputData(); this->ClearUpTemporaryFiles(); message = "finished."; this->PublishMessage(message); } emit finished(); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleResumed() +void QmitkCmdLineModuleRunner::OnModuleResumed() { this->m_UI->m_PauseButton->setChecked(false); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleProgressRangeChanged(int progressMin, int progressMax) +void QmitkCmdLineModuleRunner::OnModuleProgressRangeChanged(int progressMin, int progressMax) { this->m_UI->m_ProgressBar->setMinimum(progressMin); this->m_UI->m_ProgressBar->setMaximum(progressMax); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleProgressTextChanged(const QString& progressText) +void QmitkCmdLineModuleRunner::OnModuleProgressTextChanged(const QString& progressText) { this->m_UI->m_Console->appendPlainText(progressText); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnModuleProgressValueChanged(int progressValue) +void QmitkCmdLineModuleRunner::OnModuleProgressValueChanged(int progressValue) { this->m_UI->m_ProgressBar->setValue(progressValue); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnOutputDataReady() +void QmitkCmdLineModuleRunner::OnOutputDataReady() { m_OutputCount++; this->PublishByteArray(this->m_FutureWatcher->readPendingOutputData()); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::OnErrorDataReady() +void QmitkCmdLineModuleRunner::OnErrorDataReady() { m_ErrorCount++; this->PublishByteArray(this->m_FutureWatcher->readPendingErrorData()); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::PublishMessage(const QString& message) +void QmitkCmdLineModuleRunner::PublishMessage(const QString& message) { QString prefix = ""; // Can put additional prefix here if needed. QString outputMessage = prefix + message; qDebug() << outputMessage; this->m_UI->m_Console->appendPlainText(outputMessage); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::PublishByteArray(const QByteArray& array) +void QmitkCmdLineModuleRunner::PublishByteArray(const QByteArray& array) { QString message = array.data(); this->PublishMessage(message); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::ClearUpTemporaryFiles() +void QmitkCmdLineModuleRunner::ClearUpTemporaryFiles() { QString message; QString fileName; - foreach (fileName, m_TemporaryFileNames) + foreach (QTemporaryFile* file, m_TemporaryFiles) { - QFile file(fileName); - if (file.exists()) - { - message = QObject::tr("removing %1").arg(fileName); - this->PublishMessage(message); + assert(file != NULL); - bool success = file.remove(); + fileName = file->fileName(); + message = QObject::tr("removing %1").arg(fileName); + this->PublishMessage(message); - message = QObject::tr("removed %1, successfully=%2").arg(fileName).arg(success); - this->PublishMessage(message); - } + delete file; } + + m_TemporaryFiles.clear(); } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::LoadOutputData() +void QmitkCmdLineModuleRunner::LoadOutputData() { assert(m_DataStorage); std::vector<std::string> fileNames; QString fileName; foreach (fileName, m_OutputDataToLoad) { QString message = QObject::tr("loading %1").arg(fileName); this->PublishMessage(message); fileNames.push_back(fileName.toStdString()); } if (fileNames.size() > 0) { int numberLoaded = mitk::IOUtil::LoadFiles(fileNames, *(m_DataStorage)); QString message = QObject::tr("loaded %1 files").arg(numberLoaded); this->PublishMessage(message); } } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::SetFrontend(QmitkCmdLineModuleGui* frontEnd) +void QmitkCmdLineModuleRunner::SetFrontend(QmitkCmdLineModuleGui* frontEnd) { assert(frontEnd); assert(m_ModuleManager); assert(m_DataStorage); // We are assuming that this method is ONLY EVER CALLED ONCE. assert(!m_ModuleFrontEnd); // Assign the frontEnd to the member variable. m_ModuleFrontEnd = frontEnd; // We put the new GUI into the layout. m_Layout->insertWidget(0, m_ModuleFrontEnd->getGui()); // And configure a few other niceties. m_UI->m_ProgressTitle->setText(this->GetTitle()); m_UI->m_ConsoleGroupBox->setCollapsed(true); // We basically call SetFrontend then Run m_UI->m_ParametersGroupBox->setCollapsed(true); // so in practice the user will only want the progress bar. } //----------------------------------------------------------------------------- -void QmitkCmdLineModuleProgressWidget::Run() +void QmitkCmdLineModuleRunner::Run() { assert(m_ModuleManager); assert(m_DataStorage); assert(m_ModuleFrontEnd); m_OutputDataToLoad.clear(); QString parameterName; QString message; QList<ctkCmdLineModuleParameter> parameters; ctkCmdLineModuleReference reference = m_ModuleFrontEnd->moduleReference(); ctkCmdLineModuleDescription description = reference.description(); // Check we have valid output. If at all possible, they should be somewhere writable. parameters = m_ModuleFrontEnd->parameters("image", ctkCmdLineModuleFrontend::Output); parameters << m_ModuleFrontEnd->parameters("file", ctkCmdLineModuleFrontend::Output); parameters << m_ModuleFrontEnd->parameters("geometry", ctkCmdLineModuleFrontend::Output); foreach (ctkCmdLineModuleParameter parameter, parameters) { parameterName = parameter.name(); QString outputFileName = m_ModuleFrontEnd->value(parameterName, ctkCmdLineModuleFrontend::DisplayRole).toString(); // Try to make sure we are not running in the application installation folder, // as more likely than not, it should not have write access, and you certainly // don't want users output files dumped there. // // eg. C:/Program Files (Windows), /Applications (Mac), /usr/local (Linux) etc. QFileInfo outputFileInfo(outputFileName); QString applicationDir = QApplication::applicationDirPath(); QString outputDir = outputFileInfo.dir().absolutePath(); if (applicationDir == outputDir) { - qDebug() << "QmitkCmdLineModuleProgressWidget::Run(), output folder = application folder, so will swap to defaultOutputDir, specified in CLI module preferences"; + qDebug() << "QmitkCmdLineModuleRunner::Run(), output folder = application folder, so will swap to defaultOutputDir, specified in CLI module preferences"; QFileInfo newOutputFileInfo(m_OutputDirectoryName, outputFileInfo.fileName()); QString newOutputFileAbsolutePath = newOutputFileInfo.absoluteFilePath(); - qDebug() << "QmitkCmdLineModuleProgressWidget::Run(), swapping " << outputFileName << " to " << newOutputFileAbsolutePath; + qDebug() << "QmitkCmdLineModuleRunner::Run(), swapping " << outputFileName << " to " << newOutputFileAbsolutePath; QMessageBox msgBox; msgBox.setText("The output directory is the same as the application installation directory"); msgBox.setInformativeText(tr("Output file:\n%1\n\nwill be swapped to\n%2").arg(outputFileName).arg(newOutputFileAbsolutePath)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); m_ModuleFrontEnd->setValue(parameterName, newOutputFileAbsolutePath, ctkCmdLineModuleFrontend::DisplayRole); } } // For each output image or file, store the filename, so we can auto-load it once the process finishes. foreach (ctkCmdLineModuleParameter parameter, parameters) { parameterName = parameter.name(); QString outputFileName = m_ModuleFrontEnd->value(parameterName, ctkCmdLineModuleFrontend::DisplayRole).toString(); if (!outputFileName.isEmpty()) { m_OutputDataToLoad.push_back(outputFileName); message = "Registered " + outputFileName + " to auto load upon completion."; this->PublishMessage(message); } } // For each input image, write a temporary file as a Nifti image (TODO - iterate through list of file formats). // and then save the full path name back on the parameter. message = "Saving image data to temporary storage..."; this->PublishMessage(message); parameters = m_ModuleFrontEnd->parameters("image", ctkCmdLineModuleFrontend::Input); foreach (ctkCmdLineModuleParameter parameter, parameters) { parameterName = parameter.name(); QVariant tmp = m_ModuleFrontEnd->value(parameterName, ctkCmdLineModuleFrontend::UserRole); mitk::DataNode::Pointer node = tmp.value<mitkDataNodePtr>(); if (node.IsNotNull()) { mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData()); if (image != NULL) { - QString name = this->GetValidNodeName(QString::fromStdString(node->GetName())); - int pid = QCoreApplication::applicationPid(); - int randomInt = qrand() % 1000000; - - QString fileNameBase = m_TemporaryDirectoryName + "/" + name + QString::number(pid) + "_" + QString::number(randomInt); - QString fileName = ""; - bool writeSucess = false; + QString errorMessage; + QTemporaryFile* tempFile = this->SaveTemporaryImage(parameter, node.GetPointer(), errorMessage); - // Try to save the image using one of the specified "fileExtensions" or - // .nii if none have been specified. - if (parameter.fileExtensions().isEmpty()) + if(tempFile == NULL) { - fileName = fileNameBase + ".nii"; - try - { - if (mitk::IOUtil::SaveBaseData( image, fileName.toStdString() )) - { - writeSucess = true; - } - } - catch(const std::exception&){} - } - else - { - foreach (QString extension, parameter.fileExtensions()) - { - if (extension[0]!='.') - fileName = fileNameBase + "." + extension; - else - fileName = fileNameBase + extension; - - try - { - if (mitk::IOUtil::SaveBaseData( image, fileName.toStdString() )) - { - writeSucess = true; - break; - } - } - catch(const std::exception&) - {} - } - } - - if(!writeSucess) - { - QStringList extensions = parameter.fileExtensions(); - if (extensions.isEmpty()) - { - extensions.push_back("nii"); - } - QMessageBox::warning(this, "Saving temporary input file failed", - QString("Unsupported file formats: ") + extensions.join(", ")); + QMessageBox::warning(this, "Saving temporary file failed", errorMessage); return; } - m_TemporaryFileNames.push_back(fileName); - m_ModuleFrontEnd->setValue(parameterName, fileName); - message = "Saved " + fileName; + m_TemporaryFiles.push_back(tempFile); + m_ModuleFrontEnd->setValue(parameterName, tempFile->fileName()); + + message = "Saved " + tempFile->fileName(); this->PublishMessage(message); + } // end if image } // end if node } // end foreach input image m_OutputCount = 0; m_ErrorCount = 0; // Now we run stuff. message = "starting."; this->PublishMessage(message); if (m_FutureWatcher == NULL) { m_FutureWatcher = new ctkCmdLineModuleFutureWatcher(); connect(m_FutureWatcher, SIGNAL(started()), SLOT(OnModuleStarted())); connect(m_FutureWatcher, SIGNAL(canceled()), SLOT(OnModuleCanceled())); connect(m_FutureWatcher, SIGNAL(finished()), SLOT(OnModuleFinished())); connect(m_FutureWatcher, SIGNAL(resumed()), SLOT(OnModuleResumed())); connect(m_FutureWatcher, SIGNAL(progressRangeChanged(int,int)), SLOT(OnModuleProgressRangeChanged(int,int))); connect(m_FutureWatcher, SIGNAL(progressTextChanged(QString)), SLOT(OnModuleProgressTextChanged(QString))); connect(m_FutureWatcher, SIGNAL(progressValueChanged(int)), SLOT(OnModuleProgressValueChanged(int))); connect(m_FutureWatcher, SIGNAL(outputDataReady()), SLOT(OnOutputDataReady())); connect(m_FutureWatcher, SIGNAL(errorDataReady()), SLOT(OnErrorDataReady())); connect(m_UI->m_CancelButton, SIGNAL(clicked()), m_FutureWatcher, SLOT(cancel())); connect(m_UI->m_PauseButton, SIGNAL(toggled(bool)), this, SLOT(OnPauseButtonToggled(bool))); } ctkCmdLineModuleFuture future = m_ModuleManager->run(m_ModuleFrontEnd); m_FutureWatcher->setFuture(future); m_UI->m_PauseButton->setEnabled(future.canPause()); m_UI->m_CancelButton->setEnabled(future.canCancel()); m_UI->m_RemoveButton->setEnabled(!future.isRunning()); // Give some immediate indication that we are running. m_UI->m_ProgressTitle->setText(description.title() + ": running"); } + + +//----------------------------------------------------------------------------- +QTemporaryFile* QmitkCmdLineModuleRunner::SaveTemporaryImage(const ctkCmdLineModuleParameter ¶meter, mitk::DataNode::ConstPointer node, QString& errorMessage) const +{ + // Don't call this if node is null or node is not an image. + assert(node.GetPointer()); + mitk::Image* image = dynamic_cast<mitk::Image*>(node->GetData()); + assert(image); + + QString intermediateError; + QString intermediateErrors; + + QTemporaryFile *returnedFile = NULL; + QString name = this->GetValidNodeName(QString::fromStdString(node->GetName())); + QString fileNameTemplate = name + "_XXXXXX"; + + // If no file extensions are specified, we default to .nii + QStringList fileExts = parameter.fileExtensions(); + if (fileExts.isEmpty()) + { + fileExts.push_back(".nii"); + } + + // Try each extension until we get a good one. + foreach (QString extension, fileExts) + { + // File extensions may or may not include the leading dot, so add one if necessary. + if (!extension.startsWith(".")) + { + extension.prepend("."); + } + fileNameTemplate = fileNameTemplate + extension; + + try + { + QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QDir::separator() + fileNameTemplate); + if (tempFile->open()) + { + tempFile->close(); + if (mitk::IOUtil::SaveBaseData( image, tempFile->fileName().toStdString() )) + { + returnedFile = tempFile; + break; + } + else + { + intermediateError = QObject::tr("Tried %1, failed to save image:\n%2\n").arg(extension).arg(tempFile->fileName()); + } + } + else + { + intermediateError = QObject::tr("Tried %1, failed to open file:\n%2\n").arg(extension).arg(tempFile->fileName()); + } + } + catch(const mitk::Exception &e) + { + intermediateError = QObject::tr("Tried %1, caught MITK Exception:\nDescription: %2\nFilename: %3\nLine: %4\n") + .arg(extension).arg(e.GetDescription()).arg(e.GetFile()).arg(e.GetLine()); + } + catch(const std::exception& e) + { + intermediateError = QObject::tr("Tried %1, caught exception:\nDescription: %2\n") + .arg(extension).arg(e.what()); + } + intermediateErrors += intermediateError; + } + errorMessage = intermediateErrors; + return returnedFile; +} diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.h b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.h similarity index 81% rename from Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.h rename to Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.h index e04c7fec35..d2f9e9efcd 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleProgressWidget.h +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/QmitkCmdLineModuleRunner.h @@ -1,228 +1,235 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) University College London (UCL). All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef QMITKCMDLINEMODULEPROGRESSWIDGET_H -#define QMITKCMDLINEMODULEPROGRESSWIDGET_H +#ifndef QMITKCMDLINEMODULERUNNER_H +#define QMITKCMDLINEMODULERUNNER_H #include <QWidget> #include <QTimer> +#include <QList> + +#include <ctkCmdLineModuleParameter.h> +#include <mitkDataNode.h> class QVBoxLayout; +class QTemporaryFile; class QmitkCmdLineModuleGui; class ctkCmdLineModuleManager; class ctkCmdLineModuleFutureWatcher; namespace Ui { class QmitkCmdLineModuleProgressWidget; } namespace mitk { class DataStorage; } /** - * \class QmitkCmdLineModuleProgressWidget + * \class QmitkCmdLineModuleRunner * \brief Based on ctkCmdLineModuleExplorerProgressWidget, implements a progress widget * with console output, and space for storing the GUI widgets. * \author Matt Clarkson (m.clarkson@ucl.ac.uk) * \ingroup org_mitk_gui_qt_cmdlinemodules_internal * \sa ctkCmdLineModuleExplorerProgressWidget */ -class QmitkCmdLineModuleProgressWidget : public QWidget +class QmitkCmdLineModuleRunner : public QWidget { Q_OBJECT public: - QmitkCmdLineModuleProgressWidget(QWidget *parent = 0); - virtual ~QmitkCmdLineModuleProgressWidget(); + QmitkCmdLineModuleRunner(QWidget *parent = 0); + virtual ~QmitkCmdLineModuleRunner(); /** * \brief Sets the manager on this object, and must be called immediately * after construction, before using the widget. */ void SetManager(ctkCmdLineModuleManager* manager); /** * \brief Sets the DataStorage on this object, and must be called immediately * after construction, before using the widget. */ void SetDataStorage(mitk::DataStorage* dataStorage); - /** - * \brief Sets the Temporary Directory on this widget, and must be called - * immediately after construction, before using the widget. - */ - void SetTemporaryDirectory(const QString& directoryName); - /** * \brief Sets the Output Directory on this widget, and must be called * immediately after construction, before using the widget. */ void SetOutputDirectory(const QString& directoryName); /** * \brief Tells this widget, which module frontend it is running * \param frontEnd our QmitkCmdLineModuleGui class derived from ctkCmdLineModuleFrontend */ void SetFrontend(QmitkCmdLineModuleGui* frontEnd); /** * \brief Runs the module that this widget is currently referring to. */ void Run(); Q_SIGNALS: // These signals so that container classes such as CommandLineModuleView // can keep track of how many modules are running simultaneously. void started(); // emmitted when the module is started. void finished(); // emmitted when the module is completely finished. private Q_SLOTS: void OnCheckModulePaused(); void OnPauseButtonToggled(bool toggled); void OnRemoveButtonClicked(); void OnModuleStarted(); void OnModuleCanceled(); void OnModuleFinished(); void OnModuleResumed(); void OnModuleProgressRangeChanged(int progressMin, int progressMax); void OnModuleProgressTextChanged(const QString& progressText); void OnModuleProgressValueChanged(int progressValue); void OnOutputDataReady(); void OnErrorDataReady(); private: /** * \brief Simply returns true if this widget is considered as having been started. */ bool IsStarted() const; /** * \brief Used to write output to the console widget, and also to qDebug(). */ void PublishMessage(const QString& message); /** * \brief Used to write output to the console widget, and also to qDebug(). */ void PublishByteArray(const QByteArray& array); /** * \brief Destroys any images listed in m_TemporaryFileNames. */ void ClearUpTemporaryFiles(); /** * \brief Loads any data listed in m_OutputDataToLoad into the m_DataStorage. */ void LoadOutputData(); + /** + * \brief Saves temporary image to file. + * \param[in] node non-NULL pointer to node containing a non-NULL mitk::Image. + * \param[out] errorMessage which if not empty means an error occurred. + * \return QTemporaryFile temporary file that the caller is responsible for deleting. + * + * If the returned QTemporaryFile is NULL, check errorMessage. + * If the returned QTemporaryFile is not-NULL, there could still be data in the errorMessage. + * It could be that this method tried n file extensions, before finding a successful one. + * In this case, the returned QTemporaryFile is the successful one, and the errorMessage contains error messages of all the failed attempts. + */ + QTemporaryFile* SaveTemporaryImage(const ctkCmdLineModuleParameter& parameter, mitk::DataNode::ConstPointer node, QString& errorMessage) const; + /** * \brief Utility method to look up the title from the description. */ QString GetTitle(); /** * \brief Returns <category>.<title>, derived from the ctkCmdLineModuleReference and * hence from the ctkCmdLineModuleDescription. */ QString GetFullName() const; /** * \brief Takes nodeName, and makes sure that it only contains A-Z, a-z, 0-9, hyphen and underscore, * and does not use hyphen as the first character. * * Inspired by <a href="http://www.boost.org/doc/libs/1_43_0/libs/filesystem/doc/portability_guide.htm">boost recommendations</a>. */ - QString GetValidNodeName(const QString& nodeName); + QString GetValidNodeName(const QString& nodeName) const; /** * \brief This must be injected before the Widget is used. */ ctkCmdLineModuleManager *m_ModuleManager; /** * \brief This must be injected before the Widget is used. */ mitk::DataStorage *m_DataStorage; - /** - * \brief This must be injected before the Widget is used. - */ - QString m_TemporaryDirectoryName; - /** * \brief This must be injected before the Widget is used. */ QString m_OutputDirectoryName; /** * \brief We instantiate the main widgets from this .ui file. */ Ui::QmitkCmdLineModuleProgressWidget *m_UI; /** * \brief The m_ParametersGroupBox needs a layout. */ QVBoxLayout *m_Layout; /** * \brief The QmitkCmdLineModuleGui is created by the QmitkCmdLineModuleFactoryGui outside * of this class and injected into this class before being run. */ QmitkCmdLineModuleGui *m_ModuleFrontEnd; /** * \brief Main object to keep track of a running command line module. */ ctkCmdLineModuleFutureWatcher *m_FutureWatcher; /** * \brief Due to Qt bug 12152, we use a timer to correctly check for a paused module. */ QTimer m_PollPauseTimer; /** * \brief We store a list of temporary file names that are saved to disk before * launching a command line app, and then must be cleared up when the command line * app successfully finishes. */ - QStringList m_TemporaryFileNames; + QList<QTemporaryFile*> m_TemporaryFiles; /** * \brief We store a list of output images, so that on successful completion of * the command line module, we automatically load the output data into the mitk::DataStorage. */ QStringList m_OutputDataToLoad; /** * \brief We track how many times the OnOutputDataReady is called. */ int m_OutputCount; /** * \brief We track how many times the OnErrorDataReady is called. */ int m_ErrorCount; }; -#endif // QMITKCMDLINEMODULEPROGRESSWIDGET_H +#endif // QMITKCMDLINEMODULERUNNER_H