diff --git a/.gitattributes b/.gitattributes index 7c6ba581c4..3f33d1ec78 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.ts text +.git* export-ignore diff --git a/Applications/ctkCommandLineModuleExplorer/CMakeLists.txt b/Applications/ctkCommandLineModuleExplorer/CMakeLists.txt index b8d9a42640..e02c4a9820 100644 --- a/Applications/ctkCommandLineModuleExplorer/CMakeLists.txt +++ b/Applications/ctkCommandLineModuleExplorer/CMakeLists.txt @@ -6,23 +6,47 @@ project(ctkCommandLineModuleExplorer) set(KIT_SRCS ctkCommandLineModuleExplorerMain.cpp - ctkCLModuleExplorerMainWindow.h - ctkCLModuleExplorerMainWindow.cpp + ctkCmdLineModuleExplorerConstants.cpp + ctkCmdLineModuleExplorerDirectorySettings.cpp + ctkCmdLineModuleExplorerGeneralModuleSettings.cpp + ctkCmdLineModuleExplorerMainWindow.h + ctkCmdLineModuleExplorerMainWindow.cpp + ctkCmdLineModuleExplorerModulesSettings.cpp + ctkCmdLineModuleExplorerOutputText.cpp + ctkCmdLineModuleExplorerProgressListWidget.cpp + ctkCmdLineModuleExplorerProgressWidget.cpp + ctkCmdLineModuleExplorerShowXmlAction.cpp + ctkCmdLineModuleExplorerTabList.cpp + ctkCmdLineModuleExplorerTreeWidget.cpp + ctkCmdLineModuleExplorerUtils.cpp ) # Headers that should run through moc set(KIT_MOC_SRCS - ctkCLModuleExplorerMainWindow.h + ctkCmdLineModuleExplorerDirectorySettings.h + ctkCmdLineModuleExplorerGeneralModuleSettings.h + ctkCmdLineModuleExplorerMainWindow.h + ctkCmdLineModuleExplorerModulesSettings.h + ctkCmdLineModuleExplorerOutputText.h + ctkCmdLineModuleExplorerProgressListWidget.h + ctkCmdLineModuleExplorerProgressWidget.h + ctkCmdLineModuleExplorerShowXmlAction.h + ctkCmdLineModuleExplorerTabList.h + ctkCmdLineModuleExplorerTreeWidget.h ) # UI files set(KIT_UI_FORMS - ctkCLModuleExplorerMainWindow.ui + ctkCmdLineModuleExplorerDirectorySettings.ui + ctkCmdLineModuleExplorerGeneralModuleSettings.ui + ctkCmdLineModuleExplorerMainWindow.ui + ctkCmdLineModuleExplorerModulesSettings.ui + ctkCmdLineModuleExplorerProgressWidget.ui ) # Resources set(KIT_resources - resources/ctkCLModuleExplorer.qrc + resources/ctkCmdLineModuleExplorer.qrc ) set(QT_USE_QTUITOOLS 1) diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.cpp deleted file mode 100644 index a067df4571..0000000000 --- a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "ctkCLModuleExplorerMainWindow.h" -#include "ui_ctkCLModuleExplorerMainWindow.h" - -#include -#include -#include -#include -//#include - -#include - -ctkCLModuleExplorerMainWindow::ctkCLModuleExplorerMainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::ctkCLModuleExplorerMainWindow), - moduleManager(new ctkCmdLineModuleInstanceFactoryQtGui()) -{ - ui->setupUi(this); -} - -ctkCLModuleExplorerMainWindow::~ctkCLModuleExplorerMainWindow() -{ - delete ui; -} - -void ctkCLModuleExplorerMainWindow::addModuleTab(const ctkCmdLineModuleReference& moduleRef) -{ - ctkCmdLineModuleInstance* moduleInstance = moduleManager.createModuleInstance(moduleRef); - if (!moduleInstance) return; - - QObject* guiHandle = moduleInstance->guiHandle(); - - QWidget* widget = qobject_cast(guiHandle); - int tabIndex = ui->mainTabWidget->addTab(widget, widget->objectName()); - mapTabToModuleRef[tabIndex] = moduleInstance; -} - -void ctkCLModuleExplorerMainWindow::addModule(const QString &location) -{ - ctkCmdLineModuleReference ref = moduleManager.registerModule(location); - if (ref) - { - addModuleTab(ref); - } -} - -void ctkCLModuleExplorerMainWindow::on_actionRun_triggered() -{ - qDebug() << "Creating module command line..."; - - ctkCmdLineModuleInstance* moduleInstance = mapTabToModuleRef[ui->mainTabWidget->currentIndex()]; - if (!moduleInstance) - { - qWarning() << "Invalid module instance"; - return; - } - - QStringList cmdLineArgs = moduleInstance->commandLineArguments(); - qDebug() << cmdLineArgs; - - - //ctkCmdLineModuleProcessFuture future = moduleInstance->run(); - //future.waitForFinished(); - //qDebug() << future.standardOutput(); - -// connect(&futureWatcher, SIGNAL(finished()), this, SLOT(futureFinished())); -// ctkCmdLineModuleProcessFuture future = moduleManager.run(moduleRef); -// futureWatcher.setFuture(future); -} - -void ctkCLModuleExplorerMainWindow::futureFinished() -{ - qDebug() << "*** Future finished"; - //qDebug() << "stdout:" << futureWatcher.future().standardOutput(); - //qDebug() << "stderr:" << futureWatcher.future().standardError(); -} - -//ctkCmdLineModuleReference ctkCLModuleExplorerMainWindow::moduleReference(int tabIndex) -//{ -// return mapTabToModuleRef[tabIndex]; -//} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.h b/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.h deleted file mode 100644 index c7e3e88125..0000000000 --- a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.h +++ /dev/null @@ -1,71 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#ifndef CTKCLIPLUGINEXPLORERMAINWINDOW_H -#define CTKCLIPLUGINEXPLORERMAINWINDOW_H - -#include -#include -//#include - -#include -#include - -class ctkCmdLineModuleReference; - -namespace Ui { -class ctkCLModuleExplorerMainWindow; -} - -class ctkCLModuleExplorerMainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit ctkCLModuleExplorerMainWindow(QWidget *parent = 0); - ~ctkCLModuleExplorerMainWindow(); - - void addModule(const QString& location); - -protected Q_SLOTS: - - void on_actionRun_triggered(); - - void futureFinished(); - -protected: - - void addModuleTab(const ctkCmdLineModuleReference& moduleRef); - - //ctkCmdLineModuleReference moduleReference(int tabIndex); - -private: - - Ui::ctkCLModuleExplorerMainWindow *ui; - - ctkCmdLineModuleManager moduleManager; - - QHash mapTabToModuleRef; - - //ctkCmdLineModuleProcessFutureWatcher futureWatcher; -}; - -#endif // CTKCLIPLUGINEXPLORERMAINWINDOW_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.ui b/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.ui deleted file mode 100644 index 711c9146f8..0000000000 --- a/Applications/ctkCommandLineModuleExplorer/ctkCLModuleExplorerMainWindow.ui +++ /dev/null @@ -1,98 +0,0 @@ - - - ctkCLModuleExplorerMainWindow - - - - 0 - 0 - 800 - 600 - - - - CTK Command Line Module Explorer - - - - - 0 - - - - - -1 - - - Qt::ElideMiddle - - - true - - - true - - - - - - - - - 0 - 0 - 800 - 25 - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - - - - :/icons/run.png:/icons/run.png - - - Run - - - Run CLI Plug-in - - - Ctrl+R - - - - - - :/icons/stop.png:/icons/stop.png - - - Stop - - - Stop CLI Plug-in - - - Ctrl+C - - - - - - - - diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.cpp new file mode 100644 index 0000000000..9a0a8712ad --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerConstants.h" + +const QString ctkCmdLineModuleExplorerConstants::KEY_SEARCH_PATHS = "ModuleSearchPaths"; +const QString ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES = "RegisteredModules"; + +const QString ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES = "MaxParallelModules"; diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.h similarity index 64% rename from Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.h rename to Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.h index 352a2aa883..bf82d94680 100644 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.h +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.h @@ -1,38 +1,35 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -=============================================================================*/ -#ifndef CTKCMDLINEMODULEINSTANCEFACTORYQTGUI_H -#define CTKCMDLINEMODULEINSTANCEFACTORYQTGUI_H +=============================================================================*/ -#include "ctkCmdLineModuleInstanceFactory.h" +#ifndef CTKCMDLINEMODULEEXPLORERCONSTANTS_H +#define CTKCMDLINEMODULEEXPLORERCONSTANTS_H -#include "ctkCommandLineModulesQtGuiExport.h" +#include -class CTK_CMDLINEMODULEQTGUI_EXPORT ctkCmdLineModuleInstanceFactoryQtGui - : public ctkCmdLineModuleInstanceFactory +struct ctkCmdLineModuleExplorerConstants { -public: - - ctkCmdLineModuleInstance* create(const ctkCmdLineModuleReference& moduleRef); + static const QString KEY_SEARCH_PATHS; + static const QString KEY_REGISTERED_MODULES; + static const QString KEY_MAX_PARALLEL_MODULES; }; -#endif // CTKCMDLINEMODULEINSTANCEFACTORYQTGUI_H +#endif // CTKCMDLINEMODULEEXPLORERCONSTANTS_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.cpp new file mode 100644 index 0000000000..7dfb8a87e9 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerDirectorySettings.h" +#include "ctkCmdLineModuleExplorerConstants.h" + +#include +#include + +#include + +ctkCmdLineModuleExplorerDirectorySettings::ctkCmdLineModuleExplorerDirectorySettings(ctkCmdLineModuleDirectoryWatcher *directoryWatcher) + : DirectoryWatcher(directoryWatcher) +{ + this->setupUi(this); + + this->PathListButtonsWidget->init(this->PathListWidget); + + this->registerProperty(ctkCmdLineModuleExplorerConstants::KEY_SEARCH_PATHS, + this->PathListWidget, "paths", SIGNAL(pathsChanged(QStringList,QStringList))); +} + +void ctkCmdLineModuleExplorerDirectorySettings::applySettings() +{ + QVariant newSearchPaths = this->propertyValue(ctkCmdLineModuleExplorerConstants::KEY_SEARCH_PATHS); + this->DirectoryWatcher->setDirectories(newSearchPaths.toStringList()); + + ctkSettingsPanel::applySettings(); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleProcess_p.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.h similarity index 55% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleProcess_p.h rename to Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.h index 1c56510d6e..dc1391bca0 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleProcess_p.h +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.h @@ -19,37 +19,32 @@ =============================================================================*/ -#ifndef CTKCMDLINEMODULEPROCESS_P_H -#define CTKCMDLINEMODULEPROCESS_P_H +#ifndef CTKCMDLINEMODULEEXPLORERDIRECTORYSETTINGS_H +#define CTKCMDLINEMODULEEXPLORERDIRECTORYSETTINGS_H -#include -#include +#include -class ctkCmdLineModuleFuture; +#include "ui_ctkCmdLineModuleExplorerDirectorySettings.h" -class ctkCmdLineModuleProcess : public QObject +class ctkCmdLineModuleDirectoryWatcher; + +/** + * \class ctkCmdLineModuleExplorerDirectorySettings + * \brief Example application settings panel. + */ +class ctkCmdLineModuleExplorerDirectorySettings : public ctkSettingsPanel, public Ui::ctkCmdLineModuleExplorerDirectorySettings { Q_OBJECT public: + ctkCmdLineModuleExplorerDirectorySettings(ctkCmdLineModuleDirectoryWatcher* directoryWatcher); - ctkCmdLineModuleProcess(const QString& location, const QStringList& args); - - ctkCmdLineModuleFuture start(); - -protected Q_SLOTS: - - void processStarted(); - - void processFinished(int exitCode, QProcess::ExitStatus status); - - void processError(QProcess::ProcessError); + void applySettings(); private: - QProcess process; - const QString location; - const QStringList args; + ctkCmdLineModuleDirectoryWatcher* DirectoryWatcher; + }; -#endif // CTKCMDLINEMODULEPROCESSRUNNER_P_H +#endif // CTKCMDLINEMODULEEXPLORERDIRECTORYSETTINGS_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.ui b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.ui new file mode 100644 index 0000000000..180bf290fc --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerDirectorySettings.ui @@ -0,0 +1,105 @@ + + + ctkCmdLineModuleExplorerDirectorySettings + + + + 0 + 0 + 400 + 300 + + + + Search Paths + + + + 6 + + + 0 + + + 0 + + + 0 + + + + + Module Search Paths + + + + + + + + + + true + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::ElideMiddle + + + ctkPathListWidget::DirectoriesOnly + + + + + + + The following list of paths will be searched for executables which provide a XML parameter description when called with a "--xml" command line argument: + + + true + + + + + + + + + + + + + ctkPathListButtonsWidget + QWidget +
ctkPathListButtonsWidget.h
+
+ + ctkPathListWidget + QListView +
ctkPathListWidget.h
+
+
+ + +
diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.cpp new file mode 100644 index 0000000000..ad684d5d84 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.cpp @@ -0,0 +1,40 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerGeneralModuleSettings.h" +#include "ctkCmdLineModuleExplorerConstants.h" + +#include +#include + +ctkCmdLineModuleExplorerGeneralModuleSettings::ctkCmdLineModuleExplorerGeneralModuleSettings() +{ + this->setupUi(this); + + this->registerProperty(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES, + this->MaxParallelModules, "value", SIGNAL(valueChanged(int))); +} + +void ctkCmdLineModuleExplorerGeneralModuleSettings::applySettings() +{ + int maxParallelModules = this->propertyValue(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES).toInt(); + QThreadPool::globalInstance()->setMaxThreadCount(maxParallelModules); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.h similarity index 61% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.cpp rename to Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.h index 2ea7e291c3..af0a2c5013 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.cpp +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.h @@ -1,39 +1,42 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ -#include "ctkCmdLineModuleReferencePrivate.h" -#include "ctkCmdLineModuleXmlParser_p.h" +#ifndef CTKCMDLINEMODULEEXPLORERGENERALMODULESETTINGS_H +#define CTKCMDLINEMODULEEXPLORERGENERALMODULESETTINGS_H -#include +#include -ctkCmdLineModuleDescription ctkCmdLineModuleReferencePrivate::description() const +#include "ui_ctkCmdLineModuleExplorerGeneralModuleSettings.h" + +class ctkCmdLineModuleExplorerGeneralModuleSettings : public ctkSettingsPanel, + public Ui::ctkCmdLineModuleExplorerGeneralModuleSettings { - // Lazy creation. The title is a requirement XML element. - if (Description.title().isNull()) - { - QByteArray xml(RawXmlDescription); - QBuffer xmlInput(&xml); - ctkCmdLineModuleXmlParser parser(&xmlInput, &Description); - parser.doParse(); - } - return Description; -} + Q_OBJECT + +public: + + ctkCmdLineModuleExplorerGeneralModuleSettings(); + + void applySettings(); + +}; +#endif // CTKCMDLINEMODULEEXPLORERGENERALMODULESETTINGS_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.ui b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.ui new file mode 100644 index 0000000000..1cdc4efff7 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.ui @@ -0,0 +1,97 @@ + + + ctkCmdLineModuleExplorerGeneralModuleSettings + + + + 0 + 0 + 400 + 300 + + + + Module Settings + + + + 6 + + + 0 + + + 0 + + + 0 + + + + + Run Settings + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + 1 + + + 999 + + + + + + + Maximum parallel running modules: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.cpp new file mode 100644 index 0000000000..a6f4957b8c --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.cpp @@ -0,0 +1,348 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerMainWindow.h" +#include "ui_ctkCmdLineModuleExplorerMainWindow.h" + +#include "ctkCmdLineModuleExplorerGeneralModuleSettings.h" +#include "ctkCmdLineModuleExplorerDirectorySettings.h" +#include "ctkCmdLineModuleExplorerModulesSettings.h" +#include "ctkCmdLineModuleExplorerTabList.h" +#include "ctkCmdLineModuleExplorerProgressWidget.h" +#include "ctkCmdLineModuleExplorerConstants.h" +#include "ctkCmdLineModuleExplorerUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + + +ctkCLModuleExplorerMainWindow::ctkCLModuleExplorerMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::ctkCmdLineModuleExplorerMainWindow), + defaultModuleFrontendFactory(NULL), + moduleManager(ctkCmdLineModuleManager::WEAK_VALIDATION, QDesktopServices::storageLocation(QDesktopServices::CacheLocation)), + directoryWatcher(&moduleManager), + settingsDialog(NULL) +{ + ui->setupUi(this); + + settings.restoreState(this->objectName(), *this); + + if (!settings.contains(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES)) + { + settings.setValue(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES, QThread::idealThreadCount()); + } + QThreadPool::globalInstance()->setMaxThreadCount(settings.value(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES, + QThread::idealThreadCount()).toInt()); + + // Frontends + moduleFrontendFactories << new ctkCmdLineModuleFrontendFactoryQtGui; + moduleFrontendFactories << new ctkCmdLineModuleFrontendFactoryQtWebKit; + defaultModuleFrontendFactory = moduleFrontendFactories.front(); + + ui->modulesTreeWidget->setModuleFrontendFactories(moduleFrontendFactories, moduleFrontendFactories.front()); + ui->modulesSearchBox->setClearIcon(QIcon(":/icons/clear.png")); + connect(ui->modulesSearchBox, SIGNAL(textChanged(QString)), ui->modulesTreeWidget, SLOT(setFilter(QString))); + + // Backends + ctkCmdLineModuleBackendFunctionPointer* backendFunctionPointer = new ctkCmdLineModuleBackendFunctionPointer; + + moduleBackends.push_back(new ctkCmdLineModuleBackendLocalProcess); + moduleBackends.push_back(backendFunctionPointer); + for(int i = 0; i < moduleBackends.size(); ++i) + { + moduleManager.registerBackend(moduleBackends[i]); + } + + tabList.reset(new ctkCmdLineModuleExplorerTabList(ui->mainTabWidget)); + + // If a module is registered via the ModuleManager, add it to the tree + connect(&moduleManager, SIGNAL(moduleRegistered(ctkCmdLineModuleReference)), ui->modulesTreeWidget, SLOT(addModuleItem(ctkCmdLineModuleReference))); + connect(&moduleManager, SIGNAL(moduleUnregistered(ctkCmdLineModuleReference)), ui->modulesTreeWidget, SLOT(removeModuleItem(ctkCmdLineModuleReference))); + // React to specific frontend creations + connect(ui->modulesTreeWidget, SIGNAL(moduleFrontendCreated(ctkCmdLineModuleFrontend*)), tabList.data(), SLOT(addTab(ctkCmdLineModuleFrontend*))); + // React to tab-changes + connect(tabList.data(), SIGNAL(tabActivated(ctkCmdLineModuleFrontend*)), SLOT(moduleTabActivated(ctkCmdLineModuleFrontend*))); + connect(tabList.data(), SIGNAL(tabActivated(ctkCmdLineModuleFrontend*)), ui->progressListWidget, SLOT(setCurrentProgressWidget(ctkCmdLineModuleFrontend*))); + connect(tabList.data(), SIGNAL(tabClosed(ctkCmdLineModuleFrontend*)), ui->outputText, SLOT(frontendRemoved(ctkCmdLineModuleFrontend*))); + connect(tabList.data(), SIGNAL(tabClosed(ctkCmdLineModuleFrontend*)), ui->progressListWidget, SLOT(removeProgressWidget(ctkCmdLineModuleFrontend*))); + + connect(ui->progressListWidget, SIGNAL(progressWidgetClicked(ctkCmdLineModuleFrontend*)), tabList.data(), SLOT(setActiveTab(ctkCmdLineModuleFrontend*))); + + connect(ui->ClearButton, SIGNAL(clicked()), ui->progressListWidget, SLOT(clearList())); + + // Listen to future events for the currently active tab + + // 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 aproach instead. + pollPauseTimer.setInterval(300); + connect(&pollPauseTimer, SIGNAL(timeout()), SLOT(checkModulePaused())); + connect(¤tFutureWatcher, SIGNAL(resumed()), SLOT(currentModuleResumed())); + connect(¤tFutureWatcher, SIGNAL(canceled()), SLOT(currentModuleCanceled())); + connect(¤tFutureWatcher, SIGNAL(finished()), SLOT(currentModuleFinished())); + + foreach(QUrl fpModule, backendFunctionPointer->registeredFunctionPointers()) + { + moduleManager.registerModule(fpModule); + } + + // Register persistent modules + QFuture future = QtConcurrent::mapped(settings.value(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES).toStringList(), + ctkCmdLineModuleConcurrentRegister(&moduleManager, true)); + + // Start watching directories + directoryWatcher.setDebug(true); + directoryWatcher.setDirectories(settings.value(ctkCmdLineModuleExplorerConstants::KEY_SEARCH_PATHS).toStringList()); + + moduleTabActivated(NULL); + + pollPauseTimer.start(); + + future.waitForFinished(); +} + +ctkCLModuleExplorerMainWindow::~ctkCLModuleExplorerMainWindow() +{ + qDeleteAll(moduleBackends); + qDeleteAll(moduleFrontendFactories); + + settings.saveState(*this, this->objectName()); + if (settingsDialog) + { + settings.saveState(*settingsDialog, settingsDialog->objectName()); + } +} + +void ctkCLModuleExplorerMainWindow::addModule(const QUrl &location) +{ + moduleManager.registerModule(location); +} + +void ctkCLModuleExplorerMainWindow::closeEvent(QCloseEvent *event) +{ + QList runningFrontends; + foreach (ctkCmdLineModuleFrontend* frontend, this->tabList->tabs()) + { + if (frontend->isRunning()) + { + runningFrontends << frontend; + } + } + + if (!runningFrontends.empty()) + { + QMessageBox::StandardButton button = + QMessageBox::warning(QApplication::activeWindow(), + QString("Closing %1 running modules").arg(runningFrontends.size()), + "Some modules are still running.\n" + "Closing the application will cancel all current computations.", + QMessageBox::Ok | QMessageBox::Cancel); + if (button == QMessageBox::Ok) + { + QFutureSynchronizer futureSync; + futureSync.setCancelOnWait(true); + foreach(ctkCmdLineModuleFrontend* frontend, runningFrontends) + { + if (frontend->future().canCancel()) + { + futureSync.addFuture(frontend->future()); + } + } + futureSync.waitForFinished(); + event->accept(); + QMainWindow::closeEvent(event); + return; + } + else + { + event->ignore(); + return; + } + } + event->accept(); +} + +void ctkCLModuleExplorerMainWindow::on_actionRun_triggered() +{ + ctkCmdLineModuleFrontend* moduleFrontend = this->tabList->activeTab(); + Q_ASSERT(moduleFrontend); + + ui->actionRun->setEnabled(false); + qobject_cast(moduleFrontend->guiHandle())->setEnabled(false); + + ctkCmdLineModuleFuture future = moduleManager.run(moduleFrontend); + + ui->progressListWidget->addProgressWidget(moduleFrontend, future); + ui->progressListWidget->setCurrentProgressWidget(moduleFrontend); + + ui->actionPause->setEnabled(future.canPause() && future.isRunning()); + ui->actionPause->setChecked(future.isPaused()); + ui->actionCancel->setEnabled(future.canCancel() && future.isRunning()); + + this->currentFutureWatcher.setFuture(future); +} + +void ctkCLModuleExplorerMainWindow::on_actionPause_toggled(bool toggled) +{ + this->currentFutureWatcher.setPaused(toggled); +} + +void ctkCLModuleExplorerMainWindow::on_actionCancel_triggered() +{ + this->currentFutureWatcher.cancel(); +} + +void ctkCLModuleExplorerMainWindow::on_actionOptions_triggered() +{ + if (settingsDialog == NULL) + { + settingsDialog = new ctkSettingsDialog(this); + settings.restoreState(settingsDialog->objectName(), *settingsDialog); + settingsDialog->setSettings(&settings); + ctkSettingsPanel* generalModulePanel = new ctkCmdLineModuleExplorerGeneralModuleSettings(); + settingsDialog->addPanel(generalModulePanel); + settingsDialog->addPanel(new ctkCmdLineModuleExplorerDirectorySettings(&directoryWatcher), generalModulePanel); + settingsDialog->addPanel(new ctkCmdLineModuleExplorerModulesSettings(&moduleManager), generalModulePanel); + } + + settingsDialog->exec(); +} + +void ctkCLModuleExplorerMainWindow::on_actionLoad_triggered() +{ + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Load modules...")); + + this->setCursor(Qt::BusyCursor); + QFuture future = QtConcurrent::mapped(fileNames, ctkCmdLineModuleConcurrentRegister(&this->moduleManager)); + future.waitForFinished(); + this->unsetCursor(); + + ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(fileNames, future.results(), + this->moduleManager.validationMode()); +} + +void ctkCLModuleExplorerMainWindow::on_actionQuit_triggered() +{ + this->close(); +} + +void ctkCLModuleExplorerMainWindow::on_actionReset_triggered() +{ + this->tabList->activeTab()->resetValues(); +} + +void ctkCLModuleExplorerMainWindow::checkModulePaused() +{ + if (this->currentFutureWatcher.future().isPaused()) + { + if (!ui->actionPause->isChecked()) + { + ui->actionPause->setChecked(true); + } + } + else + { + if (ui->actionPause->isChecked()) + { + ui->actionPause->setChecked(false); + } + } +} + +void ctkCLModuleExplorerMainWindow::currentModuleResumed() +{ + ui->actionPause->setChecked(false); +} + +void ctkCLModuleExplorerMainWindow::currentModuleCanceled() +{ + ctkCmdLineModuleFrontend* frontend = this->tabList->activeTab(); + if (frontend) + { + ui->actionCancel->setEnabled(false); + ui->actionPause->setEnabled(false); + ui->actionRun->setEnabled(true); + QWidget* widget = qobject_cast(frontend->guiHandle()); + if (widget) + { + widget->setEnabled(true); + } + } +} + +void ctkCLModuleExplorerMainWindow::currentModuleFinished() +{ + ctkCmdLineModuleFrontend* frontend = this->tabList->activeTab(); + if (frontend) + { + ui->actionCancel->setEnabled(false); + ui->actionPause->setEnabled(false); + ui->actionRun->setEnabled(true); + QWidget* widget = qobject_cast(frontend->guiHandle()); + if (widget) + { + widget->setEnabled(true); + } + } +} + +void ctkCLModuleExplorerMainWindow::moduleTabActivated(ctkCmdLineModuleFrontend *module) +{ + if (module == NULL) + { + ui->actionRun->setEnabled(false); + ui->actionPause->setEnabled(false); + ui->actionCancel->setEnabled(false); + ui->actionReset->setEnabled(false); + ui->outputText->setActiveFrontend(NULL); + currentFutureWatcher.setFuture(ctkCmdLineModuleFuture()); + } + else + { + ui->actionRun->setEnabled(!module->isRunning()); + ui->actionPause->setEnabled(module->future().canPause() && module->isRunning()); + ui->actionPause->blockSignals(true); + ui->actionPause->setChecked(module->isPaused()); + ui->actionPause->blockSignals(false); + ui->actionCancel->setEnabled(module->future().canCancel() && module->isRunning()); + ui->actionReset->setEnabled(true); + ui->outputText->setActiveFrontend(module); + currentFutureWatcher.setFuture(module->future()); + } +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.h new file mode 100644 index 0000000000..ee62d5b553 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.h @@ -0,0 +1,101 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCLIPLUGINEXPLORERMAINWINDOW_H +#define CTKCLIPLUGINEXPLORERMAINWINDOW_H + +#include +#include +#include +#include + +#include +#include +#include + +class ctkCmdLineModuleExplorerTabList; +class ctkCmdLineModuleReference; +class ctkCmdLineModuleResult; +class ctkSettingsDialog; + +namespace Ui { +class ctkCmdLineModuleExplorerMainWindow; +} + +/** + * \class ctkCLModuleExplorerMainWindow + * \brief Example application main window. + */ +class ctkCLModuleExplorerMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit ctkCLModuleExplorerMainWindow(QWidget *parent = 0); + ~ctkCLModuleExplorerMainWindow(); + + void addModule(const QUrl &location); + +protected: + + void closeEvent(QCloseEvent* event); + +protected Q_SLOTS: + + void on_actionRun_triggered(); + void on_actionPause_toggled(bool toggled); + void on_actionCancel_triggered(); + void on_actionOptions_triggered(); + void on_actionLoad_triggered(); + void on_actionQuit_triggered(); + + void on_actionReset_triggered(); + + void checkModulePaused(); + void currentModuleResumed(); + void currentModuleCanceled(); + void currentModuleFinished(); + + void moduleTabActivated(ctkCmdLineModuleFrontend* module); + +private: + + QScopedPointer ui; + QScopedPointer tabList; + + ctkCmdLineModuleFrontendFactory* defaultModuleFrontendFactory; + QList moduleFrontendFactories; + QList moduleBackends; + + ctkCmdLineModuleManager moduleManager; + + QTimer pollPauseTimer; + QFutureWatcher currentFutureWatcher; + QHash frontendToOutputMap; + + ctkCmdLineModuleDirectoryWatcher directoryWatcher; + + ctkSettings settings; + ctkSettingsDialog* settingsDialog; + +}; + +#endif // CTKCLIPLUGINEXPLORERMAINWINDOW_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.ui b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.ui new file mode 100644 index 0000000000..c4c47a3ca1 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.ui @@ -0,0 +1,380 @@ + + + ctkCmdLineModuleExplorerMainWindow + + + + 0 + 0 + 1126 + 933 + + + + CTK Command Line Module Explorer + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks + + + + + 0 + + + + + -1 + + + Qt::ElideMiddle + + + true + + + true + + + + + + + + + 0 + 0 + 1126 + 25 + + + + + &File + + + + + + + + &Module + + + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + + QDockWidget::NoDockWidgetFeatures + + + Modules + + + 1 + + + + + 2 + + + 0 + + + + + + + + QAbstractItemView::NoEditTriggers + + + Qt::ElideMiddle + + + true + + + true + + + + + + + + + QDockWidget::NoDockWidgetFeatures + + + Progress Information + + + 1 + + + + + 0 + + + 0 + + + + + + 2 + + + 6 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear + + + + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 250 + 194 + + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + + + + Qt::Vertical + + + + 20 + 64 + + + + + + + + + + + + + + + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + Qt::BottomDockWidgetArea|Qt::RightDockWidgetArea|Qt::TopDockWidgetArea + + + Output + + + 8 + + + + + 0 + + + + + true + + + + + + + + + + :/icons/run.png:/icons/run.png + + + Run + + + Run module + + + Ctrl+R + + + + + + :/icons/stop.png:/icons/stop.png + + + &Cancel + + + Cancel module + + + Ctrl+C + + + + + &Load Modules... + + + Load module + + + Ctrl+L + + + + + E&xit + + + + + true + + + + :/icons/pause.png:/icons/pause.png + + + &Pause + + + Pause module + + + Ctrl+P + + + + + &Options... + + + + + Reset + + + Reset to default values + + + + + + ctkSearchBox + QLineEdit +
ctkSearchBox.h
+
+ + ctkCmdLineModuleExplorerOutputText + QTextEdit +
ctkCmdLineModuleExplorerOutputText.h
+
+ + ctkCmdLineModuleExplorerProgressListWidget + QWidget +
ctkCmdLineModuleExplorerProgressListWidget.h
+ 1 +
+ + ctkCmdLineModuleExplorerTreeWidget + QTreeView +
ctkCmdLineModuleExplorerTreeWidget.h
+
+
+ + + + +
diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.cpp new file mode 100644 index 0000000000..945f69745b --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.cpp @@ -0,0 +1,147 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerModulesSettings.h" +#include "ctkCmdLineModuleExplorerConstants.h" +#include "ctkCmdLineModuleExplorerUtils.h" +#include "ctkCmdLineModuleExplorerShowXmlAction.h" +#include "ctkCmdLineModuleExplorerUtils.h" + +#include "ui_ctkCmdLineModuleExplorerModulesSettings.h" + +#include +#include + +#include +#include +#include +#include + +ctkCmdLineModuleExplorerModulesSettings::ctkCmdLineModuleExplorerModulesSettings(ctkCmdLineModuleManager *moduleManager) + : ui(new Ui::ctkCmdLineModuleExplorerModulesSettings) + , ModuleManager(moduleManager) + , ShowXmlAction(new ctkCmdLineModuleExplorerShowXmlAction(this)) + , ModulesRegistered(false) +{ + ui->setupUi(this); + + ui->PathListButtonsWidget->init(ui->PathListWidget); + ui->PathListWidget->addAction(this->ShowXmlAction); + ui->PathListWidget->setContextMenuPolicy(Qt::ActionsContextMenu); + + this->ShowXmlAction->setEnabled(false); + + connect(ui->PathListWidget, SIGNAL(currentPathChanged(QString,QString)), SLOT(pathSelected(QString))); + connect(ui->PathListWidget, SIGNAL(pathsChanged(QStringList,QStringList)), SLOT(pathsAdded(QStringList))); + + this->registerProperty(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES, + ui->PathListWidget, "paths", SIGNAL(pathsChanged(QStringList,QStringList))); +} + +ctkCmdLineModuleExplorerModulesSettings::~ctkCmdLineModuleExplorerModulesSettings() +{ + delete ui; +} + +void ctkCmdLineModuleExplorerModulesSettings::applySettings() +{ + QStringList oldModules = this->previousPropertyValue(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES).toStringList(); + QStringList newModules = this->propertyValue(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES).toStringList(); + + QStringList removedModules; + QStringList addedModules = newModules; + foreach(const QString& oldModule, oldModules) + { + if (!newModules.contains(oldModule)) + { + removedModules << oldModule; + } + else + { + addedModules.removeAll(oldModule); + } + } + + this->setCursor(Qt::BusyCursor); + + QFuture future1 = QtConcurrent::mapped(removedModules, ctkCmdLineModuleConcurrentUnRegister(this->ModuleManager)); + QFuture future2 = QtConcurrent::mapped(addedModules, ctkCmdLineModuleConcurrentRegister(this->ModuleManager)); + + ctkSettingsPanel::applySettings(); + + QFutureSynchronizer sync; + sync.addFuture(future1); + sync.addFuture(future2); + sync.waitForFinished(); + + this->ModulesRegistered = true; + this->pathsAdded(addedModules); + this->ModulesRegistered = false; + + this->unsetCursor(); + + ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(addedModules, future2.results(), + this->ModuleManager->validationMode()); + +} + +void ctkCmdLineModuleExplorerModulesSettings::pathSelected(const QString &path) +{ + this->ShowXmlAction->setEnabled(!path.isEmpty()); + ctkCmdLineModuleReference moduleRef = this->ModuleManager->moduleReference(QUrl::fromLocalFile(path)); + this->ShowXmlAction->setModuleReference(moduleRef); +} + +void ctkCmdLineModuleExplorerModulesSettings::pathsAdded(const QStringList &paths) +{ + // Check the validity of the entries + foreach(const QString& path, paths) + { + ctkCmdLineModuleReference moduleRef = this->ModuleManager->moduleReference(QUrl::fromLocalFile(path)); + if (!moduleRef || !moduleRef.xmlValidationErrorString().isEmpty()) + { + QStandardItem* item = ui->PathListWidget->item(path); + if (this->WarningIcon.isNull()) + { + this->WarningIcon = ctkCmdLineModuleExplorerUtils::createIconOverlay( + item->icon().pixmap(item->icon().availableSizes().front()), + QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning)); + } + + QString toolTip = path + "\n\n" + tr("Warning") + ":\n\n"; + if (moduleRef) + { + item->setIcon(this->WarningIcon); + toolTip += moduleRef.xmlValidationErrorString(); + } + else if (this->ModulesRegistered) + { + item->setIcon(this->WarningIcon); + toolTip += tr("No XML output available."); + } + else + { + toolTip = path; + } + item->setToolTip(toolTip); + } + } +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.h new file mode 100644 index 0000000000..fc3d39e62b --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.h @@ -0,0 +1,61 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLORERMODULESSETTINGS_H +#define CTKCMDLINEMODULEEXPLORERMODULESSETTINGS_H + +#include + +#include + +class ctkCmdLineModuleManager; +class ctkCmdLineModuleExplorerShowXmlAction; + +namespace Ui { +class ctkCmdLineModuleExplorerModulesSettings; +} + +class ctkCmdLineModuleExplorerModulesSettings : public ctkSettingsPanel +{ + Q_OBJECT + +public: + explicit ctkCmdLineModuleExplorerModulesSettings(ctkCmdLineModuleManager* moduleManager); + ~ctkCmdLineModuleExplorerModulesSettings(); + + void applySettings(); + +private: + + Q_SLOT void pathSelected(const QString& path); + + Q_SLOT void pathsAdded(const QStringList& paths); + + Ui::ctkCmdLineModuleExplorerModulesSettings *ui; + + ctkCmdLineModuleManager* ModuleManager; + ctkCmdLineModuleExplorerShowXmlAction* ShowXmlAction; + QIcon WarningIcon; + + bool ModulesRegistered; +}; + +#endif // CTKCMDLINEMODULEEXPLORERMODULESSETTINGS_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.ui b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.ui new file mode 100644 index 0000000000..ac7ac7c78b --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.ui @@ -0,0 +1,91 @@ + + + ctkCmdLineModuleExplorerModulesSettings + + + + 0 + 0 + 400 + 300 + + + + Registered Modules + + + + 6 + + + 0 + + + 0 + + + 0 + + + + + Registered Modules + + + + + + ctkPathListWidget::FilesOnly + + + ctkPathListWidget::Executable|ctkPathListWidget::Exists|ctkPathListWidget::Readable + + + + + + + + + true + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + ctkPathListButtonsWidget + QWidget +
ctkPathListButtonsWidget.h
+
+ + ctkPathListWidget + QListView +
ctkPathListWidget.h
+
+
+ + +
diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.cpp new file mode 100644 index 0000000000..ee4c203bce --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.cpp @@ -0,0 +1,129 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerOutputText.h" + +#include "ctkCmdLineModuleFrontend.h" +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleFutureWatcher.h" + +ctkCmdLineModuleExplorerOutputText::ctkCmdLineModuleExplorerOutputText(QWidget* parent) + : QTextEdit(parent) + , CurrentWatcher(NULL) + , CurrentFrontend(NULL) +{ +} + +ctkCmdLineModuleExplorerOutputText::~ctkCmdLineModuleExplorerOutputText() +{ + qDeleteAll(this->FrontendToWatcherMap); +} + +void ctkCmdLineModuleExplorerOutputText::setActiveFrontend(ctkCmdLineModuleFrontend* moduleFrontend) +{ + if (this->CurrentFrontend == moduleFrontend) return; + + if (this->CurrentFrontend) + { + if (this->CurrentWatcher) + { + this->CurrentWatcher->disconnect(); + } + this->CurrentFrontend->disconnect(); + + // save the current output text + this->FrontendToOutputMap[this->CurrentFrontend] = this->toHtml(); + this->clear(); + } + + this->CurrentFrontend = moduleFrontend; + if (moduleFrontend) + { + // restore previous content + this->setHtml(this->FrontendToOutputMap[moduleFrontend]); + QTextCursor endCursor = this->textCursor(); + endCursor.movePosition(QTextCursor::End); + this->setTextCursor(endCursor); + + this->CurrentWatcher = FrontendToWatcherMap[moduleFrontend]; + if (this->CurrentWatcher == NULL) + { + this->CurrentWatcher = new ctkCmdLineModuleFutureWatcher; + this->FrontendToWatcherMap[moduleFrontend] = this->CurrentWatcher; + } + + connect(this->CurrentFrontend, SIGNAL(started()), SLOT(frontendStarted())); + + connect(this->CurrentWatcher, SIGNAL(outputDataReady()), SLOT(outputDataReady())); + connect(this->CurrentWatcher, SIGNAL(errorDataReady()), SLOT(errorDataReady())); + + this->CurrentWatcher->setFuture(moduleFrontend->future()); + + // if the frontend is already finished get any output we have not yet fetched + if (moduleFrontend->future().isFinished()) + { + this->outputDataReady(); + this->errorDataReady(); + } + } + else + { + if (this->CurrentWatcher) + { + this->CurrentWatcher->disconnect(); + this->CurrentWatcher = NULL; + } + if (this->CurrentFrontend) + { + this->CurrentFrontend->disconnect(); + this->CurrentFrontend = NULL; + } + this->clear(); + } +} + +void ctkCmdLineModuleExplorerOutputText::frontendRemoved(ctkCmdLineModuleFrontend *frontend) +{ + delete this->FrontendToWatcherMap[frontend]; + this->FrontendToWatcherMap.remove(frontend); + this->FrontendToOutputMap.remove(frontend); +} + +void ctkCmdLineModuleExplorerOutputText::frontendStarted() +{ + this->clear(); + this->FrontendToOutputMap[this->CurrentFrontend].clear(); + this->CurrentWatcher->setFuture(this->CurrentFrontend->future()); +} + +void ctkCmdLineModuleExplorerOutputText::outputDataReady() +{ + QByteArray newOutput = this->CurrentWatcher->readPendingOutputData(); + this->setTextColor(QColor(Qt::black)); + this->insertPlainText(newOutput.data()); +} + +void ctkCmdLineModuleExplorerOutputText::errorDataReady() +{ + QByteArray newOutput = this->CurrentWatcher->readPendingErrorData(); + this->setTextColor(QColor(Qt::darkRed)); + this->insertPlainText(newOutput.data()); +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.h new file mode 100644 index 0000000000..120eb4c64f --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerOutputText.h @@ -0,0 +1,61 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLOREROUTPUTTEXT_H +#define CTKCMDLINEMODULEEXPLOREROUTPUTTEXT_H + + +#include + +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleFutureWatcher; + +class ctkCmdLineModuleExplorerOutputText : public QTextEdit +{ + Q_OBJECT + +public: + + ctkCmdLineModuleExplorerOutputText(QWidget* parent = 0); + ~ctkCmdLineModuleExplorerOutputText(); + +public Q_SLOTS: + + void setActiveFrontend(ctkCmdLineModuleFrontend* frontend); + + void frontendRemoved(ctkCmdLineModuleFrontend* frontend); + +private Q_SLOTS: + + void frontendStarted(); + + void outputDataReady(); + void errorDataReady(); + +private: + + ctkCmdLineModuleFutureWatcher* CurrentWatcher; + ctkCmdLineModuleFrontend* CurrentFrontend; + QHash FrontendToWatcherMap; + QHash FrontendToOutputMap; +}; + +#endif // CTKCMDLINEMODULEEXPLOREROUTPUTTEXT_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.cpp new file mode 100644 index 0000000000..b700136fdb --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.cpp @@ -0,0 +1,134 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerProgressListWidget.h" +#include "ctkCmdLineModuleExplorerProgressWidget.h" + +#include "ctkCmdLineModuleFrontend.h" +#include "ctkCmdLineModuleReference.h" +#include "ctkCmdLineModuleDescription.h" + +#include + +ctkCmdLineModuleExplorerProgressListWidget::ctkCmdLineModuleExplorerProgressListWidget(QWidget *parent) + : QWidget(parent) + , CurrentWidget(NULL) +{ + QVBoxLayout* progressLayout = new QVBoxLayout(); + progressLayout->setContentsMargins(0, 0, 0, 0); + this->setLayout(progressLayout); +} + +void ctkCmdLineModuleExplorerProgressListWidget::addProgressWidget(ctkCmdLineModuleFrontend *frontend, + const ctkCmdLineModuleFuture &future) +{ + ctkCmdLineModuleExplorerProgressWidget* progressWidget = FrontendToProgressWidgetMap[frontend]; + if (progressWidget == NULL) + { + progressWidget = new ctkCmdLineModuleExplorerProgressWidget(); + FrontendToProgressWidgetMap[frontend] = progressWidget; + ProgressWidgetToFrontendMap[progressWidget] = frontend; + + connect(progressWidget, SIGNAL(clicked()), SLOT(progressWidgetClicked())); + connect(progressWidget, SIGNAL(destroyed(QObject*)), SLOT(progressWidgetDestroyed(QObject*))); + + this->layout()->addWidget(progressWidget); + } + + progressWidget->setHighlightStyle(false); + progressWidget->setTitle(frontend->moduleReference().description().title()); + progressWidget->setFuture(future); +} + + +void ctkCmdLineModuleExplorerProgressListWidget::progressWidgetDestroyed(QObject* widget) +{ + ctkCmdLineModuleExplorerProgressWidget* progressWidget = static_cast(widget); + if (CurrentWidget == progressWidget) + { + CurrentWidget = NULL; + } + ctkCmdLineModuleFrontend* frontend = ProgressWidgetToFrontendMap.take(progressWidget); + FrontendToProgressWidgetMap.remove(frontend); +} + + +void ctkCmdLineModuleExplorerProgressListWidget::removeProgressWidget(ctkCmdLineModuleFrontend *frontend) +{ + if (FrontendToProgressWidgetMap.contains(frontend)) + { + FrontendToProgressWidgetMap[frontend]->deleteLater(); + } +} + +void ctkCmdLineModuleExplorerProgressListWidget::setCurrentProgressWidget(ctkCmdLineModuleFrontend *frontend) +{ + if (frontend == NULL && CurrentWidget != NULL) + { + CurrentWidget->setHighlightStyle(false); + CurrentWidget = NULL; + return; + } + + if (CurrentWidget != NULL) + { + CurrentWidget->setHighlightStyle(false); + CurrentWidget = NULL; + } + + ctkCmdLineModuleExplorerProgressWidget* progressWidget = FrontendToProgressWidgetMap[frontend]; + if (progressWidget) + { + progressWidget->setHighlightStyle(true); + CurrentWidget = progressWidget; + } +} + +void ctkCmdLineModuleExplorerProgressListWidget::progressWidgetClicked() +{ + ctkCmdLineModuleExplorerProgressWidget* progressWidget = + static_cast(this->sender()); + + ctkCmdLineModuleFrontend* frontend = ProgressWidgetToFrontendMap[progressWidget]; + Q_ASSERT(frontend); + + this->setCurrentProgressWidget(frontend); + emit progressWidgetClicked(frontend); +} + +void ctkCmdLineModuleExplorerProgressListWidget::clearList() +{ + QList widgetsToRemove; + QHashIterator iter(FrontendToProgressWidgetMap); + while (iter.hasNext()) + { + iter.next(); + if (!iter.key()->isRunning()) + { + widgetsToRemove.push_back(iter.value()); + } + } + + foreach(ctkCmdLineModuleExplorerProgressWidget* widget, widgetsToRemove) + { + widget->deleteLater(); + } +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.h new file mode 100644 index 0000000000..fe694aa87b --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressListWidget.h @@ -0,0 +1,67 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLORERPROGRESSLISTWIDGET_H +#define CTKCMDLINEMODULEEXPLORERPROGRESSLISTWIDGET_H + +#include +#include + +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleFuture; +class ctkCmdLineModuleExplorerProgressWidget; + +class ctkCmdLineModuleExplorerProgressListWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ctkCmdLineModuleExplorerProgressListWidget(QWidget *parent = 0); + + void addProgressWidget(ctkCmdLineModuleFrontend* frontend, const ctkCmdLineModuleFuture& future); + +signals: + + void progressWidgetClicked(ctkCmdLineModuleFrontend* frontend); + +public slots: + + void removeProgressWidget(ctkCmdLineModuleFrontend* frontend); + + void setCurrentProgressWidget(ctkCmdLineModuleFrontend* frontend); + + void progressWidgetClicked(); + + void clearList(); + +private slots: + + void progressWidgetDestroyed(QObject* progressWidget); + +private: + + QHash FrontendToProgressWidgetMap; + QHash ProgressWidgetToFrontendMap; + + ctkCmdLineModuleExplorerProgressWidget* CurrentWidget; +}; + +#endif // CTKCMDLINEMODULEEXPLORERPROGRESSLISTWIDGET_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.cpp new file mode 100644 index 0000000000..24d37f3ac8 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.cpp @@ -0,0 +1,157 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include + +#include "ctkCmdLineModuleExplorerProgressWidget.h" +#include "ui_ctkCmdLineModuleExplorerProgressWidget.h" + + +ctkCmdLineModuleExplorerProgressWidget::ctkCmdLineModuleExplorerProgressWidget(QWidget *parent) + : QWidget(parent) + , ui(new Ui::ctkCmdLineModuleExplorerProgressWidget) +{ + ui->setupUi(this); + + ui->RemoveButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + + // 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 aproach instead. + PollPauseTimer.setInterval(300); + connect(&PollPauseTimer, SIGNAL(timeout()), SLOT(checkModulePaused())); + + connect(&FutureWatcher, SIGNAL(started()), SLOT(moduleStarted())); + connect(&FutureWatcher, SIGNAL(canceled()), SLOT(moduleCanceled())); + connect(&FutureWatcher, SIGNAL(finished()), SLOT(moduleFinished())); + connect(&FutureWatcher, SIGNAL(resumed()), SLOT(moduleResumed())); + connect(&FutureWatcher, SIGNAL(progressRangeChanged(int,int)), SLOT(moduleProgressRangeChanged(int,int))); + connect(&FutureWatcher, SIGNAL(progressTextChanged(QString)), ui->ProgressText, SLOT(setText(QString))); + connect(&FutureWatcher, SIGNAL(progressValueChanged(int)), ui->ProgressBar, SLOT(setValue(int))); + + connect(ui->CancelButton, SIGNAL(clicked()), &this->FutureWatcher, SLOT(cancel())); + + PollPauseTimer.start(); +} + +ctkCmdLineModuleExplorerProgressWidget::~ctkCmdLineModuleExplorerProgressWidget() +{ + delete ui; +} + +void ctkCmdLineModuleExplorerProgressWidget::setFuture(const ctkCmdLineModuleFuture &future) +{ + ui->PauseButton->setEnabled(future.canPause()); + ui->CancelButton->setEnabled(future.canCancel()); + ui->RemoveButton->setEnabled(!future.isRunning()); + + FutureWatcher.setFuture(future); +} + +void ctkCmdLineModuleExplorerProgressWidget::setTitle(const QString &title) +{ + ui->ProgressTitle->setText(title); +} + +void ctkCmdLineModuleExplorerProgressWidget::setHighlightStyle(bool highlight) +{ + QPalette::ColorRole labelRole = highlight ? QPalette::NoRole : QPalette::Midlight; + + ui->ProgressTitle->setForegroundRole(labelRole); + ui->ProgressText->setForegroundRole(labelRole); + ui->ProgressBar->setEnabled(highlight); +} + +void ctkCmdLineModuleExplorerProgressWidget::mouseReleaseEvent(QMouseEvent*) +{ + emit clicked(); +} + +void ctkCmdLineModuleExplorerProgressWidget::on_PauseButton_toggled(bool toggled) +{ + this->FutureWatcher.setPaused(toggled); +} + +void ctkCmdLineModuleExplorerProgressWidget::on_RemoveButton_clicked() +{ + this->deleteLater(); +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleStarted() +{ + this->ui->ProgressBar->setMaximum(0); +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleCanceled() +{ + this->ui->PauseButton->setEnabled(false); + this->ui->PauseButton->setChecked(false); + this->ui->CancelButton->setEnabled(false); + this->ui->RemoveButton->setEnabled(true); +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleFinished() +{ + this->ui->PauseButton->setEnabled(false); + this->ui->PauseButton->setChecked(false); + this->ui->CancelButton->setEnabled(false); + this->ui->RemoveButton->setEnabled(true); +} + +void ctkCmdLineModuleExplorerProgressWidget::checkModulePaused() +{ + if (this->FutureWatcher.future().isPaused()) + { + if (!ui->PauseButton->isChecked()) + { + ui->PauseButton->setChecked(true); + } + } + else + { + if (ui->PauseButton->isChecked()) + { + ui->PauseButton->setChecked(false); + } + } +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleResumed() +{ + this->ui->PauseButton->setChecked(false); +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleProgressRangeChanged(int progressMin, int progressMax) +{ + this->ui->ProgressBar->setMinimum(progressMin); + this->ui->ProgressBar->setMaximum(progressMax); +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleProgressTextChanged(const QString& progressText) +{ + Q_UNUSED(progressText) +} + +void ctkCmdLineModuleExplorerProgressWidget::moduleProgressValueChanged(int progressValue) +{ + Q_UNUSED(progressValue) +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.h new file mode 100644 index 0000000000..8366263cd9 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.h @@ -0,0 +1,88 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLORERPROGRESSWIDGET_H +#define CTKCMDLINEMODULEEXPLORERPROGRESSWIDGET_H + +#include "ctkCmdLineModuleResult.h" +#include "ctkCmdLineModuleFutureWatcher.h" + +#include +#include +#include + +class ctkCmdLineModuleFuture; + +namespace Ui { +class ctkCmdLineModuleExplorerProgressWidget; +} + +/** + * \class ctkCmdLineModuleExplorerProgressWidget + * \brief Example application progress update widget. + */ +class ctkCmdLineModuleExplorerProgressWidget : public QWidget +{ + Q_OBJECT + +public: + + ctkCmdLineModuleExplorerProgressWidget(QWidget *parent = 0); + ~ctkCmdLineModuleExplorerProgressWidget(); + + void setFuture(const ctkCmdLineModuleFuture& future); + + void setTitle(const QString& title); + + void setHighlightStyle(bool highlight); + +Q_SIGNALS: + + void clicked(); + +protected: + + void mouseReleaseEvent(QMouseEvent*); + +private Q_SLOTS: + + void on_PauseButton_toggled(bool toggled); + void on_RemoveButton_clicked(); + + void checkModulePaused(); + + void moduleStarted(); + void moduleCanceled(); + void moduleFinished(); + void moduleResumed(); + void moduleProgressRangeChanged(int progressMin, int progressMax); + void moduleProgressTextChanged(const QString& progressText); + void moduleProgressValueChanged(int progressValue); + +private: + Ui::ctkCmdLineModuleExplorerProgressWidget *ui; + + ctkCmdLineModuleFutureWatcher FutureWatcher; + QTimer PollPauseTimer; + +}; + +#endif // CTKCMDLINEMODULEEXPLORERPROGRESSWIDGET_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.ui b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.ui new file mode 100644 index 0000000000..f86e303da7 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerProgressWidget.ui @@ -0,0 +1,118 @@ + + + ctkCmdLineModuleExplorerProgressWidget + + + + 0 + 0 + 400 + 83 + + + + Form + + + + 2 + + + 0 + + + + + TextLabel + + + + + + + + + 0 + + + + + + + ... + + + + :/icons/pause.png:/icons/pause.png + + + true + + + true + + + + + + + ... + + + + :/icons/stop.png:/icons/stop.png + + + true + + + + + + + Remove + + + true + + + + + + + + + TextLabel + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + + + + + Qt::Horizontal + + + + + + + + + + diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.cpp new file mode 100644 index 0000000000..88bb6fd397 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.cpp @@ -0,0 +1,89 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + + +#include "ctkCmdLineModuleExplorerShowXmlAction.h" +#include "ctkCmdLineModuleDescription.h" +#include "ctkCmdLineModuleXmlException.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +ctkCmdLineModuleExplorerShowXmlAction::ctkCmdLineModuleExplorerShowXmlAction(QObject *parent) + : QAction(parent) +{ + this->setText("Show XML Description"); + + connect(this, SIGNAL(triggered()), SLOT(run())); +} + +void ctkCmdLineModuleExplorerShowXmlAction::setModuleReference(const ctkCmdLineModuleReference& ref) +{ + this->ModuleRef = ref; +} + +void ctkCmdLineModuleExplorerShowXmlAction::run() +{ + QDialog* dialog = new QDialog(); + try + { + dialog->setWindowTitle(this->ModuleRef.description().title()); + } + catch (const ctkCmdLineModuleXmlException&) + { + dialog->setWindowTitle(this->ModuleRef.location().toString()); + } + + dialog->setLayout(new QVBoxLayout()); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(1); + QPushButton* closeButton = new QPushButton(tr("Close"), dialog); + buttonLayout->addWidget(closeButton); + + QTextEdit* textEdit = new QTextEdit(dialog); + textEdit->setPlainText(this->ModuleRef.rawXmlDescription().data()); + + QLabel* statusLabel = new QLabel(dialog); + statusLabel->setWordWrap(true); + if (this->ModuleRef.xmlValidationErrorString().isEmpty()) + { + statusLabel->setText(tr("No validation errors.")); + } + else + { + statusLabel->setText(this->ModuleRef.xmlValidationErrorString()); + } + dialog->layout()->addWidget(statusLabel); + dialog->layout()->addWidget(textEdit); + dialog->layout()->addItem(buttonLayout); + + connect(closeButton, SIGNAL(clicked()), dialog, SLOT(close())); + + dialog->resize(800, 600); + dialog->show(); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.h similarity index 61% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.h rename to Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.h index aa1d9fff5f..b3c3485b5f 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.h +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerShowXmlAction.h @@ -1,40 +1,49 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ -#ifndef CTKCMDLINEMODULEINSTANCEFACTORY_H -#define CTKCMDLINEMODULEINSTANCEFACTORY_H -#include "ctkCommandLineModulesCoreExport.h" +#ifndef CTKCMDLINEMODULEEXPLORERSHOWXMLACTION_H +#define CTKCMDLINEMODULEEXPLORERSHOWXMLACTION_H + +#include "ctkCmdLineModuleReference.h" -class ctkCmdLineModuleInstance; -class ctkCmdLineModuleReference; +#include -/** - * \ingroup CommandLineModulesCore - */ -struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleInstanceFactory +class ctkCmdLineModuleExplorerShowXmlAction : public QAction { - virtual ~ctkCmdLineModuleInstanceFactory(); + Q_OBJECT + +public: + + ctkCmdLineModuleExplorerShowXmlAction(QObject* parent); + + void setModuleReference(const ctkCmdLineModuleReference& ref); + +protected: + + Q_SLOT virtual void run(); + +private: - virtual ctkCmdLineModuleInstance* create(const ctkCmdLineModuleReference& moduleInstance) = 0; + ctkCmdLineModuleReference ModuleRef; }; -#endif // CTKCMDLINEMODULEINSTANCEFACTORY_H +#endif // CTKCMDLINEMODULEEXPLORERSHOWXMLACTION_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.cpp new file mode 100644 index 0000000000..b30f1d3015 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.cpp @@ -0,0 +1,127 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerTabList.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(ctkCmdLineModuleFrontendFactory*) + +ctkCmdLineModuleExplorerTabList::ctkCmdLineModuleExplorerTabList(QTabWidget *tabWidget) + : TabWidget(tabWidget) +{ + Q_ASSERT(TabWidget != NULL); + + connect(TabWidget, SIGNAL(currentChanged(int)), SLOT(tabIndexChanged(int))); + connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(tabCloseRequested(int))); +} + +ctkCmdLineModuleExplorerTabList::~ctkCmdLineModuleExplorerTabList() +{ + qDeleteAll(this->TabIndexToFrontend); +} + +ctkCmdLineModuleFrontend* ctkCmdLineModuleExplorerTabList::activeTab() const +{ + int index = this->TabWidget->currentIndex(); + if (index < 0) return NULL; + return this->TabIndexToFrontend[index]; +} + +QList ctkCmdLineModuleExplorerTabList::tabs() const +{ + return this->TabIndexToFrontend; +} + +void ctkCmdLineModuleExplorerTabList::setActiveTab(ctkCmdLineModuleFrontend *frontend) +{ + this->TabWidget->setCurrentIndex(this->TabIndexToFrontend.indexOf(frontend)); +} + +void ctkCmdLineModuleExplorerTabList::addTab(ctkCmdLineModuleFrontend* moduleFrontend) +{ + QWidget* widget = qobject_cast(moduleFrontend->guiHandle()); + this->TabIndexToFrontend.push_back(moduleFrontend); + int index = this->TabWidget->addTab(widget, moduleFrontend->moduleReference().description().title()); + this->TabWidget->setCurrentIndex(index); +} + +void ctkCmdLineModuleExplorerTabList::tabIndexChanged(int index) +{ + if (index < 0) + { + emit tabActivated(NULL); + return; + } + emit tabActivated(this->TabIndexToFrontend[index]); +} + +void ctkCmdLineModuleExplorerTabList::tabCloseRequested(int index) +{ + ctkCmdLineModuleFrontend* frontend = this->TabIndexToFrontend[index]; + bool removeTab = false; + + if (frontend->isRunning()) + { + if (frontend->future().canCancel()) + { + QMessageBox::StandardButton button = + QMessageBox::warning(QApplication::topLevelWidgets().front(), + "Closing a running module", + "The module '" + frontend->moduleReference().description().title() + "' is still running.\n" + "Closing the tab will cancel the current computation.", + QMessageBox::Ok | QMessageBox::Cancel); + if (button == QMessageBox::Ok) + { + frontend->future().cancel(); + removeTab = true; + } + } + else + { + QMessageBox::information(QApplication::topLevelWidgets().front(), + "Closing not possible", + "The module '" + frontend->moduleReference().description().title() + "' is still running " + "and does not support being canceled."); + } + } + else + { + removeTab = true; + } + + if (removeTab) + { + this->TabIndexToFrontend.removeAt(index); + this->TabWidget->removeTab(index); + emit this->tabClosed(frontend); + delete frontend; + } +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.h new file mode 100644 index 0000000000..3bf19656ed --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTabList.h @@ -0,0 +1,69 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLORERTABLIST_H +#define CTKCMDLINEMODULEEXPLORERTABLIST_H + +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleManager; + +#include +#include + +class QTabWidget; +class QModelIndex; + +/** + * \class ctkCmdLineModuleExplorerTabList + * \brief Example application tab list + */ +class ctkCmdLineModuleExplorerTabList : public QObject +{ + Q_OBJECT + +public: + + ctkCmdLineModuleExplorerTabList(QTabWidget* tabWidget); + ~ctkCmdLineModuleExplorerTabList(); + + ctkCmdLineModuleFrontend* activeTab() const; + + QList tabs() const; + + Q_SLOT void setActiveTab(ctkCmdLineModuleFrontend* frontend); + + Q_SLOT void addTab(ctkCmdLineModuleFrontend* frontend); + + Q_SIGNAL void tabActivated(ctkCmdLineModuleFrontend* module); + Q_SIGNAL void tabClosed(ctkCmdLineModuleFrontend* module); + +private: + + Q_SLOT void tabIndexChanged(int index); + Q_SLOT void tabCloseRequested(int index); + +private: + + QTabWidget* TabWidget; + QList TabIndexToFrontend; +}; + +#endif // CTKCMDLINEMODULEEXPLORERTABLIST_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.cpp new file mode 100644 index 0000000000..1970a4c947 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.cpp @@ -0,0 +1,301 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerTreeWidget.h" +#include "ctkCmdLineModuleExplorerShowXmlAction.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QString ctkCmdLineModuleExplorerTreeWidget::CATEGORY_UNKNOWN = "Uncategorized"; + +class ctkCmdLineModuleTreeWidgetItem : public QStandardItem +{ +public: + + ctkCmdLineModuleTreeWidgetItem(const ctkCmdLineModuleReference& moduleRef) + : QStandardItem() + , ModuleRef(moduleRef) + { + QString title; + try + { + title = ModuleRef.description().title(); + } + catch (const ctkCmdLineModuleXmlException&) + { + title = ModuleRef.location().toString(); + } + + this->setText(title + " [" + ModuleRef.backend()->name() + "]"); + this->setData(QVariant::fromValue(ModuleRef)); + QString toolTip = ModuleRef.location().toString(); + if (!ModuleRef.xmlValidationErrorString().isEmpty()) + { + this->setIcon(QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning)); + toolTip += "\n\nWarning:\n\n" + ModuleRef.xmlValidationErrorString(); + } + this->setToolTip(toolTip); + } + + ctkCmdLineModuleReference moduleReference() const + { + return ModuleRef; + } + +private: + + ctkCmdLineModuleReference ModuleRef; + +}; + +ctkCmdLineModuleExplorerTreeWidget::ctkCmdLineModuleExplorerTreeWidget(QWidget *parent) + : QTreeView(parent) + , DefaultFrontendFactory(NULL) +{ + this->ContextMenu = new QMenu(this); + this->ShowFrontendMenu = this->ContextMenu->addMenu("Create Frontend"); + + this->ShowXmlAction = new ctkCmdLineModuleExplorerShowXmlAction(this); + this->ContextMenu->addAction(ShowXmlAction); + + connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(moduleDoubleClicked(QModelIndex))); + + TreeModel = new QStandardItemModel(this); + + FilterProxyModel = new ModuleSortFilterProxyModel(this); + FilterProxyModel->setSourceModel(TreeModel); + FilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + FilterProxyModel->setDynamicSortFilter(true); + this->setModel(FilterProxyModel); +} + +void ctkCmdLineModuleExplorerTreeWidget::setModuleFrontendFactories(const QList &frontendFactories, + ctkCmdLineModuleFrontendFactory* defaultFactory) +{ + this->FrontendFactories = frontendFactories; + this->DefaultFrontendFactory = defaultFactory; + + this->ShowFrontendMenu->clear(); + foreach(ctkCmdLineModuleFrontendFactory* factory, this->FrontendFactories) + { + QAction* action = this->ShowFrontendMenu->addAction(factory->name(), this, SLOT(frontendFactoryActionTriggered())); + this->ActionsToFrontendFactoryMap[action] = factory; + action->setToolTip(factory->description()); + } +} + +void ctkCmdLineModuleExplorerTreeWidget::addModuleItem(const ctkCmdLineModuleReference &moduleRef) +{ + QString categories; + try + { + categories = moduleRef.description().category(); + } + catch (const ctkCmdLineModuleXmlException&) + { + categories = CATEGORY_UNKNOWN; + } + + if (categories.isEmpty()) + { + categories = CATEGORY_UNKNOWN; + } + + QString currentCategories; + QStandardItem* oldRootItem = NULL; + foreach (const QString& category, categories.split('.', QString::SkipEmptyParts)) + { + currentCategories += (currentCategories.isEmpty() ? QString() : QString(".")) + category; + QStandardItem* rootItem = TreeWidgetCategories[currentCategories]; + if (rootItem == NULL) + { + rootItem = new QStandardItem(category); + TreeWidgetCategories[currentCategories] = rootItem; + if (oldRootItem != NULL) + { + oldRootItem->appendRow(rootItem); + } + else + { + TreeModel->appendRow(rootItem); + } + } + oldRootItem = rootItem; + } + + QStandardItem* moduleItem = new ctkCmdLineModuleTreeWidgetItem(moduleRef); + TreeWidgetItems[moduleRef] = moduleItem; + oldRootItem->appendRow(moduleItem); +} + +void ctkCmdLineModuleExplorerTreeWidget::removeModuleItem(const ctkCmdLineModuleReference &moduleRef) +{ + QStandardItem* treeWidgetItem = TreeWidgetItems.take(moduleRef); + if (treeWidgetItem == NULL) return; + + QString categories; + try + { + categories = moduleRef.description().category(); + } + catch (const ctkCmdLineModuleXmlException&) + { + categories = CATEGORY_UNKNOWN; + } + + if (categories.isEmpty()) + { + categories = CATEGORY_UNKNOWN; + } + + QStringList categoryList = categories.split('.', QString::SkipEmptyParts); + while (!categoryList.isEmpty()) + { + QStandardItem* rootItem = TreeWidgetCategories[categoryList.join(".")]; + Q_ASSERT(rootItem); + + rootItem->removeRow(treeWidgetItem->row()); + + if (rootItem->rowCount() == 0) + { + treeWidgetItem = rootItem; + TreeWidgetCategories.remove(categoryList.join(".")); + categoryList.pop_back(); + } + else + { + break; + } + } + if (categoryList.isEmpty()) + { + TreeModel->removeRow(treeWidgetItem->row()); + } +} + +void ctkCmdLineModuleExplorerTreeWidget::contextMenuEvent(QContextMenuEvent *event) +{ + QModelIndex index = this->indexAt(this->viewport()->mapFromGlobal(event->globalPos())); + if (index.isValid() && index.data(Qt::UserRole+1).isValid()) + { + this->ContextReference = index.data(Qt::UserRole+1).value(); + this->ShowXmlAction->setModuleReference(this->ContextReference); + this->ContextMenu->exec(event->globalPos()); + event->accept(); + } + else + { + this->ContextReference = ctkCmdLineModuleReference(); + event->ignore(); + } +} + +void ctkCmdLineModuleExplorerTreeWidget::moduleDoubleClicked(const QModelIndex &index) +{ + if (this->DefaultFrontendFactory == NULL) return; + + QVariant data = index.data(Qt::UserRole+1); + if (!data.isValid()) return; + + ctkCmdLineModuleReference moduleRef = data.value(); + if (!moduleRef) return; + + this->createFrontend(moduleRef, this->DefaultFrontendFactory); +} + +void ctkCmdLineModuleExplorerTreeWidget::frontendFactoryActionTriggered() +{ + ctkCmdLineModuleFrontendFactory* frontendFactory = this->ActionsToFrontendFactoryMap[static_cast(this->sender())]; + this->createFrontend(this->ContextReference, frontendFactory); +} + + +void ctkCmdLineModuleExplorerTreeWidget::setFilter(const QString &filter) +{ + this->FilterProxyModel->setFilterWildcard(filter); +} + +ctkCmdLineModuleFrontend* ctkCmdLineModuleExplorerTreeWidget::createFrontend(const ctkCmdLineModuleReference &moduleRef, + ctkCmdLineModuleFrontendFactory* frontendFactory) +{ + try + { + moduleRef.description(); + ctkCmdLineModuleFrontend* moduleFrontend = frontendFactory->create(moduleRef); + emit moduleFrontendCreated(moduleFrontend); + return moduleFrontend; + } + catch (const ctkException& e) + { + QMessageBox::information(this, "Frontend creation failed", "Creating a " + frontendFactory->name() + + " frontend failed:\n\n" + e.what()); + return NULL; + } +} + + +bool ModuleSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + + QModelIndex childIndex = index.child(0, 0); + if (childIndex.isValid()) + { + int i = 0; + bool accept = false; + while(childIndex.isValid()) + { + accept = this->filterAcceptsRow(childIndex.row(), index); + if (accept) return true; + + childIndex = index.child(++i, 0); + } + return false; + } + + return (sourceModel()->data(index).toString().contains(filterRegExp())); +} + +bool ModuleSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + QVariant l = (left.model() ? left.model()->data(left, this->sortRole()) : QVariant()); + QVariant r = (right.model() ? right.model()->data(right, this->sortRole()) : QVariant()); + return l.toString().compare(r.toString(), this->sortCaseSensitivity()) > 0; +} + + +ModuleSortFilterProxyModel::ModuleSortFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.h new file mode 100644 index 0000000000..ac34b80d32 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerTreeWidget.h @@ -0,0 +1,103 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEEXPLORERTREEWIDGET_H +#define CTKCMDLINEMODULEEXPLORERTREEWIDGET_H + +#include + +#include +#include + +class ctkCmdLineModuleFrontend; +struct ctkCmdLineModuleFrontendFactory; +class ctkCmdLineModuleExplorerShowXmlAction; + +class QStandardItem; +class QStandardItemModel; + +class ModuleSortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + + ModuleSortFilterProxyModel(QObject *parent = 0); + +protected: + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; +}; + +/** + * \class ctkCmdLineModuleExplorerTreeWidget + * \brief Example application tree widget. + */ +class ctkCmdLineModuleExplorerTreeWidget : public QTreeView +{ + Q_OBJECT + +public: + + explicit ctkCmdLineModuleExplorerTreeWidget(QWidget* parent = 0); + + void setModuleFrontendFactories(const QList& frontendFactories, ctkCmdLineModuleFrontendFactory *defaultFactory); + + Q_SLOT void addModuleItem(const ctkCmdLineModuleReference& moduleRef); + Q_SLOT void removeModuleItem(const ctkCmdLineModuleReference& moduleRef); + + Q_SIGNAL void moduleFrontendCreated(ctkCmdLineModuleFrontend* moduleFrontend); + + Q_SLOT void setFilter(const QString& filter); + +protected: + + void contextMenuEvent(QContextMenuEvent* event); + +private: + + ctkCmdLineModuleFrontend* createFrontend(const ctkCmdLineModuleReference &moduleRef, + ctkCmdLineModuleFrontendFactory* frontendFactory); + + Q_SLOT void moduleDoubleClicked(const QModelIndex& index); + Q_SLOT void frontendFactoryActionTriggered(); + + static QString CATEGORY_UNKNOWN; + + QMenu* ContextMenu; + QMenu* ShowFrontendMenu; + + ctkCmdLineModuleExplorerShowXmlAction* ShowXmlAction; + + ctkCmdLineModuleReference ContextReference; + QList FrontendFactories; + ctkCmdLineModuleFrontendFactory* DefaultFrontendFactory; + QHash TreeWidgetCategories; + QHash TreeWidgetItems; + QHash ActionsToFrontendFactoryMap; + + QStandardItemModel* TreeModel; + QSortFilterProxyModel* FilterProxyModel; +}; + +#endif // CTKCMDLINEMODULEEXPLORERTREEWIDGET_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.cpp new file mode 100644 index 0000000000..2ed413f0d1 --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.cpp @@ -0,0 +1,68 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleExplorerUtils.h" + +#include +#include +#include +#include +#include + +QPixmap ctkCmdLineModuleExplorerUtils::createIconOverlay(const QPixmap &base, const QPixmap &overlay) +{ + QPixmap result(base.width(), base.height()); + result.fill(Qt::transparent); + QPainter painter(&result); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawPixmap(0, 0, base); + painter.drawPixmap(base.width()/2, base.height()/2, + overlay.scaled(base.width()/2, base.height()/2, Qt::KeepAspectRatio)); + return result; +} + +void ctkCmdLineModuleExplorerUtils:: messageBoxModuleRegistration(const QStringList& modulePaths, + const QList& moduleRefs, + ctkCmdLineModuleManager::ValidationMode validationMode) +{ + Q_ASSERT(modulePaths.size() == moduleRefs.size()); + + QString errorMsg; + for(int i = 0; i < modulePaths.size(); ++i) + { + if (!moduleRefs.at(i)) + { + errorMsg += QObject::tr("Failed to register ") + modulePaths.at(i) + "\n\n"; + } + else if (!moduleRefs.at(i).xmlValidationErrorString().isEmpty() && + validationMode == ctkCmdLineModuleManager::STRICT_VALIDATION) + { + errorMsg += QObject::tr("Failed to register ") + modulePaths.at(i) + ":\n" + moduleRefs.at(i).xmlValidationErrorString() + "\n\n"; + } + } + + if (!errorMsg.isEmpty()) + { + QWidget* widget = QApplication::activeModalWidget(); + if (widget == NULL) widget = QApplication::activeWindow(); + QMessageBox::critical(widget, QObject::tr("Failed to register modules"), errorMsg); + } +} diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.h b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.h new file mode 100644 index 0000000000..8c1dc3dacd --- /dev/null +++ b/Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.h @@ -0,0 +1,40 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCOMMANDLINEMODULEEXPLORERUTILS_H +#define CTKCOMMANDLINEMODULEEXPLORERUTILS_H + +#include "ctkCmdLineModuleManager.h" + +#include + +struct ctkCmdLineModuleExplorerUtils +{ + + static QPixmap createIconOverlay(const QPixmap& base, const QPixmap& overlay); + + static void messageBoxModuleRegistration(const QStringList& modulePaths, + const QList& moduleRefs, + ctkCmdLineModuleManager::ValidationMode validationMode); + +}; + +#endif // CTKCOMMANDLINEMODULEEXPLORERUTILS_H diff --git a/Applications/ctkCommandLineModuleExplorer/ctkCommandLineModuleExplorerMain.cpp b/Applications/ctkCommandLineModuleExplorer/ctkCommandLineModuleExplorerMain.cpp index f41b785deb..5a263a85d6 100644 --- a/Applications/ctkCommandLineModuleExplorer/ctkCommandLineModuleExplorerMain.cpp +++ b/Applications/ctkCommandLineModuleExplorer/ctkCommandLineModuleExplorerMain.cpp @@ -36,11 +36,13 @@ #include #include -#include "ctkCLModuleExplorerMainWindow.h" +#include "ctkCmdLineModuleExplorerMainWindow.h" int main(int argc, char** argv) { QApplication myApp(argc, argv); + myApp.setOrganizationName("CommonTK"); + myApp.setApplicationName("CommandLineModuleExplorer"); ctkCommandLineParser cmdLineParser; cmdLineParser.setArgumentPrefix("--", "-"); diff --git a/Applications/ctkCommandLineModuleExplorer/resources/clear.png b/Applications/ctkCommandLineModuleExplorer/resources/clear.png new file mode 100644 index 0000000000..7132b26959 Binary files /dev/null and b/Applications/ctkCommandLineModuleExplorer/resources/clear.png differ diff --git a/Applications/ctkCommandLineModuleExplorer/resources/ctkCLModuleExplorer.qrc b/Applications/ctkCommandLineModuleExplorer/resources/ctkCmdLineModuleExplorer.qrc similarity index 66% rename from Applications/ctkCommandLineModuleExplorer/resources/ctkCLModuleExplorer.qrc rename to Applications/ctkCommandLineModuleExplorer/resources/ctkCmdLineModuleExplorer.qrc index 29b29ad4aa..84c0d88a04 100644 --- a/Applications/ctkCommandLineModuleExplorer/resources/ctkCLModuleExplorer.qrc +++ b/Applications/ctkCommandLineModuleExplorer/resources/ctkCmdLineModuleExplorer.qrc @@ -2,5 +2,7 @@ run.png stop.png + pause.png + clear.png diff --git a/Applications/ctkCommandLineModuleExplorer/resources/pause.png b/Applications/ctkCommandLineModuleExplorer/resources/pause.png new file mode 100644 index 0000000000..a63d34e17e Binary files /dev/null and b/Applications/ctkCommandLineModuleExplorer/resources/pause.png differ diff --git a/Applications/ctkCommandLineModuleExplorer/target_libraries.cmake b/Applications/ctkCommandLineModuleExplorer/target_libraries.cmake index 4dbe277fae..d00a0e283d 100644 --- a/Applications/ctkCommandLineModuleExplorer/target_libraries.cmake +++ b/Applications/ctkCommandLineModuleExplorer/target_libraries.cmake @@ -5,5 +5,9 @@ # set(target_libraries - CTKCommandLineModulesQtGui + CTKCommandLineModulesFrontendQtGui + CTKCommandLineModulesFrontendQtWebKit + CTKCommandLineModulesBackendLocalProcess + CTKCommandLineModulesBackendFunctionPointer + CTKWidgets ) diff --git a/CMake/CTKConfig.cmake.in b/CMake/CTKConfig.cmake.in index 3c5171f038..5e0488c667 100644 --- a/CMake/CTKConfig.cmake.in +++ b/CMake/CTKConfig.cmake.in @@ -70,7 +70,7 @@ INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGetAllPluginTargets.cmake") INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGetTargetDependencies.cmake") INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkFunctionGetPluginDependencies.cmake") INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroSetupPlugins.cmake") -INCLUDE("@CTKTesting_CMAKE_DIR_CONFIG@/ctkMacroGenerateMocs.cmake") +INCLUDE("@CTK_CMAKE_DIR_CONFIG@/ctkMacroGenerateMocs.cmake") SET(CTK_EXPORT_HEADER_TEMPLATE "@CTK_EXPORT_HEADER_TEMPLATE@") @@ -154,6 +154,14 @@ SET(CTK_USE_FILE "@CTK_USE_FILE@") SET(CTK_CMAKE_DIR "@CTK_CMAKE_DIR_CONFIG@") SET(CTK_CMAKE_UTILITIES_DIR "@CTK_CMAKE_UTILITIES_DIR_CONFIG@") +# Update CMake module path so that calling "find_package(DCMTK)" works as expected +# after calling "find_package(CTK)" +# Ideally projects like DCMTK or PythonQt should provide both "Config" and "Use" files. +set(CMAKE_MODULE_PATH + ${CTK_CMAKE_UTILITIES_DIR} + ${CMAKE_MODULE_PATH} + ) + # TODO The list of available libraries. # TODO The list of available plugins. diff --git a/CMake/ctkMacroBuildQtPlugin.cmake b/CMake/ctkMacroBuildQtPlugin.cmake index 254c3250dc..a8d61243a1 100644 --- a/CMake/ctkMacroBuildQtPlugin.cmake +++ b/CMake/ctkMacroBuildQtPlugin.cmake @@ -127,7 +127,7 @@ macro(ctkMacroBuildQtPlugin) # CTK_INSTALL_QTPLUGIN_DIR:STRING can be passed when configuring CTK # By default, it is the same path as CTK_INSTALL_LIB_DIR # Plugins are installed in a subdirectory corresponding to their types (e.g. designer, iconengines, imageformats...) - if (NOT DEFINED CTK_INSTALL_QTPLUGIN_DIR) + if (NOT CTK_INSTALL_QTPLUGIN_DIR) set(CTK_INSTALL_QTPLUGIN_DIR "${CTK_INSTALL_LIB_DIR}") endif() install(TARGETS ${lib_name} diff --git a/CMake/ctkMacroGenerateMocs.cmake b/CMake/ctkMacroGenerateMocs.cmake new file mode 100644 index 0000000000..3b73590b4c --- /dev/null +++ b/CMake/ctkMacroGenerateMocs.cmake @@ -0,0 +1,21 @@ + +# QT4_GENERATE_MOCS(inputfile1 [inputfile2 ...]) + +macro(QT4_GENERATE_MOCS) + foreach(file ${ARGN}) + set(moc_file moc_${file}) + QT4_GENERATE_MOC(${file} ${moc_file}) + + get_filename_component(source_name ${file} NAME_WE) + get_filename_component(source_ext ${file} EXT) + if(${source_ext} MATCHES "\\.[hH]") + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${source_name}.cpp) + set(source_ext .cpp) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${source_name}.cxx) + set(source_ext .cxx) + endif() + endif() + set_property(SOURCE ${source_name}${source_ext} APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${moc_file}) + endforeach() +endmacro() + diff --git a/CMake/ctkMacroOptionUtils.cmake b/CMake/ctkMacroOptionUtils.cmake index 7c46d0ad08..16a1052c9a 100644 --- a/CMake/ctkMacroOptionUtils.cmake +++ b/CMake/ctkMacroOptionUtils.cmake @@ -37,14 +37,23 @@ endmacro() macro(ctk_lib_option name doc default) ctk_option(CTK_LIB ${name} ${doc} ${default} ${ARGN}) + if(CTK_BUILD_ALL_LIBRARIES) + set(CTK_LIB_${name} 1) + endif() endmacro() macro(ctk_plugin_option name doc default) ctk_option(CTK_PLUGIN ${name} ${doc} ${default} ${ARGN}) + if(CTK_BUILD_ALL_PLUGINS) + set(CTK_PLUGIN_${name} 1) + endif() endmacro() macro(ctk_app_option name doc default) ctk_option(CTK_APP ${name} ${doc} ${default} ${ARGN}) + if(CTK_BUILD_ALL_APPS) + set(CTK_APP_${name} 1) + endif() endmacro() macro(ctk_enable_option_raw name doc default) diff --git a/CMake/ctkMacroSetupPlugins.cmake b/CMake/ctkMacroSetupPlugins.cmake index e19ec71e75..ddd314e21f 100644 --- a/CMake/ctkMacroSetupPlugins.cmake +++ b/CMake/ctkMacroSetupPlugins.cmake @@ -118,35 +118,56 @@ macro(ctkMacroSetupPlugins ) get_filename_component(QT_INSTALLED_LIBRARY_DIR ${QT_QMAKE_EXECUTABLE} PATH) endif() + set(plugin_symbolic_names ) + set(plugin_targets ) + foreach(plugin ${plugin_list}) + ctkFunctionExtractOptionNameAndValue(${plugin} plugin_dir plugin_value) + string(REPLACE "/" ";" _tokens ${plugin_dir}) + list(GET _tokens -1 plugin_symbolic_name) + list(APPEND plugin_symbolic_names ${plugin_symbolic_name}) + + string(REPLACE "." "_" plugin_target ${plugin_symbolic_name}) + list(APPEND plugin_targets ${plugin_target}) + + set(${plugin_symbolic_name}_plugin_dir ${plugin_dir}) + set(${plugin_symbolic_name}_plugin_value ${plugin_value}) + endforeach() + + # Check if the plugin symbolic names are valid for the current project + ctkMacroGetAllNonProjectTargetLibraries("${plugin_targets}" invalid_targets) + if(invalid_targets) + set(invalid_plugins ) + foreach(plugin_target ${invalid_targets}) + string(REPLACE "_" "." plugin_symbolic_name ${plugin_target}) + list(APPEND invalid_plugins ${plugin_symbolic_name}) + endforeach() + message(FATAL_ERROR "The following plug-ins are using invalid symbolic names: ${invalid_plugins}") + endif() + set(plugin_dirswithoption ) set(plugin_subdirs ) - foreach(plugin ${plugin_list}) - ctkFunctionExtractOptionNameAndValue(${plugin} plugin_name plugin_value) + foreach(plugin_symbolic_name ${plugin_symbolic_names}) if(MY_COMPACT_OPTIONS) - string(REPLACE "/" ";" _tokens ${plugin_name}) - list(GET _tokens -1 option_name) - set(option_name ${MY_BUILD_OPTION_PREFIX}${option_name}) + set(option_name ${MY_BUILD_OPTION_PREFIX}${plugin_symbolic_name}) else() - set(option_name ${MY_BUILD_OPTION_PREFIX}${plugin_name}) + set(option_name ${MY_BUILD_OPTION_PREFIX}${${plugin_symbolic_name}_plugin_dir}) endif() # This variable may have the form "Plugins/org.commontk.bla_option_name" - set(${plugin_name}_option_name ${option_name}) + set(${${plugin_symbolic_name}_plugin_dir}_option_name ${option_name}) # Additionally create a variable of the form "org_commontk_bla_option_name" - string(REPLACE "/" ";" _tokens ${plugin_name}) - list(GET _tokens -1 plugin_symbolic_name) string(REPLACE "." "_" plugin_target ${plugin_symbolic_name}) set(${plugin_target}_option_name ${option_name}) - option(${option_name} "Build ${plugin_name} Plugin." ${plugin_value}) + option(${option_name} "Build the ${plugin_symbolic_name} Plugin." ${${plugin_symbolic_name}_plugin_value}) if(MY_BUILD_ALL) set(${option_name} 1) endif() - list(APPEND plugin_subdirs "${plugin_name}") - if(IS_ABSOLUTE ${plugin_name}) - list(APPEND plugin_dirswithoption "${plugin_name}^^${option_name}") + list(APPEND plugin_subdirs "${${plugin_symbolic_name}_plugin_dir}") + if(IS_ABSOLUTE ${${plugin_symbolic_name}_plugin_dir}) + list(APPEND plugin_dirswithoption "${${plugin_symbolic_name}_plugin_dir}^^${option_name}") else() - list(APPEND plugin_dirswithoption "${CMAKE_CURRENT_SOURCE_DIR}/${plugin_name}^^${option_name}") + list(APPEND plugin_dirswithoption "${CMAKE_CURRENT_SOURCE_DIR}/${${plugin_symbolic_name}_plugin_dir}^^${option_name}") endif() endforeach() diff --git a/CMakeExternals/DCMTK.cmake b/CMakeExternals/DCMTK.cmake index 5b1d6ff17c..55dc9326c1 100644 --- a/CMakeExternals/DCMTK.cmake +++ b/CMakeExternals/DCMTK.cmake @@ -48,6 +48,9 @@ if(${add_project}) CMAKE_GENERATOR ${gen} UPDATE_COMMAND "" BUILD_COMMAND "" + CMAKE_ARGS + -DDCMTK_INSTALL_BINDIR:STRING=bin/${CMAKE_CFG_INTDIR} + -DDCMTK_INSTALL_LIBDIR:STRING=lib/${CMAKE_CFG_INTDIR} CMAKE_CACHE_ARGS ${ep_common_cache_args} -DBUILD_SHARED_LIBS:BOOL=OFF diff --git a/CMakeExternals/PythonQt.cmake b/CMakeExternals/PythonQt.cmake index 373e8230c5..d5fa33b9db 100644 --- a/CMakeExternals/PythonQt.cmake +++ b/CMakeExternals/PythonQt.cmake @@ -53,7 +53,7 @@ if(${add_project}) message(FATAL_ERROR "error: Python is required to build ${PROJECT_NAME}") endif() - set(revision_tag 47738f9c8c5d3ffa77c8f2e1844f899e5b548f0c) + set(revision_tag 6366f002a93aa238c55f58de949d09c552cda5a9) if(${proj}_REVISION_TAG) set(revision_tag ${${proj}_REVISION_TAG}) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index c7d5e5fcd8..f4cac82a2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,7 @@ include(CMake/ctkMacroBuildPlugin.cmake) include(CMake/ctkMacroBuildApp.cmake) include(CMake/ctkMacroBuildQtPlugin.cmake) include(CMake/ctkMacroCompilePythonScript.cmake) +include(CMake/ctkMacroGenerateMocs.cmake) include(CMake/ctkMacroWrapPythonQt.cmake) include(CMake/ctkMacroSetupQt.cmake) include(CMake/ctkMacroTargetLibraries.cmake) # Import multiple macros @@ -369,6 +370,26 @@ ctk_enable_option(Widgets "Enable Qt Widget libraries" OFF ctk_enable_option(PluginFramework "Enable Plugin Framework" OFF CTK_LIB_PluginFramework) +#----------------------------------------------------------------------------- +# Special "BUILD ALL" options + +# Build all CTK plug-ins +option(CTK_BUILD_ALL_PLUGINS "Build all CTK plug-ins" OFF) + +# Build all CTK libraries +option(CTK_BUILD_ALL_LIBRARIES "Build all CTK libraries" OFF) + +# Build all CTK applications +option(CTK_BUILD_ALL_APPS "Build all CTK applications" OFF) + +# Build everything +option(CTK_BUILD_ALL "Build everything in CTK" OFF) +if(CTK_BUILD_ALL) + set(CTK_BUILD_ALL_PLUGINS 1) + set(CTK_BUILD_ALL_LIBRARIES 1) + set(CTK_BUILD_ALL_APPS 1) +endif() + #----------------------------------------------------------------------------- # Other options @@ -451,9 +472,18 @@ ctk_lib_option(Visualization/VTK/Widgets ctk_lib_option(CommandLineModules/Core "Build the Command Line Module core library" OFF) + +ctk_lib_option(CommandLineModules/Frontend/QtWebKit + "Build the QtWebKit based Command Line Module front-end" OFF) -ctk_lib_option(CommandLineModules/QtGui - "Build the QtGui based Command Line Module library" OFF) +ctk_lib_option(CommandLineModules/Frontend/QtGui + "Build the QtGui based Command Line Module front-end" OFF) + +ctk_lib_option(CommandLineModules/Backend/LocalProcess + "Build the Command Line Module back-end for local processes" OFF) + +ctk_lib_option(CommandLineModules/Backend/FunctionPointer + "Build the Command Line Module back-end for function pointers" OFF) #ctk_lib_option(Visualization/XIP # "Build the XIP library" OFF) @@ -513,7 +543,22 @@ ctk_app_option(ctkEventBusDemo ctk_app_option(ctkCommandLineModuleExplorer "Build the Command Line Module Explorer" OFF CTK_BUILD_EXAMPLES) - + +# We use the CTKWidgets library together with the Qt Designer plug-in +# in ctkCommandLineModuleExplorer, so enabling the options here. +# (We do not need to link them into the executable, hence no entries +# in target_libraries.cmake) + +if(CTK_APP_ctkCommandLineModuleExplorer) + foreach(_option CTK_BUILD_QTDESIGNER_PLUGINS) + if(NOT ${_option}) + get_property(_docstring CACHE ${_option} PROPERTY HELPSTRING) + set(${_option} ON CACHE BOOL "${_docstring}" FORCE) + message("Enabling option [${_option}] required by [ctkCommandLineModuleExplorer]") + endif() + endforeach() +endif() + ctk_app_option(ctkPluginBrowser "Build the DICOM example application" OFF CTK_ENABLE_PluginFramework AND CTK_BUILD_EXAMPLES) @@ -557,9 +602,6 @@ set(plugin_list org.commontk.log org.commontk.log4qt org.commontk.metatype - - # Misc - #org.commontk.slicermodule ) foreach(_plugin ${plugin_list}) @@ -858,11 +900,18 @@ add_subdirectory(Libs/Testing) # Add CTK library subdirectories # foreach(lib ${CTK_LIBS}) - IF (CTK_LIB_${lib}) + if(CTK_LIB_${lib}) add_subdirectory(Libs/${lib}) endif() + # Always add the Documentation sub-directory for additional processing + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Libs/${lib}/Documentation/CMakeLists.txt) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Libs/${lib}/Documentation) + endif() endforeach() +# Add the CommandLineModules root directory for additional processing +add_subdirectory(Libs/CommandLineModules) + #----------------------------------------------------------------------------- if(CTK_USE_QTTESTING) add_subdirectory(Libs/QtTesting) @@ -897,6 +946,7 @@ endif() #add_subdirectory(Testing) #add_subdirectory(Examples) + #----------------------------------------------------------------------------- # Style Checking configuration # diff --git a/Documentation/Doxyfile.txt.in b/Documentation/Doxyfile.txt.in index 1a1b117e6d..f928304c6a 100644 --- a/Documentation/Doxyfile.txt.in +++ b/Documentation/Doxyfile.txt.in @@ -1,4 +1,4 @@ -# Doxyfile 1.7.3 +# Doxyfile 1.8.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -22,8 +22,9 @@ DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. PROJECT_NAME = @PROJECT_NAME@ @@ -33,7 +34,9 @@ PROJECT_NAME = @PROJECT_NAME@ PROJECT_NUMBER = @CTK_VERSION@ -# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short. +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "The Common Toolkit is a community effort to provide support code for medical image analysis, surgical navigation, and related projects." @@ -133,7 +136,9 @@ FULL_PATH_NAMES = YES # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the -# path to strip. +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. STRIP_FROM_PATH = @CTK_SOURCE_DIR@ @@ -202,6 +207,13 @@ TAB_SIZE = 2 ALIASES = +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list @@ -229,17 +241,34 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. EXTENSION_MAPPING = +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and @@ -260,12 +289,7 @@ CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES @@ -284,6 +308,22 @@ DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct @@ -306,10 +346,21 @@ TYPEDEF_HIDES_STRUCT = NO # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -326,6 +377,11 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = @CTK_USER_DOCUMENTATION@ +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. @@ -459,8 +515,11 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO @@ -510,12 +569,6 @@ MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = YES - # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. @@ -541,13 +594,23 @@ FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file +# output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- @@ -670,6 +733,7 @@ FILE_PATTERNS = @CTK_ADDITIONAL_FILE_PATTERN@ \ *.patch \ *.xpm \ *.dox \ + *.md \ */CMake/*.cmake # The RECURSIVE tag can be used to turn specify whether or not subdirectories @@ -678,9 +742,11 @@ FILE_PATTERNS = @CTK_ADDITIONAL_FILE_PATTERN@ \ RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = @CTK_BINARY_DIR@ \ @CTK_SOURCE_DIR@/Utilities \ @@ -688,7 +754,7 @@ EXCLUDE = @CTK_BINARY_DIR@ \ @CTK_SOURCE_DIR@/CMake/ctkDashboardScript.TEMPLATE.cmake \ @CTK_SOURCE_DIR@/Libs/Visualization/XIP -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. @@ -721,7 +787,8 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = @CTK_SOURCE_DIR@/Libs/Core/Documentation/Snippets/ \ +EXAMPLE_PATH = @CTK_SOURCE_DIR@/Libs/CommandLineModules/Documentation/Snippets/ \ + @CTK_SOURCE_DIR@/Libs/Core/Documentation/Snippets/ \ @CTK_SOURCE_DIR@/Libs/PluginFramework/Documentation/Snippets/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the @@ -798,7 +865,7 @@ INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES @@ -882,7 +949,14 @@ HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a -# standard header. +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = @@ -894,15 +968,34 @@ HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. HTML_STYLESHEET = +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images +# Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, @@ -932,20 +1025,23 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# page has loaded. HTML_DYNAMIC_SECTIONS = NO +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). @@ -973,9 +1069,9 @@ DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher @@ -1097,18 +1193,14 @@ GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [0,1..20]) -# that doxygen will group on one line in the generated HTML documentation. -# Note that a value of 0 will completely suppress the enum values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated @@ -1116,13 +1208,17 @@ ENUM_VALUES_PER_LINE = 4 # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NONE -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. -USE_INLINE_TREES = NO +ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree @@ -1155,7 +1251,7 @@ FORMULA_TRANSPARENT = YES # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and +# output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO @@ -1164,12 +1260,19 @@ USE_MATHJAX = NO # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using @@ -1243,6 +1346,13 @@ EXTRA_PACKAGES = LATEX_HEADER = +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references @@ -1276,6 +1386,12 @@ LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1307,7 +1423,7 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. @@ -1452,7 +1568,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. +# pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES @@ -1482,7 +1598,8 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. EXPAND_AS_DEFINED = @@ -1497,22 +1614,18 @@ SKIP_FUNCTION_MACROS = YES # Configuration::additions related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. TAGFILES = @@ -1578,17 +1691,14 @@ HAVE_DOT = YES # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. -# Note: Specify 1 to avoid crash with doxygen 1.7.3 +DOT_NUM_THREADS = 1 -DOT_NUM_THREADS = 1 - -# By default doxygen will write a font called Helvetica to the output -# directory and reference it in all dot files that doxygen generates. -# When you want a differently looking font you can specify the font name -# using DOT_FONTNAME. You need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. DOT_FONTNAME = FreeSans @@ -1597,17 +1707,16 @@ DOT_FONTNAME = FreeSans DOT_FONTSIZE = 10 -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. +# CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES @@ -1629,6 +1738,15 @@ GROUP_GRAPHS = YES UML_LOOK = NO +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. @@ -1669,7 +1787,7 @@ CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. @@ -1677,11 +1795,22 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, svg, gif or svg. -# If left blank png will be used. +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/CMakeLists.txt b/Libs/CommandLineModules/Backend/FunctionPointer/CMakeLists.txt new file mode 100644 index 0000000000..c681404cef --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/CMakeLists.txt @@ -0,0 +1,67 @@ +project(CTKCommandLineModulesBackendFunctionPointer) + +# +# 3rd party dependencies +# + +# +# See CTK/CMake/ctkMacroBuildLib.cmake for details +# + +set(KIT_export_directive "CTK_CMDLINEMODULEBACKENDFP_EXPORT") + +# Additional directories to include + +# Source files +set(KIT_SRCS + ctkCmdLineModuleBackendFPTypeTraits.h + ctkCmdLineModuleBackendFPUtil.cpp + ctkCmdLineModuleBackendFPUtil_p.h + ctkCmdLineModuleBackendFunctionPointer.cpp + ctkCmdLineModuleBackendFPDescriptionPrivate.cpp + ctkCmdLineModuleFunctionPointerTask.cpp + ctkCmdLineModuleFunctionPointerTask_p.h +) + +# Headers that should run through moc +set(KIT_MOC_SRCS +) + +# UI files +set(KIT_UI_FORMS +) + +# Resources +set(KIT_resources +) + +# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake +# The following macro will read the target libraries from the file 'target_libraries.cmake' +ctkFunctionGetTargetLibraries(KIT_target_libraries) + +ctkMacroBuildLib( + NAME ${PROJECT_NAME} + EXPORT_DIRECTIVE ${KIT_export_directive} + INCLUDE_DIRECTORIES ${KIT_include_directories} + SRCS ${KIT_SRCS} + MOC_SRCS ${KIT_MOC_SRCS} + UI_FORMS ${KIT_UI_FORMS} + TARGET_LIBRARIES ${KIT_target_libraries} + RESOURCES ${KIT_resources} + LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) + +target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES}) + +if(CTK_WRAP_PYTHONQT_FULL OR CTK_WRAP_PYTHONQT_LIGHT) + ctkMacroBuildLibWrapper( + TARGET ${PROJECT_NAME} + SRCS ${KIT_SRCS} + WRAPPER_LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) +endif() + +# Testing +if(BUILD_TESTING) + #add_subdirectory(Testing) +endif() diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/README.md b/Libs/CommandLineModules/Backend/FunctionPointer/README.md new file mode 100644 index 0000000000..fcd185fb46 --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/README.md @@ -0,0 +1,16 @@ +Function Pointer (experimental) {#CommandLineModulesBackendFunctionPointer_Page} +=============================== + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModulesBackendFunctionPointer_Page.html) form. \endinternal + +The Function Pointer back-end is an experimental *proof-of-concept* (however, it is used in +some unit tests as a light-weight back-end). + +This back-end allows the registration of C/C++ function pointers and uses template magic +to auto-generate an XML description using the type information in the function signature. +Many template specializations for certain parameter types are missing, hence this back-end +will only work for a limited set of argument types. See the ctkCmdLineModuleBackendFunctionPointer +class for more information. + +See the \ref CommandLineModulesBackendFunctionPointer_API module for the API documentation. diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.cpp b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.cpp new file mode 100644 index 0000000000..6050e7b4cc --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleBackendFPDescriptionPrivate.h" + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::DescriptionPrivate::xmlDescription() const +{ + QString xml; + QTextStream str(&xml); + str << "\n"; + str << "\n"; + str << " " + ModuleCategory + "\n"; + str << " " + ModuleTitle + "\n"; + str << " " + ModuleDescription + "\n"; + str << " " + ModuleContributor + "\n"; + str << " \n"; + str << " \n"; + str << " Parameters for calling a function pointer.\n"; + foreach (QString param, paramDescriptions) + { + str << param; + } + str << " \n"; + str << "\n"; + + return xml; +} diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.h b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.h new file mode 100644 index 0000000000..ce9fe0c178 --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPDescriptionPrivate.h @@ -0,0 +1,47 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKENDFUNCTIONPOINTERDESCRIPTIONPRIVATE_H +#define CTKCMDLINEMODULEBACKENDFUNCTIONPOINTERDESCRIPTIONPRIVATE_H + +#include "ctkCmdLineModuleBackendFunctionPointer.h" + +#include "ctkCmdLineModuleBackendFPUtil_p.h" + +class ctkCmdLineModuleBackendFunctionPointer::DescriptionPrivate : public QSharedData +{ +public: + + QString xmlDescription() const; + + QUrl ModuleLocation; + QString ModuleCategory; + QString ModuleTitle; + QString ModuleDescription; + QString ModuleVersion; + QString ModuleContributor; + + ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy FpProxy; + + QList paramDescriptions; +}; + +#endif // CTKCMDLINEMODULEBACKENDFUNCTIONPOINTERDESCRIPTIONPRIVATE_H diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPTypeTraits.h b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPTypeTraits.h new file mode 100644 index 0000000000..dd779e6e4b --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPTypeTraits.h @@ -0,0 +1,125 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKENDFPTYPETRAITS_H +#define CTKCMDLINEMODULEBACKENDFPTYPETRAITS_H + +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +struct NullType {}; + +template +struct Select +{ + typedef T Result; +}; + +template +struct Select +{ + typedef U Result; +}; + +template +class TypeTraits +{ +private: + + template struct PointerTraits + { + enum { result = false }; + typedef NullType PointeeType; + }; + template struct PointerTraits + { + enum { result = true }; + typedef U PointeeType; + }; + template struct ReferenceTraits + { + enum { result = false }; + typedef NullType ReferenceType; + }; + template struct ReferenceTraits + { + enum { result = true }; + typedef U ReferenceType; + }; + + template struct UnConst + { + typedef U Result; + }; + template struct UnConst + { + typedef U Result; + }; + +public: + + typedef typename PointerTraits::PointeeType PointeeType; + typedef typename ReferenceTraits::ReferenceType ReferenceType; + + enum { isPointer = PointerTraits::result }; + enum { isReference = ReferenceTraits::result }; + + typedef typename Select::Result, + typename Select::Result, typename UnConst::Result>::Result >::Result RawType; +}; + +template +struct EnableIf +{ + typedef T Type; +}; + +template +struct EnableIf {}; + +template +struct IsSame +{ + static bool const value = false; +}; + +template +struct IsSame +{ + static bool const value = true; +}; + +template +struct IsBaseOf +{ + static D* MakeD(); + static char (& Test(B*))[1]; + static char (& Test(...))[2]; + static bool const value = sizeof Test(MakeD()) == 1 && + !IsSame::value; +}; + + +} +} + + +#endif // CTKCMDLINEMODULEBACKENDFPTYPETRAITS_H diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil.cpp b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil.cpp new file mode 100644 index 0000000000..204e5b0f4d --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil.cpp @@ -0,0 +1,65 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleBackendFPUtil_p.h" + + +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +//---------------------------------------------------------------------------- +FunctionPointerHolderBase::~FunctionPointerHolderBase() +{ +} + +//---------------------------------------------------------------------------- +FunctionPointerProxy::FunctionPointerProxy() + : FpHolder(NULL) +{} + +//---------------------------------------------------------------------------- +FunctionPointerProxy::~FunctionPointerProxy() +{ + delete FpHolder; +} + +//---------------------------------------------------------------------------- +FunctionPointerProxy::FunctionPointerProxy(const FunctionPointerProxy& other) + : FpHolder(other.FpHolder->clone()) +{ +} + +//---------------------------------------------------------------------------- +FunctionPointerProxy& FunctionPointerProxy::operator=(const FunctionPointerProxy& other) +{ + delete this->FpHolder; + this->FpHolder = other.FpHolder->clone(); + return *this; +} + +//---------------------------------------------------------------------------- +void FunctionPointerProxy::call(const QList &args) +{ + FpHolder->call(args); +} + +} +} diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil_p.h b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil_p.h new file mode 100644 index 0000000000..f0d20e685a --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFPUtil_p.h @@ -0,0 +1,117 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKENDFPUTIL_P_H +#define CTKCMDLINEMODULEBACKENDFPUTIL_P_H + +#include "ctkCommandLineModulesBackendFunctionPointerExport.h" + +#include + +class ctkCmdLineModuleBackendFunctionPointer; + +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +struct CTK_CMDLINEMODULEBACKENDFP_EXPORT FunctionPointerHolderBase +{ + virtual ~FunctionPointerHolderBase(); + + virtual FunctionPointerHolderBase* clone() const = 0; + + virtual void call(const QList& args) = 0; +}; + + +template +struct FunctionPointerHolder : public FunctionPointerHolderBase +{ + typedef void (*FunctionPointerType)(A); + + FunctionPointerHolder(FunctionPointerType fp) : Fp(fp) {} + + FunctionPointerHolderBase* clone() const + { + return new FunctionPointerHolder(*this); + } + + void call(const QList& args) + { + Q_ASSERT(args.size() > 0); + Q_ASSERT(args.at(0).canConvert()); + Fp(args.at(0).value()); + } + + FunctionPointerType Fp; +}; + +template +struct FunctionPointerHolder2 : public FunctionPointerHolderBase +{ + typedef void (*FunctionPointerType)(A,B); + + FunctionPointerHolder2(FunctionPointerType fp) : Fp(fp) {} + + FunctionPointerHolderBase* clone() const + { + return new FunctionPointerHolder2(*this); + } + + void call(const QList& args) + { + Q_ASSERT(args.size() > 1); + Q_ASSERT(args.at(0).canConvert()); + Q_ASSERT(args.at(1).canConvert()); + Fp(args.at(0).value(), args.at(1).value()); + } + + FunctionPointerType Fp; +}; + +struct CTK_CMDLINEMODULEBACKENDFP_EXPORT FunctionPointerProxy +{ + FunctionPointerProxy(); + ~FunctionPointerProxy(); + + FunctionPointerProxy(const FunctionPointerProxy& other); + FunctionPointerProxy& operator=(const FunctionPointerProxy& other); + + template + FunctionPointerProxy(void (*fp)(A)) + : FpHolder(new FunctionPointerHolder(fp)) {} + + template + FunctionPointerProxy(void (*fp)(A,B)) + : FpHolder(new FunctionPointerHolder2(fp)) {} + + void call(const QList& args); + +private: + + friend class ::ctkCmdLineModuleBackendFunctionPointer; + + FunctionPointerHolderBase* FpHolder; +}; + +} +} + +#endif // CTKCMDLINEMODULEBACKENDFPUTIL_P_H diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.cpp b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.cpp new file mode 100644 index 0000000000..fd54e29fdb --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.cpp @@ -0,0 +1,279 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleBackendFunctionPointer.h" + +#include "ctkCmdLineModuleBackendFPUtil_p.h" +#include "ctkCmdLineModuleBackendFPDescriptionPrivate.h" +#include "ctkCmdLineModuleFunctionPointerTask_p.h" + +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleFrontend.h" + +#include +#include +#include +#include +#include + + +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +extern int qHash(const QUrl& url); +#endif + +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +//---------------------------------------------------------------------------- +template<> +CTK_CMDLINEMODULEBACKENDFP_EXPORT QString GetParameterTypeName() +{ + return "integer"; +} + +//---------------------------------------------------------------------------- +template<> +CTK_CMDLINEMODULEBACKENDFP_EXPORT QString GetParameterTypeName >() +{ + return "integer-vector"; +} + +} +} + +//---------------------------------------------------------------------------- +#ifdef Q_OS_WIN +#include +void sleep_secs(int secs) +{ + Sleep(secs*1000); +} +#else +#include +void sleep_secs(int secs) +{ + struct timespec nanostep; + nanostep.tv_sec = secs; + nanostep.tv_nsec = 0; + nanosleep(&nanostep, NULL); +} +#endif + +void CalculateFibonacciNumbers(int level) //, QList* result) +{ + qDebug() << "Number: 0"; + if (level > 0) + { + sleep_secs(1); + qDebug() << "Number: 1"; + if (level == 1) return; + } + + int first = 0; + int second = 1; + for (int i = 1; i < level; ++i) + { + int tmp = first; + first = second; + second = first + tmp; + sleep_secs(1); + qDebug() << "Number:" << second; + } +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::Description::Description() + : d(new ctkCmdLineModuleBackendFunctionPointer::DescriptionPrivate) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::Description::~Description() +{ +} + +//---------------------------------------------------------------------------- +QUrl ctkCmdLineModuleBackendFunctionPointer::Description::moduleLocation() const +{ + return d->ModuleLocation; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::Description::moduleCategory() const +{ + return d->ModuleCategory; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleBackendFunctionPointer::Description::setModuleCategory(const QString& category) +{ + d->ModuleCategory = category; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::Description::moduleTitle() const +{ + return d->ModuleTitle; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleBackendFunctionPointer::Description::setModuleTitle(const QString &title) +{ + d->ModuleTitle = title; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::Description::moduleDescription() const +{ + return d->ModuleDescription; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleBackendFunctionPointer::Description::setModuleDescription(const QString &description) +{ + d->ModuleDescription = description; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::Description::moduleVersion() const +{ + return d->ModuleVersion; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleBackendFunctionPointer::Description::setModuleVersion(const QString &version) +{ + d->ModuleVersion = version; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::Description::moduleContributor() const +{ + return d->ModuleContributor; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleBackendFunctionPointer::Description::setModuleContributor(const QString &contributor) +{ + d->ModuleContributor = contributor; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::Description::Description(const QUrl &location, + const ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy &fpProxy) + : d(new ctkCmdLineModuleBackendFunctionPointer::DescriptionPrivate) +{ + d->ModuleLocation = location; + d->FpProxy = fpProxy; +} + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleBackendFunctionPointerPrivate +{ + QHash UrlToFpDescription; +}; + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::ctkCmdLineModuleBackendFunctionPointer() + : d(new ctkCmdLineModuleBackendFunctionPointerPrivate) +{ + this->registerFunctionPointer("Fibonacci Number", CalculateFibonacciNumbers, "Count"); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::~ctkCmdLineModuleBackendFunctionPointer() +{ +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::name() const +{ + return "Function Pointer (experimental)"; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendFunctionPointer::description() const +{ + return "Calls a previously registered function pointer."; +} + +//---------------------------------------------------------------------------- +QList ctkCmdLineModuleBackendFunctionPointer::schemes() const +{ + static QList supportedSchemes = QList() << "fp"; + return supportedSchemes; +} + +//---------------------------------------------------------------------------- +qint64 ctkCmdLineModuleBackendFunctionPointer::timeStamp(const QUrl &location) const +{ + Q_UNUSED(location) + return 0; +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleBackendFunctionPointer::rawXmlDescription(const QUrl& location) +{ + if (!d->UrlToFpDescription.contains(location)) return QByteArray(); + //qDebug() << d->UrlToFpDescription[location].d->xmlDescription(); + return QByteArray(qPrintable(d->UrlToFpDescription[location].d->xmlDescription())); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleBackendFunctionPointer::run(ctkCmdLineModuleFrontend *frontend) +{ + QUrl url = frontend->location(); + + const Description& descr = d->UrlToFpDescription[url]; + QList args = this->arguments(frontend); + + // Instances of ctkCmdLineModuleFunctionPointerTask are auto-deleted by the + // thread pool + ctkCmdLineModuleFunctionPointerTask* fpTask = new ctkCmdLineModuleFunctionPointerTask(descr, args); + return fpTask->start(); +} + +//---------------------------------------------------------------------------- +QList ctkCmdLineModuleBackendFunctionPointer::arguments(ctkCmdLineModuleFrontend *frontend) const +{ + return frontend->values().values(); +} + +//---------------------------------------------------------------------------- +QList ctkCmdLineModuleBackendFunctionPointer::registeredFunctionPointers() const +{ + return d->UrlToFpDescription.keys(); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendFunctionPointer::Description* +ctkCmdLineModuleBackendFunctionPointer::registerFunctionPointerProxy(const QString& title, + const ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy& proxy, + const QList& params) +{ + QUrl url(QString("fp://0x%1").arg(reinterpret_cast(proxy.FpHolder))); + d->UrlToFpDescription[url] = Description(url, proxy); + + Description& fpDescr = d->UrlToFpDescription[url]; + fpDescr.setModuleTitle(title); + fpDescr.d->paramDescriptions = params; + return &fpDescr; +} diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.h b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.h new file mode 100644 index 0000000000..b2189a15bc --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.h @@ -0,0 +1,209 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKENDFUNCTIONPOINTER_H +#define CTKCMDLINEMODULEBACKENDFUNCTIONPOINTER_H + +#include "ctkCmdLineModuleBackend.h" + +#include "ctkCommandLineModulesBackendFunctionPointerExport.h" +#include "ctkCmdLineModuleBackendFPTypeTraits.h" +#include "ctkCmdLineModuleBackendFPUtil_p.h" + +#include +#include +#include +#include +#include + +#include + + +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +struct FunctionPointerProxy; + +template +QString GetParameterTypeName(); + +struct ImageType {}; + +// default parameter description +template +struct CreateXmlFor +{ + static QString parameter(int index, const QString& typeName, const QString& label = QString(), const QString& description = QString()) + { + QString xmlParameter; + QTextStream str(&xmlParameter); + str << " <" << typeName << ">\n"; + str << " " << QString("param%1").arg(index) << "\n"; + str << " " << index << "\n"; + str << " " << (description.isEmpty() ? "Description not available." : description) << "\n"; + str << " \n"; + str << " \n"; + return xmlParameter; + } +}; + +// specialization for input image types +template +struct CreateXmlFor::value >::Type > +{ + static QString parameter(int index, const QString& typeName, const QString& label = QString(), const QString& description = QString()) + { + QString xmlParameter; + QTextStream str(&xmlParameter); + str << " <" << typeName << ">\n"; + str << " " << QString("param%1").arg(index) << "\n"; + str << " " << index << "\n"; + str << " " << (description.isEmpty() ? "Description not available." : description) << "\n"; + str << " \n"; + str << " input\n"; + str << " \n"; + return xmlParameter; + } +}; + +} +} + +Q_DECLARE_METATYPE(QList*) + +struct ctkCmdLineModuleBackendFunctionPointerPrivate; + +/** + * \class ctkCmdLineModuleBackendFunctionPointer + * \brief Provides a back-end implementation to enable directly calling a function pointer. + * \ingroup CommandLineModulesBackendFunctionPointer_API + * + * \warning This back-end is highly experimental and will not work for most function pointers when + * trying to register them via registerFunctionPointer(). + */ +class CTK_CMDLINEMODULEBACKENDFP_EXPORT ctkCmdLineModuleBackendFunctionPointer : public ctkCmdLineModuleBackend +{ + +public: + + class DescriptionPrivate; + + class Description + { + public: + + Description(); + ~Description(); + + QUrl moduleLocation() const; + + QString moduleCategory() const; + void setModuleCategory(const QString &category); + + QString moduleTitle() const; + void setModuleTitle(const QString& title); + + QString moduleDescription() const; + void setModuleDescription(const QString& description); + + QString moduleVersion() const; + void setModuleVersion(const QString& version); + + QString moduleContributor() const; + void setModuleContributor(const QString& contributor); + + private: + + friend class ctkCmdLineModuleBackendFunctionPointer; + friend class ctkCmdLineModuleFunctionPointerTask; + Description(const QUrl& location, const ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy& fpProxy); + + QSharedPointer d; + + }; + + ctkCmdLineModuleBackendFunctionPointer(); + ~ctkCmdLineModuleBackendFunctionPointer(); + + virtual QString name() const; + virtual QString description() const; + + virtual QList schemes() const; + + virtual qint64 timeStamp(const QUrl &location) const; + + virtual QByteArray rawXmlDescription(const QUrl& location); + + QList registeredFunctionPointers() const; + + template + Description* registerFunctionPointer(const QString& title, void (*fp)(A), + const QString& paramLabel = QString(), const QString& paramDescr = QString()) + { + typedef typename ctk::CmdLineModuleBackendFunctionPointer::TypeTraits::RawType RawTypeA; + + QList params; + params << ctk::CmdLineModuleBackendFunctionPointer::CreateXmlFor:: + parameter(0, + ctk::CmdLineModuleBackendFunctionPointer::GetParameterTypeName(), + paramLabel, paramDescr); + return this->registerFunctionPointerProxy(title, ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy(fp), params); + } + + template + Description* registerFunctionPointer(const QString& title, void (*fp)(A,B), + const QString& paramLabel0 = QString(), const QString& paramDescr0 = QString(), + const QString& paramLabel1 = QString(), const QString& paramDescr1 = QString()) + { + typedef typename ctk::CmdLineModuleBackendFunctionPointer::TypeTraits::RawType RawTypeA; + typedef typename ctk::CmdLineModuleBackendFunctionPointer::TypeTraits::RawType RawTypeB; + + QList params; + params << ctk::CmdLineModuleBackendFunctionPointer::CreateXmlFor:: + parameter(0, + ctk::CmdLineModuleBackendFunctionPointer::GetParameterTypeName(), + paramLabel0, paramDescr0); + params << ctk::CmdLineModuleBackendFunctionPointer::CreateXmlFor:: + parameter(1, + ctk::CmdLineModuleBackendFunctionPointer::GetParameterTypeName(), + paramLabel1, paramDescr1); + return this->registerFunctionPointerProxy(title, ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy(fp), params); + } + +protected: + + virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* frontend); + + virtual QList arguments(ctkCmdLineModuleFrontend* frontend) const; + +private: + + Description* registerFunctionPointerProxy(const QString &title, + const ctk::CmdLineModuleBackendFunctionPointer::FunctionPointerProxy& proxy, + const QList& params); + + + QScopedPointer d; + +}; + + +#endif // CTKCMDLINEMODULEBACKENDFUNCTIONPOINTER_H diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask.cpp b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask.cpp new file mode 100644 index 0000000000..ffbe5dc9d3 --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask.cpp @@ -0,0 +1,81 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFunctionPointerTask_p.h" + +#include "ctkCmdLineModuleBackendFPDescriptionPrivate.h" + +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleRunException.h" + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFunctionPointerTask::ctkCmdLineModuleFunctionPointerTask(const ctkCmdLineModuleBackendFunctionPointer::Description &fpDescr, const QList ¶mValues) + : FpDescription(fpDescr) + , ParamValues(paramValues) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleFunctionPointerTask::start() +{ + this->setRunnable(this); + this->setProgressRange(0,0); + this->reportStarted(); + ctkCmdLineModuleFuture future = this->future(); + QThreadPool::globalInstance()->start(this, /*m_priority*/ 0); + return future; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFunctionPointerTask::run() +{ + if (this->isCanceled()) + { + this->reportFinished(); + return; + } + + // call the function pointer and catch any exceptions + QString excMsg; + try + { + FpDescription.d->FpProxy.call(ParamValues); + } + catch (const std::exception& e) + { + excMsg = e.what(); + } + catch (...) + { + excMsg = "Unknown exception."; + } + + if (!excMsg.isNull()) + { + this->reportException(ctkCmdLineModuleRunException(FpDescription.moduleLocation(), 0, excMsg)); + } + + this->setProgressRange(0,1); + this->setProgressValue(1); + + //this->reportResult(result); + this->reportFinished(); +} diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask_p.h b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask_p.h new file mode 100644 index 0000000000..ce5d7ba066 --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleFunctionPointerTask_p.h @@ -0,0 +1,53 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFUNCTIONPOINTERTASK_P_H +#define CTKCMDLINEMODULEFUNCTIONPOINTERTASK_P_H + +#include "ctkCmdLineModuleFutureInterface.h" + +#include "ctkCmdLineModuleBackendFunctionPointer.h" + +#include + +/** + * \class ctkCmdLineModuleFunctionPointerTask + * \brief Provides a ctkCmdLineModuleFutureInterface implementation specifically to + * run a function pointer asynchronousely. + * \ingroup CommandLineModulesBackendFunctionPointer_API + */ +class ctkCmdLineModuleFunctionPointerTask : public ctkCmdLineModuleFutureInterface, public QRunnable +{ +public: + + ctkCmdLineModuleFunctionPointerTask(const ctkCmdLineModuleBackendFunctionPointer::Description& fpDescr, const QList& paramValues); + + ctkCmdLineModuleFuture start(); + + void run(); + +private: + + ctkCmdLineModuleBackendFunctionPointer::Description FpDescription; + QList ParamValues; +}; + +#endif // CTKCMDLINEMODULEFUNCTIONPOINTERTASK_P_H diff --git a/Libs/CommandLineModules/Backend/FunctionPointer/target_libraries.cmake b/Libs/CommandLineModules/Backend/FunctionPointer/target_libraries.cmake new file mode 100644 index 0000000000..139da8221c --- /dev/null +++ b/Libs/CommandLineModules/Backend/FunctionPointer/target_libraries.cmake @@ -0,0 +1,9 @@ +# +# See CMake/ctkMacroGetTargetLibraries.cmake +# +# This file should list the libraries required to build the current CTK libraries +# + +set(target_libraries + CTKCommandLineModulesCore + ) diff --git a/Libs/CommandLineModules/Backend/LocalProcess/CMakeLists.txt b/Libs/CommandLineModules/Backend/LocalProcess/CMakeLists.txt new file mode 100644 index 0000000000..71b2344c19 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/CMakeLists.txt @@ -0,0 +1,66 @@ +project(CTKCommandLineModulesBackendLocalProcess) + +# +# 3rd party dependencies +# + +# +# See CTK/CMake/ctkMacroBuildLib.cmake for details +# + +set(KIT_export_directive "CTK_CMDLINEMODULEBACKENDLP_EXPORT") + +# Additional directories to include + +# Source files +set(KIT_SRCS + ctkCmdLineModuleBackendLocalProcess.cpp + ctkCmdLineModuleProcessTask.cpp + ctkCmdLineModuleProcessWatcher.cpp + ctkCmdLineModuleProcessWatcher_p.h +) + +# Headers that should run through moc +set(KIT_MOC_SRCS + ctkCmdLineModuleProcessWatcher_p.h +) + +# UI files +set(KIT_UI_FORMS +) + +# Resources +set(KIT_resources + Resources/ctkCmdLineModulesBackendLocalProcess.qrc +) + +# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake +# The following macro will read the target libraries from the file 'target_libraries.cmake' +ctkFunctionGetTargetLibraries(KIT_target_libraries) + +ctkMacroBuildLib( + NAME ${PROJECT_NAME} + EXPORT_DIRECTIVE ${KIT_export_directive} + INCLUDE_DIRECTORIES ${KIT_include_directories} + SRCS ${KIT_SRCS} + MOC_SRCS ${KIT_MOC_SRCS} + UI_FORMS ${KIT_UI_FORMS} + TARGET_LIBRARIES ${KIT_target_libraries} + RESOURCES ${KIT_resources} + LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) + +target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES}) + +if(CTK_WRAP_PYTHONQT_FULL OR CTK_WRAP_PYTHONQT_LIGHT) + ctkMacroBuildLibWrapper( + TARGET ${PROJECT_NAME} + SRCS ${KIT_SRCS} + WRAPPER_LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) +endif() + +# Testing +if(BUILD_TESTING) + #add_subdirectory(Testing) +endif() diff --git a/Libs/CommandLineModules/Backend/LocalProcess/Documentation/CMakeLists.txt b/Libs/CommandLineModules/Backend/LocalProcess/Documentation/CMakeLists.txt new file mode 100644 index 0000000000..4bb75e1635 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/Documentation/CMakeLists.txt @@ -0,0 +1,5 @@ + +# Copy XML schema file to the Doxygen output directory +execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../Resources/ctkCmdLineModuleProcess.xsd + ${CTK_BINARY_DIR}/Documentation/html/ctkCmdLineModuleProcess.xsd) + diff --git a/Libs/CommandLineModules/Backend/LocalProcess/README.md b/Libs/CommandLineModules/Backend/LocalProcess/README.md new file mode 100644 index 0000000000..5b41cbcc9f --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/README.md @@ -0,0 +1,12 @@ +Local Process {#CommandLineModulesBackendLocalProcess_Page} +============= + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModulesBackendLocalProcess_Page.html) form. \endinternal + +The Local Process back-end is a fully featured back-end implementation for handling executable +modules. When registered with a ctkCmdLineModuleManager instance, it will handle the registration +of all modules with a "file" location URL scheme. See the ctkCmdLineModuleBackendLocalProcess class +for details. + +See the \ref CommandLineModulesBackendLocalProcess_API module for the API documentation. diff --git a/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModuleProcess.xsd b/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModuleProcess.xsd new file mode 100644 index 0000000000..62d97230b2 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModuleProcess.xsd @@ -0,0 +1,143 @@ + + + + + + + + CTK XML schema for progress and result reporting in command line modules. + false + false + + The XML schema for the XML fragments used when reporting progress and results in command line modules. + + + + + + + + + The root element. This is added automatically and must not be printed out by the module. + + + + + + + + + + + + + + + + + + + + + + + + + + + Marks the start of a set of processing instructions. + + + + + + The name of the current "filter" (set of processing instructions). + + + + + A short, descriptive text about the purpose of this filter. + + + + + + + + + + A float value between 0 and 1 to report the current overall progress. + + + + + + + + + + + + Report a progress value and corresponding progress text. + + + + + + + + + + Report the current result. + + + + + The output parameter name to which this result belongs to. + + + + + + + + + Marks the end of a set of processing instructions. + + + + + + A short, descriptive text about the end state of this filter. + + + + + + diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.qrc b/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModulesBackendLocalProcess.qrc similarity index 53% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.qrc rename to Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModulesBackendLocalProcess.qrc index bf4101fec4..b273f9168f 100644 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.qrc +++ b/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModulesBackendLocalProcess.qrc @@ -1,5 +1,5 @@ - ctkCLIModuleBlur2dImage.xml + ctkCmdLineModuleProcess.xsd diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.cpp b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.cpp new file mode 100644 index 0000000000..d8147bd15d --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.cpp @@ -0,0 +1,191 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleBackendLocalProcess.h" + +#include "ctkCmdLineModuleDescription.h" +#include "ctkCmdLineModuleFrontend.h" +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleParameter.h" +#include "ctkCmdLineModuleParameterGroup.h" +#include "ctkCmdLineModuleProcessTask.h" +#include "ctkCmdLineModuleReference.h" +#include "ctkCmdLineModuleRunException.h" + +#include "ctkUtils.h" + +#include +#include + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleBackendLocalProcessPrivate +{ + + QString normalizeFlag(const QString& flag) const + { + return flag.trimmed().remove(QRegExp("^-*")); + } + + QStringList commandLineArguments(const QHash& currentValues, + const ctkCmdLineModuleDescription& description) const + { + QStringList cmdLineArgs; + QHash indexedArgs; + + QHashIterator valuesIter(currentValues); + while(valuesIter.hasNext()) + { + valuesIter.next(); + ctkCmdLineModuleParameter parameter = description.parameter(valuesIter.key()); + if (parameter.index() > -1) + { + indexedArgs.insert(parameter.index(), valuesIter.value().toString()); + } + else + { + QString argFlag; + if (parameter.longFlag().isEmpty()) + { + argFlag = QString("-") + this->normalizeFlag(parameter.flag()); + } + else + { + argFlag = QString("--") + this->normalizeFlag(parameter.longFlag()); + } + + if (parameter.tag() == "boolean") + { + if (valuesIter.value().toBool()) + { + cmdLineArgs << argFlag; + } + } + else + { + QStringList args; + if (parameter.multiple()) + { + args = valuesIter.value().toString().split(',', QString::SkipEmptyParts); + } + else + { + QString arg = valuesIter.value().toString(); + if (arg.isEmpty()) + { + arg = parameter.defaultValue(); + } + if (!arg.isEmpty()) + { + args.push_back(valuesIter.value().toString()); + } + } + + if (args.length() > 0) // don't write the argFlag if there was no argument, and no default. + { + foreach(QString arg, args) + { + cmdLineArgs << argFlag << arg; + } + } + } + } + } + + QList indexes = indexedArgs.keys(); + qSort(indexes.begin(), indexes.end()); + foreach(int index, indexes) + { + cmdLineArgs << indexedArgs[index]; + } + + return cmdLineArgs; + } +}; + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendLocalProcess::ctkCmdLineModuleBackendLocalProcess() + : d(new ctkCmdLineModuleBackendLocalProcessPrivate){ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackendLocalProcess::~ctkCmdLineModuleBackendLocalProcess() +{ +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendLocalProcess::name() const +{ + return "Local Process"; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleBackendLocalProcess::description() const +{ + return "Runs an executable command line module using a local process."; +} + +//---------------------------------------------------------------------------- +QList ctkCmdLineModuleBackendLocalProcess::schemes() const +{ + static QList supportedSchemes = QList() << "file"; + return supportedSchemes; +} + +//---------------------------------------------------------------------------- +qint64 ctkCmdLineModuleBackendLocalProcess::timeStamp(const QUrl &location) const +{ + QFileInfo fileInfo(location.toLocalFile()); + if (fileInfo.exists()) + { + QDateTime dateTime = fileInfo.lastModified(); + return ctk::msecsTo(QDateTime::fromTime_t(0), dateTime); + } + return 0; +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleBackendLocalProcess::rawXmlDescription(const QUrl &location) +{ + QProcess process; + process.setReadChannel(QProcess::StandardOutput); + process.start(location.toLocalFile(), QStringList("--xml")); + + if (!process.waitForFinished() || process.exitStatus() == QProcess::CrashExit || + process.error() != QProcess::UnknownError) + { + throw ctkCmdLineModuleRunException(location, process.exitCode(), process.errorString()); + } + + process.waitForReadyRead(); + return process.readAllStandardOutput(); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleBackendLocalProcess::run(ctkCmdLineModuleFrontend* frontend) +{ + QStringList args = d->commandLineArguments(frontend->values(), frontend->moduleReference().description()); + + // Instances of ctkCmdLineModuleProcessTask are auto-deleted by the + // thread pool. + ctkCmdLineModuleProcessTask* moduleProcess = + new ctkCmdLineModuleProcessTask(frontend->location().toLocalFile(), args); + return moduleProcess->start(); +} diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.h b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.h new file mode 100644 index 0000000000..e8ecf2e0e9 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.h @@ -0,0 +1,99 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKENDLOCALPROCESS_H +#define CTKCMDLINEMODULEBACKENDLOCALPROCESS_H + +#include "ctkCmdLineModuleBackend.h" + +#include "ctkCommandLineModulesBackendLocalProcessExport.h" + +#include + +struct ctkCmdLineModuleBackendLocalProcessPrivate; + +/** + * @ingroup CommandLineModulesBackendLocalProcess_API + * + * @brief Provides an ctkCmdLineModuleBackend implementation + * to run a locally installed command line application. + * + * Use this back-end if you want to be able to register local executables as command + * line modules. The back-end handles the "file" URL scheme, allowing you to register + * modules with the ctkCmdLineModuleManager by using + * @code + * ctkCmdLineModuleManager::registerModule(QUrl::fromLocalFile("/path/to/executable")); + * @endcode + * + * The XML description for a module is extracted from the standard output of the + * executable when calling it with the \c ––xml command line argument. + * + * The ctkCmdLineModuleFuture returned by run() allows cancelation by killing the running + * process. On Unix systems, it also allows to pause it. + */ +class CTK_CMDLINEMODULEBACKENDLP_EXPORT ctkCmdLineModuleBackendLocalProcess : public ctkCmdLineModuleBackend +{ + +public: + + ctkCmdLineModuleBackendLocalProcess(); + ~ctkCmdLineModuleBackendLocalProcess(); + + virtual QString name() const; + virtual QString description() const; + + /** + * @brief This back-end can handle the "file" URL scheme. + * @return Returns the schemes this back-end can handle. + */ + virtual QList schemes() const; + + /** + * @brief Returns the last modified time of the module at \c location. + * @param location The location URL of the module for which to get the timestamp. + * @return A timestamp. + */ + virtual qint64 timeStamp(const QUrl &location) const; + + /** + * @brief Get the raw XML description from the module at \c location. + * @param location The location URL of the module for which to get the XML description. + * @return The raw XML description. + * + * This method always calls the executable with a \c ––xml argument and returns + * the complete data emitted on the standard output channel. + */ + virtual QByteArray rawXmlDescription(const QUrl& location); + + /** + * @brief Run a front-end for this module in a local process. + * @param frontend The front-end to run. + * @return A future object for communicating with the running process. + */ + virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend *frontend); + +private: + + QScopedPointer d; + +}; + +#endif // CTKCMDLINEMODULEBACKENDLOCALPROCESS_H diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.cpp b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.cpp new file mode 100644 index 0000000000..bfd8048b48 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.cpp @@ -0,0 +1,109 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleProcessTask.h" +#include "ctkCmdLineModuleProcessWatcher_p.h" +#include "ctkCmdLineModuleRunException.h" +#include "ctkCmdLineModuleXmlProgressWatcher.h" +#include "ctkCmdLineModuleFuture.h" + +#include +#include +#include +#include + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleProcessTaskPrivate +{ + ctkCmdLineModuleProcessTaskPrivate(const QString& location, const QStringList& args) + : Location(location) + , Args(args) + {} + + const QString Location; + const QStringList Args; +}; + +//---------------------------------------------------------------------------- +ctkCmdLineModuleProcessTask::ctkCmdLineModuleProcessTask(const QString& location, const QStringList& args) + : d(new ctkCmdLineModuleProcessTaskPrivate(location, args)) +{ + this->setCanCancel(true); +#ifdef Q_OS_UNIX + this->setCanPause(true); +#endif +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleProcessTask::~ctkCmdLineModuleProcessTask() +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleProcessTask::start() +{ + this->setRunnable(this); + this->reportStarted(); + ctkCmdLineModuleFuture future = this->future(); + QThreadPool::globalInstance()->start(this, /*m_priority*/ 0); + return future; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessTask::run() +{ + if (this->isCanceled()) + { + this->reportFinished(); + return; + } + + QProcess process; + process.setReadChannel(QProcess::StandardOutput); + + QEventLoop localLoop; + QObject::connect(&process, SIGNAL(finished(int)), &localLoop, SLOT(quit())); + QObject::connect(&process, SIGNAL(error(QProcess::ProcessError)), &localLoop, SLOT(quit())); + + process.start(d->Location, d->Args, QIODevice::ReadOnly | QIODevice::Text); + + ctkCmdLineModuleProcessWatcher progressWatcher(process, d->Location, *this); + Q_UNUSED(progressWatcher) + + localLoop.exec(); + + if (process.error() != QProcess::UnknownError || process.exitCode() != 0) + { + this->reportException(ctkCmdLineModuleRunException(d->Location, process.exitCode(), process.errorString())); + } + + if (this->progressValue() == 1001) + { + // We got a "filter-end" progress report, potentially with a comment, + // so don't overwrite the comment in the progress text. + this->setProgressValue(1002); + } + else + { + this->setProgressValueAndText(1002, QObject::tr("Finished.")); + } + this->reportFinished(); +} diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.h b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.h new file mode 100644 index 0000000000..006314812c --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessTask.h @@ -0,0 +1,67 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEPROCESSTASK_H +#define CTKCMDLINEMODULEPROCESSTASK_H + +#include "ctkCmdLineModuleFutureInterface.h" + +#include "ctkCommandLineModulesBackendLocalProcessExport.h" + +#include +#include +#include +#include +#include +#include + +class QProcess; + +struct ctkCmdLineModuleProcessTaskPrivate; + +/** + * \class ctkCmdLineModuleProcessTask + * \brief Implements ctkCmdLineModuleFutureInterface to enabling + * running a command line application asynchronously. + * \ingroup CommandLineModulesBackendLocalProcess_API + */ +class CTK_CMDLINEMODULEBACKENDLP_EXPORT ctkCmdLineModuleProcessTask + : public ctkCmdLineModuleFutureInterface, public QRunnable +{ + +public: + + ctkCmdLineModuleProcessTask(const QString& location, const QStringList& args); + ~ctkCmdLineModuleProcessTask(); + + ctkCmdLineModuleFuture start(); + + void run(); + +private: + + QScopedPointer d; + +}; + + + +#endif // CTKCMDLINEMODULEPROCESSTASK_H diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher.cpp b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher.cpp new file mode 100644 index 0000000000..7ece875485 --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher.cpp @@ -0,0 +1,169 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleProcessWatcher_p.h" + +#include "ctkCmdLineModuleFuture.h" + +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif + +//---------------------------------------------------------------------------- +ctkCmdLineModuleProcessWatcher::ctkCmdLineModuleProcessWatcher(QProcess& process, const QString& location, + ctkCmdLineModuleFutureInterface &futureInterface) + : process(process), location(location), futureInterface(futureInterface), processXmlWatcher(&process), + processPaused(false), progressValue(0) +{ + // The reported float value in the range [0.0,1.0] for the progress is scaled to [0,1000]. + // Value 1001 is reserved for the last "filter-end" output, which is reported as a progress event. + // Value 1002 is reserved internally to report process termination. + futureInterface.setProgressRange(0, 1002); + + connect(&processXmlWatcher, SIGNAL(filterStarted(QString,QString)), SLOT(filterStarted(QString,QString))); + connect(&processXmlWatcher, SIGNAL(filterProgress(float,QString)), SLOT(filterProgress(float,QString))); + connect(&processXmlWatcher, SIGNAL(filterResult(QString,QString)), SLOT(filterResult(QString,QString))); + connect(&processXmlWatcher, SIGNAL(filterFinished(QString,QString)), SLOT(filterFinished(QString,QString))); + connect(&processXmlWatcher, SIGNAL(filterXmlError(QString)), SLOT(filterXmlError(QString))); + + connect(&processXmlWatcher, SIGNAL(outputDataAvailable(QByteArray)), SLOT(outputDataAvailable(QByteArray))); + connect(&processXmlWatcher, SIGNAL(errorDataAvailable(QByteArray)), SLOT(errorDataAvailable(QByteArray))); + + connect(&futureWatcher, SIGNAL(canceled()), SLOT(cancelProcess())); +#ifdef Q_OS_UNIX + connect(&futureWatcher, SIGNAL(resumed()), SLOT(resumeProcess())); + // 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... + //connect(&futureWatcher, SIGNAL(paused()), SLOT(pauseProcess())); + connect(&pollPauseTimer, SIGNAL(timeout()), this, SLOT(pauseProcess())); + pollPauseTimer.start(500); +#endif + futureWatcher.setFuture(futureInterface.future()); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::filterStarted(const QString& name, const QString& comment) +{ + futureInterface.setProgressValueAndText(incrementProgress(), comment.isEmpty() ? tr("Starting") + name : comment); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::filterProgress(float progress, const QString& comment) +{ + futureInterface.setProgressValueAndText(updateProgress(progress), comment); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::filterResult(const QString ¶meter, const QString &value) +{ + futureInterface.reportResult(ctkCmdLineModuleResult(parameter, value)); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::filterFinished(const QString& name, const QString& comment) +{ + int progressValue = incrementProgress(); + if (progressValue == 1000) progressValue = 1001; + futureInterface.setProgressValueAndText(progressValue, comment.isEmpty() ? tr("Finished ") + name : comment); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::filterXmlError(const QString &error) +{ + qDebug().nospace() << "[Module " << location << "]: " << error; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::pauseProcess() +{ + if (processPaused || !futureInterface.isPaused()) return; + +#ifdef Q_OS_UNIX + if (::kill(process.pid(), SIGSTOP)) + { + // error + futureInterface.setPaused(false); + } + else + { + processPaused = true; + } +#endif +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::resumeProcess() +{ + if (!processPaused) return; + +#ifdef Q_OS_UNIX + if(::kill(process.pid(), SIGCONT)) + { + // error + futureInterface.setPaused(true); + } + else + { + processPaused = false; + } +#endif +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::cancelProcess() +{ + process.kill(); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::outputDataAvailable(const QByteArray &outputData) +{ + futureInterface.reportOutputData(outputData); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleProcessWatcher::errorDataAvailable(const QByteArray &errorData) +{ + futureInterface.reportErrorData(errorData); +} + +//---------------------------------------------------------------------------- +int ctkCmdLineModuleProcessWatcher::updateProgress(float progress) +{ + progressValue = static_cast(progress * 1000.0f); + // normalize the value to lie between 0 and 1000. + // 0 is reported when the process starts and 1001 is reserved for + // reporting completion. + if (progressValue < 1) progressValue = 1; + if (progressValue > 1000) progressValue = 1000; + return progressValue; +} + +//---------------------------------------------------------------------------- +int ctkCmdLineModuleProcessWatcher::incrementProgress() +{ + if (++progressValue > 1000) progressValue = 1000; + return progressValue; +} diff --git a/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher_p.h b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher_p.h new file mode 100644 index 0000000000..130993c9ac --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleProcessWatcher_p.h @@ -0,0 +1,81 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEPROCESSWATCHER_P_H +#define CTKCMDLINEMODULEPROCESSWATCHER_P_H + +#include "ctkCmdLineModuleXmlProgressWatcher.h" +#include "ctkCmdLineModuleFutureInterface.h" + +#include +#include +#include + +class ctkCmdLineModuleResult; + +class QProcess; + +/** + * \class ctkCmdLineModuleProcessWatcher + * \brief Provides progress updates using QFutureWatcher + * \ingroup CommandLineModulesBackendLocalProcess_API + */ +class ctkCmdLineModuleProcessWatcher : public QObject +{ + Q_OBJECT + +public: + + ctkCmdLineModuleProcessWatcher(QProcess& process, const QString& location, + ctkCmdLineModuleFutureInterface& futureInterface); + +protected Q_SLOTS: + + void filterStarted(const QString& name, const QString& comment); + void filterProgress(float progress, const QString &comment); + void filterResult(const QString& parameter, const QString& value); + void filterFinished(const QString& name, const QString &comment); + + void filterXmlError(const QString& error); + + void pauseProcess(); + void resumeProcess(); + void cancelProcess(); + + void outputDataAvailable(const QByteArray& outputData); + void errorDataAvailable(const QByteArray& errorData); + +private: + + int updateProgress(float progress); + int incrementProgress(); + + QProcess& process; + QString location; + ctkCmdLineModuleFutureInterface& futureInterface; + ctkCmdLineModuleXmlProgressWatcher processXmlWatcher; + QFutureWatcher futureWatcher; + QTimer pollPauseTimer; + bool processPaused; + int progressValue; +}; + +#endif // CTKCMDLINEMODULEPROCESSWATCHER_P_H diff --git a/Libs/CommandLineModules/Backend/LocalProcess/target_libraries.cmake b/Libs/CommandLineModules/Backend/LocalProcess/target_libraries.cmake new file mode 100644 index 0000000000..139da8221c --- /dev/null +++ b/Libs/CommandLineModules/Backend/LocalProcess/target_libraries.cmake @@ -0,0 +1,9 @@ +# +# See CMake/ctkMacroGetTargetLibraries.cmake +# +# This file should list the libraries required to build the current CTK libraries +# + +set(target_libraries + CTKCommandLineModulesCore + ) diff --git a/Libs/CommandLineModules/CMakeLists.txt b/Libs/CommandLineModules/CMakeLists.txt new file mode 100644 index 0000000000..8f292faccd --- /dev/null +++ b/Libs/CommandLineModules/CMakeLists.txt @@ -0,0 +1,7 @@ + +if(BUILD_TESTING AND + CTK_LIB_CommandLineModules/Core AND CTK_LIB_CommandLineModules/Backend/LocalProcess) + add_subdirectory(Testing) +endif() + +add_subdirectory(Documentation) diff --git a/Libs/CommandLineModules/Core/CMakeLists.txt b/Libs/CommandLineModules/Core/CMakeLists.txt index 8ca38a2f6c..009ded1b1b 100644 --- a/Libs/CommandLineModules/Core/CMakeLists.txt +++ b/Libs/CommandLineModules/Core/CMakeLists.txt @@ -14,22 +14,32 @@ set(KIT_export_directive "CTK_CMDLINEMODULECORE_EXPORT") # Source files set(KIT_SRCS + ctkCmdLineModuleBackend.cpp + ctkCmdLineModuleCache.cpp + ctkCmdLineModuleCache_p.h + ctkCmdLineModuleConcurrentHelpers.cpp + ctkCmdLineModuleDefaultPathBuilder.cpp ctkCmdLineModuleDescription.cpp - ctkCmdLineModuleDescriptionPrivate.h - ctkCmdLineModuleFuture.h - #ctkCmdLineModuleFuture.cpp - ctkCmdLineModuleInstance.cpp - ctkCmdLineModuleInstanceFactory.cpp + ctkCmdLineModuleDescription_p.h + ctkCmdLineModuleDirectoryWatcher.cpp + ctkCmdLineModuleDirectoryWatcher_p.h + ctkCmdLineModuleFrontend.cpp + ctkCmdLineModuleFrontendFactory.cpp + ctkCmdLineModuleFuture.cpp + ctkCmdLineModuleFutureInterface_p.h + ctkCmdLineModuleFutureInterface.cpp + ctkCmdLineModuleFutureWatcher.cpp ctkCmdLineModuleManager.cpp ctkCmdLineModuleParameter.cpp - ctkCmdLineModuleParameterPrivate.cpp + ctkCmdLineModuleParameter_p.h ctkCmdLineModuleParameterGroup.cpp - ctkCmdLineModuleParameterGroupPrivate.h + ctkCmdLineModuleParameterGroup_p.h ctkCmdLineModuleParameterParsers_p.h - #ctkCmdLineModuleProcess.cpp - ctkCmdLineModuleProcess_p.h + ctkCmdLineModulePathBuilder.cpp + ctkCmdLineModuleResult.cpp + ctkCmdLineModuleXmlProgressWatcher.cpp ctkCmdLineModuleReference.cpp - ctkCmdLineModuleReferencePrivate.cpp + ctkCmdLineModuleRunException.cpp ctkCmdLineModuleXmlException.cpp ctkCmdLineModuleXmlMsgHandler_p.h ctkCmdLineModuleXmlMsgHandler.cpp @@ -41,10 +51,21 @@ set(KIT_SRCS # Headers that should run through moc set(KIT_MOC_SRCS - ctkCmdLineModuleInstance.h - #ctkCmdLineModuleProcess_p.h + ctkCmdLineModuleDirectoryWatcher.h + ctkCmdLineModuleDirectoryWatcher_p.h + ctkCmdLineModuleFutureWatcher.h + ctkCmdLineModuleManager.h ) +QT4_GENERATE_MOCS( + ctkCmdLineModuleFrontend.h + ctkCmdLineModuleXmlProgressWatcher.h +) + +#list(APPEND KIT_SRCS +# ${CMAKE_CURRENT_BINARY_DIR}/moc_ctkCmdLineModuleXmlProgressWatcher.h +#) + # UI files set(KIT_UI_FORMS ) diff --git a/Libs/CommandLineModules/Core/Documentation/CMakeLists.txt b/Libs/CommandLineModules/Core/Documentation/CMakeLists.txt new file mode 100644 index 0000000000..7c8a02eeac --- /dev/null +++ b/Libs/CommandLineModules/Core/Documentation/CMakeLists.txt @@ -0,0 +1,5 @@ + +# Copy XML schema file to the Doxygen output directory +execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../Resources/ctkCmdLineModule.xsd + ${CTK_BINARY_DIR}/Documentation/html/ctkCmdLineModule.xsd) + diff --git a/Libs/CommandLineModules/Core/README.md b/Libs/CommandLineModules/Core/README.md new file mode 100644 index 0000000000..4c0a35449f --- /dev/null +++ b/Libs/CommandLineModules/Core/README.md @@ -0,0 +1,12 @@ +Core {#CommandLineModulesCore_Page} +==== + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModulesCore_Page.html) form. \endinternal + +The Command Line Modules Core library provides high-level classes to manage command line modules. + +It provides abstract classes for working with back and front ends and a central management class +for registering and running modules, the ctkCmdLineModuleManager. + +See the \ref CommandLineModulesCore_API module for the API documentation. diff --git a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModule.xsd b/Libs/CommandLineModules/Core/Resources/ctkCmdLineModule.xsd index e7105a9d32..62326a9fbf 100644 --- a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModule.xsd +++ b/Libs/CommandLineModules/Core/Resources/ctkCmdLineModule.xsd @@ -1,6 +1,17 @@ + - + + + + + CTK XML schema for command line modules. + false + false + + The XML schema for the XML description of command line module parameters. + + + The root element for each module XML description. It must contain + at least one "parameters" element. + - - - - - - - - - + + + + Classifies the module (e.g. Filtering, Segmentation). + The value can be a dot separated string to create category hierarchies. + + + + + + A human-readable name for the module. + + + + + + A detailed description of the modules purpose. + + + + + + The modules version number. A suggested format is: + <p> + major.minor.patch.build.status + </p><p> + where status is one of + <ul> + <li>vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).</li> + <li>a: alpha</li> + <li>b: beta</li> + <li>rc: release candidate</li> + <li>fcs: first customer ship</li> + </ul> + </p> + + + + + + + A URL pointing to a documentation or home page of the module. + + + + + + The type of license or a URL containing the license. + + + + + + The author(s) of the command line module. + + + + + + Acknowledgements for funding agency, employer, colleague, etc. + + - + + + Starts a group of parameters. + + @@ -34,44 +106,74 @@ =================================================================== --> + + Starts a group of parameters. + + - - + + + A short string used as the label for this group. + + + + + + A description of this parameter group. + + + - - + + + - - + + + + + + + - - - + + + + + + + - + + - + + + This value is usually used in GUI generators to decide + if the parameters belonging to this group should be initially hidden to the user or not. + + + + This type specifies elements common to all parameter types. + + + + The unique name (within this module) of the parameter. This is only used internally. + + @@ -93,18 +203,62 @@ + + You must either specify "flag" or "longflag" (or both) or "index". + - + + + An integer starting at 0, that specifies a module argument that has no flags. + The index value 1000 is reserved as a marker for output parameters (see the "channel" element) to indicate that + this parameter is used to return results during the execution of this module and does not need to be set. + + - - - - + + + A brief description of the parameter. + + + + + + A label for parameter. + + + + + + A default value for the parameter. The default must be a type that is compatible with the + parameter type. The vector parameters are specified as comma separated values of the atomic parameter type. + + + + + + + + Specifies whether the parameter is an input or output parameter. Output parameters can for + example specify file paths where to write output data (e.g. using the "image" element) or they can represent + "simple return parameters", indicated by providing an "index" of 1000. The current values of suche simple return + parameters are not passed to the module during its execution. Rather, the module itself reports these parameter + values during execution. + + + + + + + + + + + @@ -118,6 +272,10 @@ =================================================================== --> + + This type represents vectors of integers, floats, and doubles and can contain + constraints on the domain of valid values for the vector elements. + @@ -134,9 +292,18 @@ =================================================================== --> + + Parameters of this type are allowed to be passed multiple times with + different values to the module if the attribute "multiple" is set to true. Note that if such + a parameter has no flags, its values must be passed as the last arguments to the module. + - + + + Allows this parameter to occur multiple times. + + @@ -148,6 +315,10 @@ =================================================================== --> + + This type represents integers, floats, and doubles and can contain + constraints on the domain of valid values. + @@ -164,10 +335,18 @@ =================================================================== --> + + Restricts the valid parameter value to one and only one element out of + a specified descrete set of values. + - + + + Defines one possible enumeration value. + + @@ -180,9 +359,23 @@ =================================================================== --> + + A parameter describing a point or region in 3D with a specified coordinate system. + + + Specifies the coordinate system. If unspecified, the executing module is free to interpret the + coordinates in the most appropriate way. For more information about the different systems, see + <a href="http://www.slicer.org/slicerWiki/index.php/Coordinate_systems">Coordinate Systems</a>. + <ul> + <li><b>ras</b> (Right, Anterior, Superior) coordinate system.</li> + <li><b>ijk</b> image coordinate system.</li> + <li><b>lps</b> (Left, Posterior, Superior) coordinate system.</li> + </ul> + + @@ -204,8 +397,15 @@ - + + + A comma separated list of allowed file extensions. + + + + Optionally specifies the allowed geometry type. + @@ -216,41 +416,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + A comma separated list of allowed file extensions (e.g. "nrrd,mhd"). + + @@ -265,6 +446,9 @@ + + Optionally specifies the allowed image type. + @@ -282,7 +466,7 @@ @@ -293,10 +477,23 @@ + + A single character flag (e.g. "s", "W", etc.). Not required if "longFlag" is specified. + - - + + + A comma separated list of aliases. Can be used to provide different flags for the same parameter. + + + + + + A comma separated list of deprecated aliases. When invoking a module with one of these aliases, + the callee will be notified about the new preferred flag name. + + @@ -308,20 +505,51 @@ + + A multi-character flag (e.g. "spacing", "Watcher", etc.). Not required if "flag" is specified. + - - + + + A comma separated list of aliases. Can be used to provide different long flags for the same parameter. + + + + + + A comma separated list of deprecated aliases. When invoking a module with one of these aliases, + the callee will be notified about the new preferred long flag name. + + + + + Constraints on the allowed parameter value for scalar types and their vector variants. + - - - + + + The minimum allowed value for the parameter. If not specified, the minimum is the smallest + possible value for the parameter type. + + + + + The maximum allowed value for the parameter. If not specified, the maximum is the largest + possible value for the parameter type. + + + + + The increment for the parameter. + + diff --git a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModules.qrc b/Libs/CommandLineModules/Core/Resources/ctkCmdLineModules.qrc index 21f2884497..ffbc77342e 100644 --- a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModules.qrc +++ b/Libs/CommandLineModules/Core/Resources/ctkCmdLineModules.qrc @@ -1,7 +1,5 @@ ctkCmdLineModule.xsd - ctkCmdLineModuleXmlToQtUi.xsl - QtDesigner.xsd diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/CMakeLists.txt b/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/CMakeLists.txt deleted file mode 100644 index 347cc43562..0000000000 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ - -ctkFunctionCreateCLIModule(Blur2dImage) diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/CMakeLists.txt b/Libs/CommandLineModules/Core/Testing/CLIModules/CMakeLists.txt deleted file mode 100644 index c077057342..0000000000 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ - -# This is very simple and for test purposes -# only. Relies on naming conventions and has -# no extensive error checking yet. -function(ctkFunctionCreateCLIModule name) - set(_src_files ${ARGN}) - list(APPEND _src_files ctkCLIModule${name}.cpp) - qt4_add_resources(_src_files ctkCLIModule${name}.qrc) - - add_executable(ctkCLIModule${name} ${_src_files}) - target_link_libraries(ctkCLIModule${name} CTKCore ${QT_LIBRARIES}) - add_dependencies(ctkCLITestModules ctkCLIModule${name}) -endfunction() - -set(_cli_modules - Blur2dImage - Tour -) - -add_custom_target(ctkCLITestModules) - -foreach(_cli_module ${_cli_modules}) - add_subdirectory(${_cli_module}) -endforeach() diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/CMakeLists.txt b/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/CMakeLists.txt deleted file mode 100644 index 06b4625734..0000000000 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ - -ctkFunctionCreateCLIModule(Tour) diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/ctkCLIModuleBlur2dImage.xml b/Libs/CommandLineModules/Core/Testing/CLIModules/ctkCLIModuleBlur2dImage.xml deleted file mode 100644 index cdcae048f0..0000000000 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/ctkCLIModuleBlur2dImage.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - Tours - Execution Model Tour - - Shows one of each type of parameter. - - 1.0 - - - Daniel Blezek - - - - - Variations on scalar parameters - - - integerVariable - i - integer - - An integer without constraints - - - 30 - - - booleanParam - b - - A boolean without constraints - - - - - fileVar - 3 - Some file - - bla - input - - - dirVar - 6 - Some dir - - /home - output - - - geomVar - 3 - Some geom - - - - doubleVariable - d - double - An double with constraints - - 30 - - 0 - 1.e3 - 0 - - - - - - - Variations on vector parameters - - floatVector - f - A vector of floats - - 1.3,2,-14 - - - stringVector - string_vector - A vector of strings - - "foo",bar,"foobar" - - - - - - Variations on enumeration parameters - - stringChoice - e - enumeration - An enumeration of strings - - foo - foo - "foobar" - foofoo - - - pointVar - p - asf - - 0.5,-34.2,43 - - - - diff --git a/Libs/CommandLineModules/Core/Testing/CMakeLists.txt b/Libs/CommandLineModules/Core/Testing/CMakeLists.txt index 5747ec817c..cdeb442a1d 100644 --- a/Libs/CommandLineModules/Core/Testing/CMakeLists.txt +++ b/Libs/CommandLineModules/Core/Testing/CMakeLists.txt @@ -1,2 +1 @@ -add_subdirectory(CLIModules) -#add_subdirectory(Cpp) +add_subdirectory(Cpp) diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/CMakeLists.txt b/Libs/CommandLineModules/Core/Testing/Cpp/CMakeLists.txt index 1dccd77c0b..7bf88c79c1 100644 --- a/Libs/CommandLineModules/Core/Testing/Cpp/CMakeLists.txt +++ b/Libs/CommandLineModules/Core/Testing/Cpp/CMakeLists.txt @@ -1,20 +1,46 @@ set(KIT ${PROJECT_NAME}) +set(LIBRARY_NAME ${PROJECT_NAME}) create_test_sourcelist(Tests ${KIT}CppTests.cpp - ctkModuleDescriptionTest.cpp + ctkCmdLineModuleManagerTest.cpp + ctkCmdLineModuleXmlProgressWatcherTest.cpp + ctkCmdLineModuleDefaultPathBuilderTest.cpp ) -SET (TestsToRun ${Tests}) -REMOVE (TestsToRun ${KIT}CppTests.cpp) +set(TestsToRun ${Tests}) +remove(TestsToRun ${KIT}CppTests.cpp) -set(LIBRARY_NAME ${PROJECT_NAME}) +set(Tests_SRCS ${Tests_SRCS} + ctkCmdLineModuleSignalTester.cpp +) +set(Tests_MOC_SRCS ${Tests_MOC_SRCS} + ctkCmdLineModuleSignalTester.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/Libs/Testing + ${CMAKE_CURRENT_BINARY_DIR} + ) -add_executable(${KIT}CppTests ${Tests}) +set(Tests_MOC_CPP) +QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS}) +QT4_GENERATE_MOCS( + ctkCmdLineModuleManagerTest.cpp + ctkCmdLineModuleXmlProgressWatcherTest.cpp +) +set(Tests_UI_CPP) +if(TEST_UI_FORMS) + QT4_WRAP_UI(Tests_UI_CPP ${Tests_UI_FORMS}) +endif() +set(Tests_RESOURCES_SRCS) +QT4_ADD_RESOURCES(Tests_RESOURCES_SRCS ${Tests_RESOURCES}) + +add_executable(${KIT}CppTests ${Tests} ${Tests_SRCS} ${Tests_MOC_CPP} ${Tests_UI_CPP} ${Tests_RESOURCES_SRCS}) target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES}) # # Add Tests # - -SIMPLE_TEST( ctkModuleDescriptionTest.cpp ) - +SIMPLE_TEST(ctkCmdLineModuleManagerTest) +SIMPLE_TEST(ctkCmdLineModuleXmlProgressWatcherTest) +SIMPLE_TEST(ctkCmdLineModuleDefaultPathBuilderTest ${CTK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleDefaultPathBuilderTest.cpp b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleDefaultPathBuilderTest.cpp new file mode 100644 index 0000000000..df67436695 --- /dev/null +++ b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleDefaultPathBuilderTest.cpp @@ -0,0 +1,136 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkTest.h" +#include "ctkCmdLineModuleDefaultPathBuilder.h" + +// Others +#include + +/** + * \fn Basic tests for ctkCmdLineModuleDefaultPathBuilderTest. + */ +int ctkCmdLineModuleDefaultPathBuilderTest(int argc, char* argv[]) +{ + QApplication myApp(argc, argv); + + if (argc != 2) + { + qDebug() << "Usage: ctkCmdLineModuleDefaultPathBuilderTest directoryThatTheApplicationIsIn"; + } + QString runtimeDirectoryName = argv[1]; + QDir runtimeDirectory(runtimeDirectoryName); + + ctkCmdLineModuleDefaultPathBuilder builder; + + QStringList defaultList = builder.build(); + if (defaultList.size() != 0) + { + qDebug() << "The default list should be empty"; + return EXIT_FAILURE; + } + + builder.setLoadFromCurrentDir(true); + + QStringList result = builder.build(); + qDebug() << "1. Built:" << result; + + if (result.size() != 2) + { + qDebug() << "The flag setLoadFromCurrentDir enables scanning of the current working directory plus the subfolder cli-modules"; + return EXIT_FAILURE; + } + + builder.setLoadFromApplicationDir(true); + + result = builder.build(); + qDebug() << "2. Built:" << result; + + if (result.size() != 4) + { + qDebug() << "The flag setLoadFromApplicationDir enables scanning of the current installation directory plus the subfolder cli-modules"; + return EXIT_FAILURE; + } + + builder.setLoadFromCurrentDir(false); + + result = builder.build(); + qDebug() << "3. Built:" << result; + + if (!result.contains(runtimeDirectory.absolutePath())) + { + qDebug() << "Loading from the application diretory (where THIS application is located), should produce the same path as passed in via the command line argument ${CTK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}"; + } + + builder.setLoadFromHomeDir(true); + + result = builder.build(); + qDebug() << "4. Built:" << result; + + if (result.size() != 4) + { + qDebug() << "Should now be loading from applicationDir, applicationDir/cli-modules, homeDir, homeDir/cli-modules"; + return EXIT_FAILURE; + } + + builder.setLoadFromCtkModuleLoadPath(true); + + result = builder.build(); + qDebug() << "5. Built:" << result; + + // If the environment variable CTK_MODULE_LOAD_PATH exists, it should point to a valid directory. + // If it does not exist, then the list should not change. + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + qDebug() << "Environment is:" << env.toStringList(); + + if (env.contains("CTK_MODULE_LOAD_PATH")) + { + QDir loadDir(env.value("CTK_MODULE_LOAD_PATH")); + + qDebug() << "CTK_MODULE_LOAD_PATH does exist, and is set to:" << env.value("CTK_MODULE_LOAD_PATH") << ", and isExists() returns " << loadDir.exists(); + + if (loadDir.exists() && result.size() != 5) + { + qDebug() << "Environment variable CTK_MODULE_LOAD_PATH did exist and is valid, so there should be 5 entries"; + return EXIT_FAILURE; + } + else if (!loadDir.exists() && result.size() != 4) + { + qDebug() << "Environment variable CTK_MODULE_LOAD_PATH did exist but is invalid, so there should be 4 entries"; + return EXIT_FAILURE; + } + } + else if (result.size() != 4) + { + qDebug() << "Environment variable CTK_MODULE_LOAD_PATH did not exist, so there should still be 4 entries as previous test"; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp new file mode 100644 index 0000000000..3f326435cf --- /dev/null +++ b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp @@ -0,0 +1,191 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + + +#include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleBackend.h" +#include "ctkException.h" +#include "ctkCmdLineModuleFuture.h" + +#include "ctkTest.h" + +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +extern int qHash(const QUrl& url); +#endif + +namespace { + +class BackendMockUp : public ctkCmdLineModuleBackend +{ + +public: + + void addModule(const QUrl& location, const QByteArray& xml) + { + this->UrlToXml[location] = xml; + } + + virtual QString name() const { return "Mockup"; } + virtual QString description() const { return "Test Mock-up"; } + virtual QList schemes() const { return QList() << "test"; } + virtual qint64 timeStamp(const QUrl& /*location*/) const { return 0; } + virtual QByteArray rawXmlDescription(const QUrl& location) + { + return UrlToXml[location]; + } + +protected: + + virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* /*frontend*/) + { + return ctkCmdLineModuleFuture(); + } + +private: + + QHash UrlToXml; +}; + +} + +//----------------------------------------------------------------------------- +class ctkCmdLineModuleManagerTester : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void initTestCase(); + + void testStrictValidation(); + void testWeakValidation(); + void testSkipValidation(); + +private: + + QByteArray validXml; + QByteArray invalidXml; +}; + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleManagerTester::initTestCase() +{ + validXml = "\n" + " My Filter\n" + " Awesome filter\n" + " \n" + " \n" + " bla\n" + " \n" + " param\n" + " i\n" + " bla\n" + " \n" + " \n" + " \n" + "\n"; + + invalidXml = "\n" + " Awesome filter\n" + " My Filter\n" + " \n" + " \n" + " bla\n" + " \n" + " param\n" + " i\n" + " bla\n" + " \n" + " \n" + " \n" + "\n"; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleManagerTester::testStrictValidation() +{ + BackendMockUp backend; + backend.addModule(QUrl("test://validXml"), validXml); + backend.addModule(QUrl("test://invalidXml"), invalidXml); + + ctkCmdLineModuleManager manager; + manager.registerBackend(&backend); + + ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml")); + QVERIFY(moduleRef); + QVERIFY(moduleRef.xmlValidationErrorString().isEmpty()); + + try + { + manager.registerModule(QUrl("test://invalidXml")); + QFAIL("Succeeded in registering invalid module"); + } + catch (const ctkInvalidArgumentException&) + { + } +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleManagerTester::testWeakValidation() +{ + BackendMockUp backend; + backend.addModule(QUrl("test://validXml"), validXml); + backend.addModule(QUrl("test://invalidXml"), invalidXml); + + ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::WEAK_VALIDATION); + manager.registerBackend(&backend); + + ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml")); + QVERIFY(moduleRef); + QVERIFY(moduleRef.xmlValidationErrorString().isEmpty()); + + ctkCmdLineModuleReference moduleRef2 = manager.registerModule(QUrl("test://invalidXml")); + QVERIFY(moduleRef2); + QVERIFY(!moduleRef2.xmlValidationErrorString().isEmpty()); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleManagerTester::testSkipValidation() +{ + BackendMockUp backend; + backend.addModule(QUrl("test://validXml"), validXml); + backend.addModule(QUrl("test://invalidXml"), invalidXml); + + ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::SKIP_VALIDATION); + manager.registerBackend(&backend); + + ctkCmdLineModuleReference moduleRef = manager.registerModule(QUrl("test://validXml")); + QVERIFY(moduleRef); + QVERIFY(moduleRef.xmlValidationErrorString().isEmpty()); + + ctkCmdLineModuleReference moduleRef2 = manager.registerModule(QUrl("test://invalidXml")); + QVERIFY(moduleRef2); + QVERIFY(moduleRef2.xmlValidationErrorString().isEmpty()); +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleManagerTest) +#include "moc_ctkCmdLineModuleManagerTest.cpp" diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp new file mode 100644 index 0000000000..a2fdfdd18d --- /dev/null +++ b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp @@ -0,0 +1,116 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleSignalTester.h" + +#include + +void ctkCmdLineModuleSignalTester::moduleStarted() +{ + events.push_back("module.started"); +} + +void ctkCmdLineModuleSignalTester::moduleFinished() +{ + events.push_back("module.finished"); +} + +void ctkCmdLineModuleSignalTester::moduleProgressValueChanged(int /*progress*/) +{ + events.push_back("module.progressValueChanged"); +} + +void ctkCmdLineModuleSignalTester::moduleProgressTextChanged(const QString &/*text*/) +{ + events.push_back("module.progressTextChanged"); +} + +void ctkCmdLineModuleSignalTester::modulePaused() +{ + events.push_back("module.paused"); +} + +void ctkCmdLineModuleSignalTester::moduleResumed() +{ + events.push_back("module.resumed"); +} + +void ctkCmdLineModuleSignalTester::moduleCanceled() +{ + events.push_back("module.canceled"); +} + +void ctkCmdLineModuleSignalTester::filterStarted(const QString &/*name*/, const QString &/*comment*/) +{ + events.push_back("filter.started"); +} + +void ctkCmdLineModuleSignalTester::filterProgress(float /*progress*/, const QString &/*comment*/) +{ + events.push_back("filter.progress"); +} + +void ctkCmdLineModuleSignalTester::filterFinished(const QString &/*name*/, const QString &/*comment*/) +{ + events.push_back("filter.finished"); +} + +void ctkCmdLineModuleSignalTester::filterXmlError(const QString &/*error*/) +{ + events.push_back("filter.xmlError"); +} + +bool ctkCmdLineModuleSignalTester::checkSignals(const QList& expectedSignals) +{ + if (events.size() != expectedSignals.size()) + { + dumpSignals(expectedSignals); + return false; + } + + for (int i=0; i < expectedSignals.size(); ++i) + { + if (expectedSignals[i] != events[i]) + { + dumpSignals(expectedSignals); + return false; + } + } + return true; +} + +void ctkCmdLineModuleSignalTester::dumpSignals(const QList& expectedSignals) +{ + int max = events.size() > expectedSignals.size() ? events.size() : expectedSignals.size(); + qDebug() << "Expected signal -- Actual signal"; + for (int i = 0; i < max; ++i) + { + QString sig = i < events.size() ? events[i] : QString(); + if (i < expectedSignals.size()) + { + qDebug() << " " << expectedSignals[i] << "--" << sig; + } + else + { + qDebug() << " " << "- NONE - " << "--" << sig; + } + } +} diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.h b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.h new file mode 100644 index 0000000000..7c6e414391 --- /dev/null +++ b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleSignalTester.h @@ -0,0 +1,59 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULESIGNALTESTER_H +#define CTKCMDLINEMODULESIGNALTESTER_H + +#include +#include + +class ctkCmdLineModuleSignalTester : public QObject +{ + Q_OBJECT + +public: + + bool checkSignals(const QList& expectedSignals); + void dumpSignals(const QList& expectedSignals); + + +public Q_SLOTS: + + virtual void moduleStarted(); + virtual void moduleFinished(); + virtual void moduleProgressValueChanged(int progress); + virtual void moduleProgressTextChanged(const QString& text); + + virtual void modulePaused(); + virtual void moduleResumed(); + virtual void moduleCanceled(); + + virtual void filterStarted(const QString& name, const QString& comment); + virtual void filterProgress(float progress, const QString& comment); + virtual void filterFinished(const QString& name, const QString& comment); + virtual void filterXmlError(const QString& error); + +private: + + QList events; +}; + +#endif // CTKCMDLINEMODULESIGNALTESTER_H diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleXmlProgressWatcherTest.cpp b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleXmlProgressWatcherTest.cpp new file mode 100644 index 0000000000..8ed8636e95 --- /dev/null +++ b/Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleXmlProgressWatcherTest.cpp @@ -0,0 +1,198 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + + +#include + +#include "ctkCmdLineModuleSignalTester.h" + +#include "ctkTest.h" + +#include +#include +#include +#include + + +namespace { + +//----------------------------------------------------------------------------- +// Custom signal tester +class SignalTester : public ctkCmdLineModuleSignalTester +{ +public: + + SignalTester() : accumulatedProgress(0) + {} + + void filterStarted(const QString& name, const QString& comment) + { + ctkCmdLineModuleSignalTester::filterStarted(name, comment); + if (name != "My Filter") + { + error = "Filter name does not match \"My Filter\" (got \"" + name + "\")"; + return; + } + if (comment != "Awesome filter") + { + error = "Filter comment does not match \"Awesome filter\" (got \"" + comment + "\")"; + return; + } + } + + void filterProgress(float progress, const QString& comment) + { + ctkCmdLineModuleSignalTester::filterProgress(progress, comment); + accumulatedProgress += progress; + } + + void filterFinished(const QString& name, const QString& comment) + { + ctkCmdLineModuleSignalTester::filterFinished(name, comment); + if (name != "My Filter") + { + error = "Filter name does not match \"My Filter\" (got \"" + name + "\")"; + return; + } + } + + void filterXmlError(const QString& error) + { + ctkCmdLineModuleSignalTester::filterXmlError(error); + this->error = error; + } + + QString error; + float accumulatedProgress; +}; + +} + +//----------------------------------------------------------------------------- +class ctkCmdLineModuleXmlProgressWatcherTester : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testSignalsAndValues(); + void testMalformedXml(); +}; + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleXmlProgressWatcherTester::testSignalsAndValues() +{ + // Test data + QByteArray filterStart = "\n" + "My Filter\n" + "Awesome filter\n" + "\n"; + QString filterProgress = "%1\n"; + QByteArray filterEnd = "\n" + "My Filter\n" + "23\n" + ""; + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + ctkCmdLineModuleXmlProgressWatcher progressWatcher(&buffer); + + SignalTester signalTester; + signalTester.connect(&progressWatcher, SIGNAL(filterStarted(QString,QString)), &signalTester, SLOT(filterStarted(QString,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterProgress(float,QString)), &signalTester, SLOT(filterProgress(float,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterFinished(QString,QString)), &signalTester, SLOT(filterFinished(QString,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterXmlError(QString)), &signalTester, SLOT(filterXmlError(QString))); + + buffer.write(filterStart); + buffer.write(filterProgress.arg(0.3).toLatin1()); + buffer.write(filterProgress.arg(0.6).toLatin1()); + buffer.write(filterProgress.arg(0.9).toLatin1()); + buffer.write(filterEnd); + + QCoreApplication::processEvents(); + + QList expectedSignals; + expectedSignals << "filter.started"; + expectedSignals << "filter.progress"; + expectedSignals << "filter.progress"; + expectedSignals << "filter.progress"; + expectedSignals << "filter.finished"; + + if (!signalTester.error.isEmpty()) + { + qDebug() << signalTester.error; + QFAIL("XML parsing error"); + } + + QVERIFY(signalTester.checkSignals(expectedSignals)); + + QCOMPARE(signalTester.accumulatedProgress, 1.8f); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleXmlProgressWatcherTester::testMalformedXml() +{ + // Test data + QByteArray filterOutput = "\n" + "My Filter\n" + "Awesome filter\n" + "chunk...\n" + "0.2\n" + "\n" + "0.5\n" + "\n" + + "My Filter\n" + "23\n" + ""; + + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + ctkCmdLineModuleXmlProgressWatcher progressWatcher(&buffer); + + SignalTester signalTester; + signalTester.connect(&progressWatcher, SIGNAL(filterStarted(QString,QString)), &signalTester, SLOT(filterStarted(QString,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterProgress(float,QString)), &signalTester, SLOT(filterProgress(float,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterFinished(QString,QString)), &signalTester, SLOT(filterFinished(QString,QString))); + signalTester.connect(&progressWatcher, SIGNAL(filterXmlError(QString)), &signalTester, SLOT(filterXmlError(QString))); + + buffer.write(filterOutput); + + QCoreApplication::processEvents(); + + QList expectedSignals; + expectedSignals << "filter.xmlError"; + expectedSignals << "filter.started"; + expectedSignals << "filter.progress"; + expectedSignals << "filter.finished"; + + QVERIFY(!signalTester.error.isEmpty()); + qDebug() << signalTester.error; + + QVERIFY(signalTester.checkSignals(expectedSignals)); + + QCOMPARE(signalTester.accumulatedProgress, 0.5f); +} + + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleXmlProgressWatcherTest) +#include "moc_ctkCmdLineModuleXmlProgressWatcherTest.cpp" diff --git a/Libs/CommandLineModules/Core/Testing/Cpp/ctkModuleDescriptionTest.cpp b/Libs/CommandLineModules/Core/Testing/Cpp/ctkModuleDescriptionTest.cpp deleted file mode 100644 index e3fd174cbd..0000000000 --- a/Libs/CommandLineModules/Core/Testing/Cpp/ctkModuleDescriptionTest.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*========================================================================= - - Library: CTK - - Copyright (c) 2010 Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -// Qt includes -#include - -// CTK includes -#include "ctkModuleDescription.h" -#include - -//----------------------------------------------------------------------------- -int ctkModuleDescriptionTest(int argc, char * argv [] ) -{ - Q_UNUSED(argc); - Q_UNUSED(argv); - - - ctkModuleParameter param; - param[ "Tag" ] = "MyTag"; - param[ "Name" ] = "MyName"; - param[ "Description" ] = "MyDescription"; - param[ "Label" ] = "MyLabel"; - param[ "CPPType" ] = "MyCPPType"; - param[ "Type" ] = "MyType"; - param[ "Reference" ] = "MyReference"; - param[ "Hidden" ] = "false"; - param[ "ArgType" ] = "MyArgType"; - param[ "StringToType" ] = "MyStringToType"; - param[ "Default" ] = "MyDefault"; - param[ "Flag" ] = "MyFlag"; - param[ "LongFlag" ] = "MyLongFlag"; - param[ "Constraints" ] = "MyConstraints"; - param[ "Minimum" ] = "MyMinimum"; - param[ "Maximum" ] = "MyMaximum"; - param[ "Channel" ] = "MyChannel"; - param[ "Index" ] = "MyIndex"; - param[ "Multiple" ] = "false"; - param[ "Aggregate" ] = "false"; - param[ "FileExtensions" ] = ".vtk,.jpg"; - param[ "FlagAliases" ] = "MyFlagAliases"; - param[ "DeprecatedFlagAliases" ] = "MyDeprecatedFlagAliases"; - param[ "LongFlagAliases" ] = "MyLongFlagAliases"; - param[ "DeprecatedLongFlagAliases" ] = "MyDeprecatedLongFlagAliases"; - param[ "CoordinateSystem" ] = "MyCoordinateSystem"; - - - ctkModuleParameterGroup group; - group[ "Label" ] = "MyLabel"; - group[ "Description" ] = "MyDescription"; - group[ "Advanced" ] = "MyAdvanced"; - - - ctkModuleDescription module; - module[ "Category" ] = "MyCategory"; - module[ "Index" ] = "MyIndex"; - module[ "Title" ] = "MyTitle"; - module[ "Description" ] = "MyDescription"; - module[ "DocumentationURL" ] = "MyDocumentationURL"; - module[ "License" ] = "MyLicense"; - module[ "Acknowledgements" ] = "MyAcknowledgements"; - module[ "Contributor" ] = "MyContributor"; - module[ "Type" ] = "MyType"; - module[ "AlternativeType" ] = "MyAlternativeType"; - module[ "Target" ] = "MyTarget"; - module[ "AlternativeTarget" ] = "MyAlternativeTarget"; - module[ "Location" ] = "MyLocation"; - - group.addParameter( new ctkModuleParameter(param) ); - module.addParameterGroup( new ctkModuleParameterGroup(group) ); - - QTextStream stream(stdout); - stream<< module; - - return EXIT_SUCCESS; -} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.cpp similarity index 82% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.cpp rename to Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.cpp index 268b52706a..56219ef4ef 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstanceFactory.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.cpp @@ -1,26 +1,27 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ -#include "ctkCmdLineModuleInstanceFactory.h" +#include "ctkCmdLineModuleBackend.h" -ctkCmdLineModuleInstanceFactory::~ctkCmdLineModuleInstanceFactory() +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackend::~ctkCmdLineModuleBackend() { } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.h new file mode 100644 index 0000000000..82d3e32ceb --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.h @@ -0,0 +1,106 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEBACKEND_H +#define CTKCMDLINEMODULEBACKEND_H + +#include "ctkCommandLineModulesCoreExport.h" + +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleFuture; + +template class QList; +class QUrl; + +/** + * @ingroup CommandLineModulesCore_API + * + * @brief Abstract base class for all back-end command line module + * implementations. + * + * A back-end is responsible for providing the XML module description for a + * given URL and its "timestamp". It also knows how to actually run a module, + * using the current parameter values provided by a ctkCmdLineModuleFrontend instance. + * + * @see ctkCmdLineModuleBackendLocalProcess + * @see ctkCmdLineModuleBackendFunctionPointer + */ +struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleBackend +{ + + ~ctkCmdLineModuleBackend(); + + /** + * @brief Returns the name of the type of the backend, not the name + * of the thing or application that is run. + * @return A QString containing the name. + */ + virtual QString name() const = 0; + + /** + * @brief Returns a brief description of the type of the backend. + * @return A QString containing a description. + */ + virtual QString description() const = 0; + + /** + * @brief Returns a list of URL schemes this back-end can handle. + * @return A list of "schemes", meaning the capabilities. + */ + virtual QList schemes() const = 0; + + /** + * @brief Returns a timestap of the backend, which for example in the + * case of the LocalProcess may be the last modified time of the command line + * application. + */ + virtual qint64 timeStamp(const QUrl& location) const = 0; + + /** + * @brief Get the XML parameter description from the given location. + * @param location The location URL specifying the module. + * @return The raw XML parameter description. + * + * This method may be concurrently called by the ctkCmdLineModuleManager and + * must be thread-safe. Implementations must not use any caching mechanism, + * as caching is done by the ctkCmdLineModuleManager itself, checking the + * return value of timeStamp(). + * + */ + virtual QByteArray rawXmlDescription(const QUrl& location) = 0; + +protected: + + friend class ctkCmdLineModuleManager; + + /** + * @brief The main method to actually execute the back-end process. + * @param frontend A pointer to a front end implementation. + * + * Implementations must execute the actual task of running the module asynchronously + * and return from this method immediately. After returning from this method, + * accessing the frontend pointer is not guaranteed to be safe. + */ + virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* frontend) = 0; + +}; + +#endif // CTKCMDLINEMODULEBACKEND_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleCache.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleCache.cpp new file mode 100644 index 0000000000..06cc298470 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleCache.cpp @@ -0,0 +1,183 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleCache_p.h" + +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +#include "ctkCommandLineModulesCoreExport.h" +CTK_CMDLINEMODULECORE_EXPORT int qHash(const QUrl& url) +{ + return qHash(url.toString()); +} +#endif + +struct ctkCmdLineModuleCachePrivate +{ + QString CacheDir; + + QHash LocationToTimeStamp; + QHash LocationToXmlDescription; + + QMutex Mutex; + + void LoadTimeStamps() + { + QDirIterator dirIter(this->CacheDir, QStringList() << "*.timestamp", QDir::Files | QDir::Readable); + while(dirIter.hasNext()) + { + QFile timestampFile(dirIter.next()); + timestampFile.open(QIODevice::ReadOnly); + QUrl url = QUrl(timestampFile.readLine().trimmed().data()); + QByteArray timestamp = timestampFile.readLine(); + bool ok = false; + qint64 ts = timestamp.toLongLong(&ok); + if (ok && !url.isEmpty()) + { + this->LocationToTimeStamp[url] = ts; + } + } + } + + QString timeStampFileName(const QUrl& moduleLocation) const + { + return this->CacheDir + "/" + QString::number(qHash(moduleLocation)) + ".timestamp"; + } + + QString xmlFileName(const QUrl& moduleLocation) const + { + return this->CacheDir + "/" + QString::number(qHash(moduleLocation)) + ".xml"; + } +}; + +ctkCmdLineModuleCache::ctkCmdLineModuleCache(const QString& cacheDir) + : d(new ctkCmdLineModuleCachePrivate) +{ + d->CacheDir = cacheDir; + d->LoadTimeStamps(); +} + +ctkCmdLineModuleCache::~ctkCmdLineModuleCache() +{ +} + +QString ctkCmdLineModuleCache::cacheDir() const +{ + QMutexLocker lock(&d->Mutex); + return d->CacheDir; +} + +QByteArray ctkCmdLineModuleCache::rawXmlDescription(const QUrl& moduleLocation) const +{ + QMutexLocker lock(&d->Mutex); + + if (d->LocationToXmlDescription.contains(moduleLocation)) + { + return d->LocationToXmlDescription[moduleLocation]; + } + // lazily load the XML description from the file system + QByteArray xml; + QString a = moduleLocation.toString(); + QString fn = d->xmlFileName(moduleLocation); + QFile xmlFile(d->xmlFileName(moduleLocation)); + if (xmlFile.exists() && xmlFile.open(QIODevice::ReadOnly)) + { + xml = xmlFile.readAll(); + xmlFile.close(); + } + d->LocationToXmlDescription[moduleLocation] = xml; + return xml; +} + +qint64 ctkCmdLineModuleCache::timeStamp(const QUrl& moduleLocation) const +{ + QMutexLocker lock(&d->Mutex); + if (d->LocationToTimeStamp.contains(moduleLocation)) + { + return d->LocationToTimeStamp[moduleLocation]; + } + return -1; +} + +void ctkCmdLineModuleCache::cacheXmlDescription(const QUrl& moduleLocation, qint64 timestamp, const QByteArray& xmlDescription) +{ + QFile timestampFile(d->timeStampFileName(moduleLocation)); + QFile xmlFile(d->xmlFileName(moduleLocation)); + timestampFile.remove(); + timestampFile.open(QIODevice::WriteOnly); + + QByteArray ba; + QTextStream str(&ba); + str << moduleLocation.toString() << '\n' << timestamp; + str.flush(); + if (timestampFile.write(ba) == -1) + { + timestampFile.close(); + timestampFile.remove(); + return; + } + timestampFile.close(); + + xmlFile.remove(); + if (!xmlDescription.isEmpty()) + { + xmlFile.open(QIODevice::WriteOnly); + if (xmlFile.write(xmlDescription) == -1) + { + timestampFile.remove(); + xmlFile.close(); + xmlFile.remove(); + return; + } + } + + { + QMutexLocker lock(&d->Mutex); + d->LocationToXmlDescription[moduleLocation] = xmlDescription; + d->LocationToTimeStamp[moduleLocation] = timestamp; + } +} + +void ctkCmdLineModuleCache::removeCacheEntry(const QUrl& moduleLocation) +{ + { + QMutexLocker lock(&d->Mutex); + d->LocationToTimeStamp.remove(moduleLocation); + d->LocationToXmlDescription.remove(moduleLocation); + } + + QFile timestampFile(d->timeStampFileName(moduleLocation)); + if (timestampFile.exists()) + { + timestampFile.remove(); + } + QFile xmlFile(d->xmlFileName(moduleLocation)); + if (xmlFile.exists()) + { + xmlFile.remove(); + } +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleCache_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleCache_p.h new file mode 100644 index 0000000000..ad895ba28f --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleCache_p.h @@ -0,0 +1,88 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULECACHE_H +#define CTKCMDLINEMODULECACHE_H + +#include + +struct ctkCmdLineModuleCachePrivate; + +class QUrl; + +/** + * \class ctkCmdLineModuleCache + * \brief Private non-exported class to contain a cache of + * XML descriptions and time-stamps. + * \ingroup CommandLineModulesCore_API + */ +class ctkCmdLineModuleCache +{ + +public: + + ctkCmdLineModuleCache(const QString& cacheDir); + ~ctkCmdLineModuleCache(); + + /** + * @brief Returns the directory containing the cached information. + * @return a directory path + */ + QString cacheDir() const; + + /** + * @brief Returns the cached XML associated with a module. + * @param moduleLocation QUrl representing the location, + * for example a file path for a local process. + * @return QByteArray the XML + */ + QByteArray rawXmlDescription(const QUrl& moduleLocation) const; + + /** + * @brief Returns the time stamp associated with a module. + * @param moduleLocation QUrl representing the location, + * for example a file path for a local process. + * @return time since epoch + */ + qint64 timeStamp(const QUrl& moduleLocation) const; + + /** + * @brief Adds a modules XML and timestamp to the cache. + * @param moduleLocation QUrl representing the location, + * for example a file path for a local process. + * @param timestamp the time + * @param xmlDescription the XML + */ + void cacheXmlDescription(const QUrl& moduleLocation, qint64 timestamp, const QByteArray& xmlDescription); + + /** + * @brief Removes an entry from the cache. + * @param moduleLocation QUrl representing the location, + * for example a file path for a local process. + */ + void removeCacheEntry(const QUrl& moduleLocation); + +private: + + QScopedPointer d; +}; + +#endif // CTKCMDLINEMODULECACHE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.cpp new file mode 100644 index 0000000000..3983b3a198 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.cpp @@ -0,0 +1,93 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleConcurrentHelpers.h" + +#include "ctkCmdLineModuleManager.h" +#include "ctkException.h" + +#include +#include + +//---------------------------------------------------------------------------- +ctkCmdLineModuleConcurrentRegister::ctkCmdLineModuleConcurrentRegister(ctkCmdLineModuleManager* manager, + bool debug) + : ModuleManager(manager), Debug(debug) +{} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference ctkCmdLineModuleConcurrentRegister::operator()(const QString& moduleLocation) +{ + return this->operator ()(QUrl::fromLocalFile(moduleLocation)); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference ctkCmdLineModuleConcurrentRegister::operator()(const QUrl& moduleUrl) +{ + try + { + return this->ModuleManager->registerModule(moduleUrl); + } + catch (const ctkException& e) + { + if (this->Debug) + { + qDebug() << e; + } + return ctkCmdLineModuleReference(); + } + catch (...) + { + if (this->Debug) + { + qDebug() << "Registering module" << moduleUrl << "failed with an unknown exception."; + } + return ctkCmdLineModuleReference(); + } +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleConcurrentUnRegister::ctkCmdLineModuleConcurrentUnRegister(ctkCmdLineModuleManager* manager) + : ModuleManager(manager) +{} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleConcurrentUnRegister::operator()(const QString& moduleLocation) +{ + return this->operator ()(QUrl::fromLocalFile(moduleLocation)); +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleConcurrentUnRegister::operator()(const QUrl& moduleUrl) +{ + return this->operator ()(this->ModuleManager->moduleReference(moduleUrl)); +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleConcurrentUnRegister::operator()(const ctkCmdLineModuleReference& moduleRef) +{ + if (moduleRef) + { + this->ModuleManager->unregisterModule(moduleRef); + return true; + } + return false; +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.h new file mode 100644 index 0000000000..39bd3025f5 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.h @@ -0,0 +1,77 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULECONCURRENTHELPERS_H +#define CTKCMDLINEMODULECONCURRENTHELPERS_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include "ctkCmdLineModuleReference.h" + +class ctkCmdLineModuleManager; + +/** + * \ingroup CommandLineModulesCore_API + * + * \brief A function object for concurrently adding modules. + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleConcurrentRegister +{ + +public: + + typedef ctkCmdLineModuleReference result_type; + + ctkCmdLineModuleConcurrentRegister(ctkCmdLineModuleManager* manager, bool debug = false); + ctkCmdLineModuleReference operator()(const QString& moduleLocation); + ctkCmdLineModuleReference operator()(const QUrl& moduleUrl); + +private: + + ctkCmdLineModuleManager* ModuleManager; + bool Debug; +}; + +/** + * \ingroup CommandLineModulesCore_API + * + * \brief A function object for concurrently removing modules. + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleConcurrentUnRegister +{ + +public: + + typedef bool result_type; + + ctkCmdLineModuleConcurrentUnRegister(ctkCmdLineModuleManager* manager); + + bool operator()(const QString& moduleLocation); + bool operator()(const QUrl& moduleUrl); + bool operator()(const ctkCmdLineModuleReference& moduleRef); + +private: + + ctkCmdLineModuleManager* ModuleManager; +}; + + +#endif // CTKCMDLINEMODULECONCURRENTHELPERS_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.cpp new file mode 100644 index 0000000000..95a95427f0 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.cpp @@ -0,0 +1,180 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleDefaultPathBuilder.h" +#include +#include +#include +#include + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleDefaultPathBuilderPrivate +{ +public: + ctkCmdLineModuleDefaultPathBuilderPrivate(); + ~ctkCmdLineModuleDefaultPathBuilderPrivate(); + QStringList build() const; + + void setLoadFromHomeDir(bool doLoad); + void setLoadFromCurrentDir(bool doLoad); + void setLoadFromApplicationDir(bool doLoad); + void setLoadFromCtkModuleLoadPath(bool doLoad); + + bool LoadFromHomeDir; + bool LoadFromCurrentDir; + bool LoadFromApplicationDir; + bool LoadFromCtkModuleLoadPath; + +}; + +//----------------------------------------------------------------------------- +// ctkCmdLineModuleDefaultPathBuilderPrivate methods + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDefaultPathBuilderPrivate::ctkCmdLineModuleDefaultPathBuilderPrivate() +: LoadFromHomeDir(false) +, LoadFromCurrentDir(false) +, LoadFromApplicationDir(false) +, LoadFromCtkModuleLoadPath(false) +{ + +} + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDefaultPathBuilderPrivate::~ctkCmdLineModuleDefaultPathBuilderPrivate() +{ + +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilderPrivate::setLoadFromHomeDir(bool doLoad) +{ + LoadFromHomeDir = doLoad; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilderPrivate::setLoadFromCurrentDir(bool doLoad) +{ + LoadFromCurrentDir = doLoad; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilderPrivate::setLoadFromApplicationDir(bool doLoad) +{ + LoadFromApplicationDir = doLoad; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilderPrivate::setLoadFromCtkModuleLoadPath(bool doLoad) +{ + LoadFromCtkModuleLoadPath = doLoad; +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDefaultPathBuilderPrivate::build() const +{ + QStringList result; + + QString suffix = "cli-modules"; + + if (LoadFromCtkModuleLoadPath) + { + char *ctkModuleLoadPath = getenv("CTK_MODULE_LOAD_PATH"); + if (ctkModuleLoadPath != NULL) + { + QDir dir = QDir(QString(ctkModuleLoadPath)); + if (dir.exists()) + { + result << dir.canonicalPath(); + } + } + } + + if (LoadFromHomeDir) + { + if (QDir::home().exists()) + { + result << QDir::homePath(); + result << QDir::homePath() + QDir::separator() + suffix; + } + } + + if (LoadFromCurrentDir) + { + if (QDir::current().exists()) + { + result << QDir::currentPath(); + result << QDir::currentPath() + QDir::separator() + suffix; + } + } + + if (LoadFromApplicationDir) + { + result << QCoreApplication::applicationDirPath(); + result << QCoreApplication::applicationDirPath() + QDir::separator() + suffix; + } + + return result; +} + +//----------------------------------------------------------------------------- +// ctkCmdLineModuleDefaultPathBuilder methods + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDefaultPathBuilder::ctkCmdLineModuleDefaultPathBuilder() + : d(new ctkCmdLineModuleDefaultPathBuilderPrivate) +{ +} + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDefaultPathBuilder::~ctkCmdLineModuleDefaultPathBuilder() +{ +} + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDefaultPathBuilder::build() const +{ + return d->build(); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilder::setLoadFromHomeDir(bool doLoad) +{ + d->setLoadFromHomeDir(doLoad); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilder::setLoadFromCurrentDir(bool doLoad) +{ + d->setLoadFromCurrentDir(doLoad); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilder::setLoadFromApplicationDir(bool doLoad) +{ + d->setLoadFromApplicationDir(doLoad); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDefaultPathBuilder::setLoadFromCtkModuleLoadPath(bool doLoad) +{ + d->setLoadFromCtkModuleLoadPath(doLoad); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.h new file mode 100644 index 0000000000..cb1c6041a7 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDefaultPathBuilder.h @@ -0,0 +1,96 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef __ctkCmdLineModuleDefaultPathBuilder_h +#define __ctkCmdLineModuleDefaultPathBuilder_h + +#include "ctkCommandLineModulesCoreExport.h" +#include +#include + +struct ctkCmdLineModuleDefaultPathBuilderPrivate; + +/** + * \class ctkCmdLineModuleDefaultPathBuilder + * \brief Builds up a list of directory paths to search for command + * line modules. + * \ingroup CommandLineModulesCore_API + * \author m.clarkson@ucl.ac.uk + * + * Implements the following basic strategy, depending on which boolean + * flags are on: By default they are all off, as directory scanning is + * often time consuming. + * + *
+ * 1. CTK_MODULE_LOAD_PATH environment variable
+ * 2. Home directory
+ * 3. Home directory / cli-modules
+ * 4. Current working directory
+ * 5. Current working directory / cli-modules
+ * 6. Application directory
+ * 7. Application directory / cli-modules
+ * 
+ */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleDefaultPathBuilder +{ + +public: + + ctkCmdLineModuleDefaultPathBuilder(); + ~ctkCmdLineModuleDefaultPathBuilder(); + + /** + * @brief Instruct the builder to include the users + * home directory and sub-folder cli-modules. + */ + virtual void setLoadFromHomeDir(bool doLoad); + + /** + * @brief Instruct the builder to include the current + * running directory and sub-folder cli-modules. + */ + virtual void setLoadFromCurrentDir(bool doLoad); + + /** + * @brief Instruct the builder to include the application + * installation directory and sub-folder cli-modules. + */ + virtual void setLoadFromApplicationDir(bool doLoad); + + /** + * @brief Instruct the builder to include the path denoted + * by the environment variable CTK_MODULE_LOAD_PATH. + */ + virtual void setLoadFromCtkModuleLoadPath(bool doLoad); + + /** + * @brief Builds the list of paths to search and returns them + * as QStringList + * @return a QStringList of directory path names + */ + virtual QStringList build() const; + +private: + + QScopedPointer d; + Q_DISABLE_COPY(ctkCmdLineModuleDefaultPathBuilder) +}; + +#endif diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.cpp index 606eafdd4d..9218325123 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.cpp @@ -19,7 +19,7 @@ limitations under the License. =============================================================================*/ #include "ctkCmdLineModuleDescription.h" -#include "ctkCmdLineModuleDescriptionPrivate.h" +#include "ctkCmdLineModuleDescription_p.h" #include "ctkCmdLineModuleParameter.h" #include "ctkCmdLineModuleParameterGroup.h" @@ -65,6 +65,14 @@ QString ctkCmdLineModuleDescription::title() const return d->Title; } + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleDescription::categoryDotTitle() const +{ + return this->category() + "." + this->title(); +} + + //---------------------------------------------------------------------------- QString ctkCmdLineModuleDescription::description() const { diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.h index 6d8bdef16c..975097eae3 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription.h @@ -35,12 +35,13 @@ class ctkCmdLineModuleParameterGroup; class ctkCmdLineModuleParameter; /** - * \ingroup CommandLineModulesCore - * - * Description of the parameters of a command line module. + * \class ctkCmdLineModuleDescription + * \brief Description of the parameters of a command line module. + * \ingroup CommandLineModulesCore_API * * The parameters can be used for automated GUI generation or execution - * of the module. + * of the module, and are directly related to the XML description used to + * describe the command line module parameters. */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleDescription { @@ -54,37 +55,86 @@ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleDescription static ctkCmdLineModuleDescription parse(QIODevice* input); + /** + * @brief Returns the category, derived from the \code \endcode tag. + */ QString category() const; + /** + * @brief Returns the title, derived from the \code \endcode tag. + */ QString title() const; + /** + * @brief Helper method that returns the category followed by a dot followed by the title. + */ + QString categoryDotTitle() const; + + /** + * @brief Returns the title, derived from the \code <description> \endcode tag. + */ QString description() const; + /** + * @brief Returns the title, derived from the \code <version> \endcode tag. + */ QString version() const; + /** + * @brief Returns the title, derived from the \code <documentation-url> \endcode tag. + */ QString documentationURL() const; + /** + * @brief Returns the title, derived from the \code <license> \endcode tag. + */ QString license() const; + /** + * @brief Returns the title, derived from the \code <acknowledgements> \endcode tag. + */ QString acknowledgements() const; + /** + * @brief Returns the title, derived from the \code <contributor> \endcode tag. + */ QString contributor() const; + /** + * @brief Should return a QIcon, but does not appear to be supported yet. + */ QIcon logo() const; + /** + * \brief The XML can define groups of parameters, so this method returns + * a QList of ctkCmdLineModuleParameterGroup to handle groups. + */ QList<ctkCmdLineModuleParameterGroup> parameterGroups() const; + /** + * @brief Searches the list of parameters, checking if a parameter has the given name. + * @param name the name of the parameter, derived from the \code <name> \endcode tag. + * @return true if this module has a parameter called name and false otherwise + */ bool hasParameter(const QString& name) const; + /** + * @brief Returns the parameter specified by name + * @param name the name of the parameter, derived from the \code <name> \endcode tag. + * @return the parameter + * @throw ctkInvalidArgumentException if this module does not have this parameter. + */ ctkCmdLineModuleParameter parameter(const QString& name) const; - // Does the module have any simple (primitive) return types? + /** + * @brief Does the module have any simple (primitive) return types? + */ bool hasReturnParameters() const; private: friend class ctkCmdLineModuleXmlParser; - friend class ctkCmdLineModuleReferencePrivate; + friend struct ctkCmdLineModuleReferencePrivate; ctkCmdLineModuleDescription(); diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDescriptionPrivate.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleDescription_p.h similarity index 100% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleDescriptionPrivate.h rename to Libs/CommandLineModules/Core/ctkCmdLineModuleDescription_p.h diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.cpp new file mode 100644 index 0000000000..5575d68646 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.cpp @@ -0,0 +1,467 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleDirectoryWatcher.h" +#include "ctkCmdLineModuleDirectoryWatcher_p.h" +#include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleConcurrentHelpers.h" +#include "ctkException.h" + +#include <QObject> +#include <QFileSystemWatcher> +#include <QDir> +#include <QFile> +#include <QFileInfo> +#include <QUrl> +#include <QDebug> +#include <QtConcurrentMap> + +#include <iostream> + + +//----------------------------------------------------------------------------- +// ctkCmdLineModuleDirectoryWatcher methods + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDirectoryWatcher::ctkCmdLineModuleDirectoryWatcher(ctkCmdLineModuleManager* moduleManager) + : d(new ctkCmdLineModuleDirectoryWatcherPrivate(moduleManager)) +{ + Q_ASSERT(moduleManager); +} + + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDirectoryWatcher::~ctkCmdLineModuleDirectoryWatcher() +{ + +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcher::setDebug(bool debug) +{ + d->setDebug(debug); +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcher::setDirectories(const QStringList& directories) +{ + d->setDirectories(directories); +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcher::directories() const +{ + return d->directories(); +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcher::setAdditionalModules(const QStringList& modules) +{ + d->setAdditionalModules(modules); +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcher::additionalModules() const +{ + return d->additionalModules(); +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcher::commandLineModules() const +{ + return d->commandLineModules(); +} + + +//----------------------------------------------------------------------------- +// ctkCmdLineModuleDirectoryWatcherPrivate methods + + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDirectoryWatcherPrivate::ctkCmdLineModuleDirectoryWatcherPrivate(ctkCmdLineModuleManager* moduleManager) +: ModuleManager(moduleManager) +, FileSystemWatcher(NULL) +, Debug(false) +{ + FileSystemWatcher = new QFileSystemWatcher(); + + connect(this->FileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onFileChanged(QString))); + connect(this->FileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(onDirectoryChanged(QString))); +} + + +//----------------------------------------------------------------------------- +ctkCmdLineModuleDirectoryWatcherPrivate::~ctkCmdLineModuleDirectoryWatcherPrivate() +{ + delete this->FileSystemWatcher; +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcherPrivate::setDebug(bool debug) +{ + this->Debug = 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(); +} + + +//----------------------------------------------------------------------------- +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(); +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcherPrivate::additionalModules() const +{ + // 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 currentCommandLineModules = this->commandLineModules(); + + if (currentDirectories.size() > 0) + { + this->FileSystemWatcher->removePaths(currentDirectories); + } + if (currentCommandLineModules.size() > 0) + { + this->FileSystemWatcher->removePaths(currentCommandLineModules); + } + + if (directories.size() > 0) + { + this->FileSystemWatcher->addPaths(directories); + } + if (files.size() > 0) + { + this->FileSystemWatcher->addPaths(files); + } + + if (this->Debug) + { + qDebug() << "ctkCmdLineModuleDirectoryWatcherPrivate::updateWatchedPaths watching directories:\n" << directories << "\n and files:\n" << files; + } +} + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcherPrivate::filterInvalidDirectories(const QStringList& directories) const +{ + QStringList result; + + QString path; + foreach (path, directories) + { + if (!path.isNull() && !path.isEmpty() && !path.trimmed().isEmpty()) + { + QDir dir = QDir(path); + if (dir.exists()) + { + result << dir.absolutePath(); + } + } + } + + return result; +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcherPrivate::filterFilesNotInCurrentDirectories(const QStringList& filenames) const +{ + QStringList currentDirectories = this->directories(); + QStringList filteredFileNames; + + for (int i = 0; i < filenames.size(); i++) + { + QFileInfo fileInfo(filenames[i]); + + if (fileInfo.exists() && !(currentDirectories.contains(fileInfo.absolutePath()))) + { + filteredFileNames << fileInfo.absoluteFilePath(); + } + } + return filteredFileNames; +} + + +//----------------------------------------------------------------------------- +QStringList ctkCmdLineModuleDirectoryWatcherPrivate::getExecutablesInDirectory(const QString& path) const +{ + QStringList result; + + QString executable; + QFileInfo executableFileInfo; + + QDir dir = QDir(path); + if (dir.exists()) + { + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::Executable); + QFileInfoList executablesFileInfoList = dir.entryInfoList(); + + foreach (executableFileInfo, executablesFileInfoList) + { + executable = executableFileInfo.absoluteFilePath(); + result << executable; + } + } + + return result; +} + + +//----------------------------------------------------------------------------- +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 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. + + QString path; + QStringList currentlyWatchedDirectories = this->directories(); + + QStringList modulesToUnload; + QStringList modulesToLoad; + + // First remove modules from current directories that are no longer in the requested "directories" list. + foreach (path, currentlyWatchedDirectories) + { + if (!directories.contains(path)) + { + QStringList currentlyWatchedFiles = this->extractCurrentlyWatchedFilenamesInDirectory(path); + + QString filename; + foreach (filename, currentlyWatchedFiles) + { + modulesToUnload << filename; + } + } + } + + // Now for each requested directory. + foreach (path, directories) + { + // Existing folder. + if (currentlyWatchedDirectories.contains(path)) + { + QStringList currentlyWatchedFiles = this->extractCurrentlyWatchedFilenamesInDirectory(path); + QStringList executablesInDirectory = this->getExecutablesInDirectory(path); + + QString executable; + foreach (executable, currentlyWatchedFiles) + { + if (!executablesInDirectory.contains(executable)) + { + modulesToUnload << executable; + } + } + + foreach(executable, executablesInDirectory) + { + if (!currentlyWatchedFiles.contains(executable)) + { + modulesToLoad << executable; + } + } + } + else + { + // New folder + QStringList executables = this->getExecutablesInDirectory(path); + + QString executable; + foreach (executable, executables) + { + modulesToLoad << executable; + } + } + } + + this->unloadModules(modulesToUnload); + this->loadModules(modulesToLoad); +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcherPrivate::updateModules(const QString &directory) +{ + // 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(); + if (!currentlyWatchedDirectories.contains(directory)) + { + currentlyWatchedDirectories << directory; + } + this->setModules(currentlyWatchedDirectories); + this->updateWatchedPaths(currentlyWatchedDirectories, this->MapFileNameToReference.keys()); +} + + +//----------------------------------------------------------------------------- +QList<ctkCmdLineModuleReference> ctkCmdLineModuleDirectoryWatcherPrivate::loadModules(const QStringList& executables) +{ + QList<ctkCmdLineModuleReference> refs = QtConcurrent::blockingMapped(executables, + ctkCmdLineModuleConcurrentRegister(this->ModuleManager, this->Debug)); + + for (int i = 0; i < executables.size(); ++i) + { + if (refs[i]) + { + this->MapFileNameToReference[executables[i]] = refs[i]; + } + } + return refs; +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcherPrivate::unloadModules(const QStringList& executables) +{ + QtConcurrent::blockingMapped(executables, ctkCmdLineModuleConcurrentUnRegister(this->ModuleManager)); + foreach(QString executable, executables) + { + this->MapFileNameToReference.remove(executable); + } +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcherPrivate::onFileChanged(const QString& path) +{ + ctkCmdLineModuleReference ref = this->loadModules(QStringList() << path).front(); + if (ref) + { + if (this->Debug) qDebug() << "Reloaded " << path; + } + else + { + if (this->Debug) qDebug() << "ctkCmdLineModuleDirectoryWatcherPrivate::onFileChanged(" << path << "): failed to load module"; + } +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleDirectoryWatcherPrivate::onDirectoryChanged(const QString &path) +{ + QStringList directories; + directories << path; + + QStringList validDirectories = this->filterInvalidDirectories(directories); + + if (validDirectories.size() > 0) + { + updateModules(path); + + if (this->Debug) qDebug() << "Reloaded modules in" << path; + } + else + { + if (this->Debug) qDebug() << "ctkCmdLineModuleDirectoryWatcherPrivate::onDirectoryChanged(" << path << "): failed to load modules, as path invalid."; + } +} + + diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.h new file mode 100644 index 0000000000..66033288a7 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher.h @@ -0,0 +1,118 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef __ctkCmdLineModuleDirectoryWatcher_h +#define __ctkCmdLineModuleDirectoryWatcher_h + +#include <ctkCommandLineModulesCoreExport.h> + +#include "ctkCmdLineModuleReference.h" + +#include <QObject> +#include <QScopedPointer> + +class ctkCmdLineModuleManager; +class ctkCmdLineModuleDirectoryWatcherPrivate; + +/** + * \class ctkCmdLineModuleDirectoryWatcher + * \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 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 +{ + Q_OBJECT + +public: + + ctkCmdLineModuleDirectoryWatcher(ctkCmdLineModuleManager* moduleManager); + virtual ~ctkCmdLineModuleDirectoryWatcher(); + + /** + * \brief Set the watcher into debug mode, for more output. + * \param debug if true, you get more output, otherwise, less output. + */ + void setDebug(bool debug); + + /** + * \brief Set the directories to be watched. + * \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); + + /** + * \brief Returns the list of directories currently being watched. + */ + QStringList directories() const; + + /** + * \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 commandLineModules() const; + +private: + + QScopedPointer<ctkCmdLineModuleDirectoryWatcherPrivate> d; + Q_DISABLE_COPY(ctkCmdLineModuleDirectoryWatcher) +}; + +#endif // __ctkCmdLineModuleDirectoryWatcher_h diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher_p.h new file mode 100644 index 0000000000..8a360efb64 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleDirectoryWatcher_p.h @@ -0,0 +1,176 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef __ctkCmdLineModuleDirectoryWatcherPrivate_h +#define __ctkCmdLineModuleDirectoryWatcherPrivate_h + +#include <QHash> +#include <QString> +#include <QStringList> +#include <QFileInfoList> + +#include "ctkCmdLineModuleReference.h" +#include "ctkCmdLineModuleDirectoryWatcher.h" + +class QFileSystemWatcher; + +/** + * \class ctkCmdLineModuleDirectoryWatcherPrivate + * \brief Private implementation class implementing directory/file watching to + * load new modules into a ctkCmdLineModuleManager. + * + * \ingroup CommandLineModulesCore_API + * \author m.clarkson@ucl.ac.uk + */ +class ctkCmdLineModuleDirectoryWatcherPrivate : public QObject +{ + + Q_OBJECT + +public: + + ctkCmdLineModuleDirectoryWatcherPrivate(ctkCmdLineModuleManager* ModuleManager); + virtual ~ctkCmdLineModuleDirectoryWatcherPrivate(); + + /** + * \see ctkCmdLineModuleDirectoryWatcher::setDebug + */ + void setDebug(bool debug); + + /** + * \see ctkCmdLineModuleDirectoryWatcher::setDirectories + */ + void setDirectories(const QStringList& directories); + + /** + * \see ctkCmdLineModuleDirectoryWatcher::directories + */ + QStringList directories() const; + + /** + * \see ctkCmdLineModuleDirectoryWatcher::setAdditionalModules + */ + void setAdditionalModules(const QStringList& files); + + /** + * \see ctkCmdLineModuleDirectoryWatcher::additionalModules + */ + QStringList additionalModules() const; + + /** + * \see ctkCmdLineModuleDirectoryWatcher::commandLineModules + */ + QStringList commandLineModules() const; + +public Q_SLOTS: + + /** + * \brief We connect QFileSystemWatcher to here. + */ + void onFileChanged(const QString& path); + + /** + * \brief We connect QFileSystemWatcher to here. + */ + void onDirectoryChanged(const QString &path); + +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 + */ + void updateWatchedPaths(const QStringList& directories, const QStringList& files); + + /** + * \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 valid directories, denoted by their absolute path. + */ + QStringList filterInvalidDirectories(const QStringList& directories) const; + + /** + * \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 filterFilesNotInCurrentDirectories(const QStringList& filenames) const; + + /** + * \brief Returns a list of executable files (not necessarily valid command line clients) in a directory. + * + * \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 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 setModules(const QStringList &directories); + + /** + * \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 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); + + QHash<QString, ctkCmdLineModuleReference> MapFileNameToReference; + ctkCmdLineModuleManager* ModuleManager; + QFileSystemWatcher* FileSystemWatcher; + QStringList AdditionalModules; + bool Debug; +}; + +#endif + diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.cpp new file mode 100644 index 0000000000..7cf7210daa --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.cpp @@ -0,0 +1,210 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFrontend.h" + +#include "ctkCmdLineModuleDescription.h" +#include "ctkCmdLineModuleParameter.h" +#include "ctkCmdLineModuleParameterGroup.h" +#include "ctkCmdLineModuleReference.h" +#include "ctkCmdLineModuleFuture.h" +#include "ctkException.h" + +#include <QVariant> +#include <QUrl> +#include <QFutureWatcher> + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleFrontendPrivate +{ + ctkCmdLineModuleFrontendPrivate(const ctkCmdLineModuleReference& moduleRef, ctkCmdLineModuleFrontend* q) + : q(q) + , ModuleReference(moduleRef) + { + } + + void _q_resultReadyAt(int index) + { + q->resultReady(Future.resultAt(index)); + } + + ctkCmdLineModuleFrontend* q; + + ctkCmdLineModuleReference ModuleReference; + + QList<QString> ParameterNames; + + ctkCmdLineModuleFuture Future; + QFutureWatcher<ctkCmdLineModuleResult> FutureWatcher; +}; + + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontend::ctkCmdLineModuleFrontend(const ctkCmdLineModuleReference& moduleRef) + : d(new ctkCmdLineModuleFrontendPrivate(moduleRef, this)) +{ + connect(&d->FutureWatcher, SIGNAL(resultReadyAt(int)), SLOT(_q_resultReadyAt(int))); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontend::setFuture(const ctkCmdLineModuleFuture &future) +{ + d->Future = future; + // Reset all simple output (return) parameter values + foreach(const ctkCmdLineModuleParameter& param, this->parameters(QString(), Output)) + { + if (param.index() == 1000) + { + this->setValue(param.name(), param.defaultValue()); + } + } + + d->FutureWatcher.setFuture(d->Future); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontend::~ctkCmdLineModuleFrontend() +{ +} + +QList<QString> ctkCmdLineModuleFrontend::parameterNames() const +{ + if (!d->ParameterNames.isEmpty()) return d->ParameterNames; + + foreach (ctkCmdLineModuleParameterGroup paramGroup, + moduleReference().description().parameterGroups()) + { + foreach (ctkCmdLineModuleParameter param, paramGroup.parameters()) + { + d->ParameterNames.push_back(param.name()); + } + } + return d->ParameterNames; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference ctkCmdLineModuleFrontend::moduleReference() const +{ + return d->ModuleReference; +} + +//---------------------------------------------------------------------------- +QUrl ctkCmdLineModuleFrontend::location() const +{ + return d->ModuleReference.location(); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleFrontend::future() const +{ + return d->Future; +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleFrontend::isRunning() const +{ + return d->Future.isRunning(); +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleFrontend::isPaused() const +{ + return d->Future.isPaused(); +} + +//---------------------------------------------------------------------------- +QHash<QString, QVariant> ctkCmdLineModuleFrontend::values() const +{ + QHash<QString,QVariant> result; + foreach(QString parameterName, parameterNames()) + { + result.insert(parameterName, value(parameterName)); + } + return result; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontend::setValues(const QHash<QString, QVariant> &values) +{ + QHashIterator<QString,QVariant> iter(values); + while(iter.hasNext()) + { + iter.next(); + setValue(iter.key(), iter.value()); + } +} + +//---------------------------------------------------------------------------- +QList<ctkCmdLineModuleParameter> ctkCmdLineModuleFrontend::parameters(const QString &type, ParameterFilters filters) +{ + ctkCmdLineModuleDescription description = this->moduleReference().description(); + QList<ctkCmdLineModuleParameter> parameters; + foreach(ctkCmdLineModuleParameterGroup group, description.parameterGroups()) + { + foreach(ctkCmdLineModuleParameter param, group.parameters()) + { + if (filters.testFlag(Input) && + (param.channel().isEmpty() || param.channel().compare("input", Qt::CaseInsensitive) == 0)) + { + if (type.isEmpty() || param.tag().compare(type, Qt::CaseInsensitive) == 0) + { + parameters << param; + } + } + if (filters.testFlag(Output) && param.channel().compare("output", Qt::CaseInsensitive) == 0) + { + if (type.isEmpty() || param.tag().compare(type, Qt::CaseInsensitive) == 0) + { + parameters << param; + } + } + } + } + return parameters; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontend::resetValues() +{ + foreach(ctkCmdLineModuleParameter param, this->parameters()) + { + this->setValue(param.name(), param.defaultValue()); + } +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontend::resultReady(const ctkCmdLineModuleResult &result) +{ + try + { + if (this->moduleReference().description().parameter(result.parameter()).channel() != "output") + { + qWarning() << "Module" << this->moduleReference().location() << "is reporting results for non-output parameter" + << result.parameter() << ". Report ignored."; + return; + } + this->setValue(result.parameter(), result.value()); + } + catch (const ctkInvalidArgumentException&) + {} +} + +#include "moc_ctkCmdLineModuleFrontend.h" diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.h new file mode 100644 index 0000000000..5eac7c91ec --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.h @@ -0,0 +1,277 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFRONTEND_H +#define CTKCMDLINEMODULEFRONTEND_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include <QObject> + +template<class K, class V> class QHash; +class QUrl; + +class ctkCmdLineModuleFuture; +class ctkCmdLineModuleReference; +class ctkCmdLineModuleParameter; +class ctkCmdLineModuleResult; +struct ctkCmdLineModuleFrontendPrivate; + +/** + * \class ctkCmdLineModuleFrontend + * \brief Abstract base class for all front-end command + * line module implementations. + * \ingroup CommandLineModulesCore_API + * + * A module front-end represents a set of current parameter values for a specific + * module. A front-end instance is usually associated with a graphical user interface, + * accessible via guiHandle(). This allows users to interactively change parameter values + * of the module. + * + * \see ctkCmdLineModuleFrontendQtGui + * \see ctkCmdLineModuleFrontendQtWebKit + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleFrontend : public QObject +{ + Q_OBJECT + Q_ENUMS(ParamterValueRole) + +public: + + enum ParameterValueRole { + + /** + * Data returned using this role must not be of any type not supported by + * QVariant by default. For complex parameter types (like file, image, + * geometry, etc.) the data must be convertible to a QString pointing + * to a local resource. + * + * This role is usually used by backends for retrieving data and is mainly + * important for data which acts as a handle to the real data (e.g. a + * backend usually needs to get the absolute path to a local file for the + * current value of an input image parameter, instead of the image label + * displayed in a GUI). + */ + LocalResourceRole = 0, + + /** + * Describes data suitable for displaying in a GUI. For many parameter types + * (e.g. scalar and vector parameters) data returned by this role will be + * the same as returned by the LocalResourceRole role. + **/ + DisplayRole = 1, + + /** + * This role can be used in custom frontends to return a QVariant + * containing for example an in-memory representation of a complex object. + * One can then either convert the in-memory representation to a local + * resource before running a module such that arbitrary backends relying on + * the LocalResourceRole role can process the data. Or one creates a custom + * backend which knows how to handle QVariants returned by this role. + */ + UserRole = 8 + }; + + enum ParameterFilter { + /** Parameters with channel = "input" */ + Input = 0x01, + /** Parameter with channel = "output" */ + Output = 0x02, + /** A convenience enum value combining Input and Output. */ + All = Input | Output + }; + Q_DECLARE_FLAGS(ParameterFilters, ParameterFilter) + + ~ctkCmdLineModuleFrontend(); + + /** + * @brief Returns the GUI representation. + * @return A GUI handle that can then be embeded in an application window for instance. + * + * The returned object is a handle to the real GUI toolkit specific object representing + * the user interface. For Qt based front-ends, the returned object is usually a QWidget + * instance pointing to the main container widget for the GUI. See the documentation + * of the front-end sub-class for specific information. + */ + virtual QObject* guiHandle() const = 0; + + /** + * @brief GUIs will need to be able to read parameters, + * here we retrieve by role. + * + * @return QVariant + * @see ParameterValueRole + */ + virtual QVariant value(const QString& parameter, + int role = LocalResourceRole) const = 0; + + /** + * @brief Set the value of a certain parameter. + * + * @param parameter The name of the parameter, as defined in the XML. + * @param value The value for that parameter. + * @param role The role for which to set the data. + * + * @see ParameterValueRole + */ + virtual void setValue(const QString& parameter, const QVariant& value, + int role = DisplayRole) = 0; + + /** + * @brief Return the ctkCmdLineModuleFuture, derived from QFuture to + * provide asynchronous processing and interaction with the running frontend. + * + * Note that the future returned by this method will be different after the + * frontend was started. Either use isRunning() to check wether this frontend + * is currently running or connect to the started() signal. + * + * @see ctkCmdLineModuleFuture + */ + virtual ctkCmdLineModuleFuture future() const; + + /** + * @brief Returns a QUrl to define the location of the module that is run. + * + * For a local process this may be the file location of the command + * line module. For other implementations, such as a web-service, + * this could be a web URL. + * + * @return QUrl A resource independent URL defining where the module is. + */ + QUrl location() const; + + /** + * @brief Returns a ctkCmdLineModuleReference value object that refers + * and provides access to the module. + * @return ctkCmdLineModuleReference + */ + ctkCmdLineModuleReference moduleReference() const; + + /** + * @brief Returns a list of all valid parameter names. + */ + virtual QList<QString> parameterNames() const; + + /** + * @brief Returns a map of parameter names and values. + */ + virtual QHash<QString,QVariant> values() const; + + /** + * @brief Enables the parameter values to be set. + */ + virtual void setValues(const QHash<QString,QVariant>& values); + + /** + * @brief Indicates if the currently associated ctkCmdLineModuleFuture object + * is in state "running". + * @return \c true if running and \c false otherwise. + */ + bool isRunning() const; + + /** + * @brief Indicates if the currently associated ctkCmdLineModuleFuture Object + * is in state "paused". + * @return \c true if paused and \c false otherwise. + */ + bool isPaused() const; + + // convenience methods + + /** + * @brief Useful method to return subsets of parameter objects, searhing + * by type for example "image" and filter for example "input"/"output". + * @param type The type of parameter, as defined in the XML element. + * @param filters flag to define whether we want input/output. + * @return QList of ctkCmdLineModuleParameter depending on type and filters. + * @see ParameterFilter + */ + QList<ctkCmdLineModuleParameter> parameters( + const QString& type = QString(), + ParameterFilters filters = All); + + void resetValues(); + +Q_SIGNALS: + + /** + * @brief This signal is emitted whenever a parameter value is changed by using + * the ctkCmdLineModuleFrontend class. + * @param parameter The parameter name. + * @param value The new parameter value. + * + * Please note that this signal is not emitted if a parameter value is + * changed in the generated GUI. + */ + void valueChanged(const QString& parameter, const QVariant& value); + + /** + * @brief This signal is emitted when the frontend is run. + * + * You can use this signal to get the ctkCmdLineModuleFuture instance + * from future() to interact with the running frontend. + */ + void started(); + +protected: + + /** + * @brief Constructor. + */ + ctkCmdLineModuleFrontend(const ctkCmdLineModuleReference& moduleRef); + +private Q_SLOTS: + + /** + * @brief Provides results as reported by the running module. + * @param result + * + * This method is called when a running module reports a new + * result. The default implementation updates the current value + * of the output parameter in the GUI with the reported value. + */ + virtual void resultReady(const ctkCmdLineModuleResult& result); + +private: + + /** + * @brief Sets the ctkCmdLineModuleFuture which effectively + * contains the backend that is run. + */ + void setFuture(const ctkCmdLineModuleFuture& future); + +private: + + Q_DISABLE_COPY(ctkCmdLineModuleFrontend) + + friend struct ctkCmdLineModuleFrontendPrivate; + friend class ctkCmdLineModuleManager; + friend class ctkCmdLineModulePrivate; + + Q_PRIVATE_SLOT(d, void _q_resultReadyAt(int)) + + QScopedPointer<ctkCmdLineModuleFrontendPrivate> d; + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ctkCmdLineModuleFrontend::ParameterFilters) + +#endif // CTKCMDLINEMODULEFRONTEND_H diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.cpp similarity index 75% rename from Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.cpp rename to Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.cpp index a9f25b3869..e96b51071f 100644 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceFactoryQtGui.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.cpp @@ -19,11 +19,10 @@ =============================================================================*/ -#include "ctkCmdLineModuleInstanceFactoryQtGui.h" -#include "ctkCmdLineModuleInstanceQtGui_p.h" +#include "ctkCmdLineModuleFrontendFactory.h" -ctkCmdLineModuleInstance* -ctkCmdLineModuleInstanceFactoryQtGui::create(const ctkCmdLineModuleReference& moduleRef) + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontendFactory::~ctkCmdLineModuleFrontendFactory() { - return new ctkCmdLineModuleInstanceQtGui(moduleRef); } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.h new file mode 100644 index 0000000000..4223e2804c --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFrontendFactory.h @@ -0,0 +1,64 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFRONTENDFACTORY_H +#define CTKCMDLINEMODULEFRONTENDFACTORY_H + +#include "ctkCommandLineModulesCoreExport.h" + +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleReference; + +/** + * \class ctkCmdLineModuleFrontendFactory + * \brief Factory class to create new front-ends. + * \ingroup CommandLineModulesCore_API + * + * Front-end implementors are advised to create and export a sub-class of + * this class to unify the creation process of front-ends. + * + * \see ctkCmdLineModuleFrontend + */ +struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleFrontendFactory +{ + virtual ~ctkCmdLineModuleFrontendFactory(); + + /** + * @brief Get the name of this factory. + * @return The factory name. + */ + virtual QString name() const = 0; + + /** + * @brief Get the description for this factory. + * @return A factory description. + */ + virtual QString description() const = 0; + + /** + * @brief Creates front-end instances. + * @param moduleRef The module reference for which to create a front-end. + * @return The created front-end or NULL if creation failed. + */ + virtual ctkCmdLineModuleFrontend* create(const ctkCmdLineModuleReference& moduleRef) = 0; +}; + +#endif // CTKCMDLINEMODULEFRONTENDFACTORY_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.cpp index 67a472e11d..337e4f9669 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.cpp @@ -1,97 +1,57 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ #include "ctkCmdLineModuleFuture.h" -struct ctkCmdLineModuleFutureInterfacePrivate +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture::ctkCmdLineModuleFuture() { - ctkCmdLineModuleFutureInterfacePrivate() - : refCount(1), _exitCode(0), _exitStatus(QProcess::NormalExit), - _processError(QProcess::UnknownError) - {} - - QAtomicInt refCount; - - int _exitCode; - QProcess::ExitStatus _exitStatus; - QProcess::ProcessError _processError; - QString _errorString; - QString _stdOut; - QString _stdErr; -}; - -ctkCmdLineModuleFutureInterface::QFutureInterface(State initialState) - : QFutureInterfaceBase(initialState), d(new ctkCmdLineModuleFutureInterfacePrivate) -{ } +} -ctkCmdLineModuleFutureInterface::QFutureInterface(const ctkCmdLineModuleFutureInterface& other) - : QFutureInterfaceBase(other), d(other.d) +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture::ctkCmdLineModuleFuture(ctkCmdLineModuleFutureInterface* p) + : QFuture<ctkCmdLineModuleResult>(p) { - d->refCount.ref(); } -ctkCmdLineModuleFutureInterface ctkCmdLineModuleFutureInterface::canceledResult() -{ return ctkCmdLineModuleFutureInterface(State(Started | Finished | Canceled)); } - -ctkCmdLineModuleFutureInterface& ctkCmdLineModuleFutureInterface::operator=(const ctkCmdLineModuleFutureInterface& other) +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFuture::readAllOutputData() const { - QFutureInterfaceBase::operator=(other); - other.d->refCount.ref(); - if(!d->refCount.deref()) delete d; - d = other.d; - return *this; + return d.outputData(); } -int ctkCmdLineModuleFutureInterface::exitCode() const -{ QMutexLocker lock(this->mutex()); return d->_exitCode; } - -void ctkCmdLineModuleFutureInterface::reportExitCode(int code) -{ QMutexLocker lock(this->mutex()); d->_exitCode = code; } - -QProcess::ExitStatus ctkCmdLineModuleFutureInterface::exitStatus() const -{ QMutexLocker lock(this->mutex()); return d->_exitStatus; } - -void ctkCmdLineModuleFutureInterface::reportExitStatus(QProcess::ExitStatus status) -{ QMutexLocker lock(this->mutex()); d->_exitStatus = status; } - -QProcess::ProcessError ctkCmdLineModuleFutureInterface::error() const -{ QMutexLocker lock(this->mutex()); return d->_processError; } - -void ctkCmdLineModuleFutureInterface::reportProcessError(QProcess::ProcessError procErr) -{ QMutexLocker lock(this->mutex()); d->_processError = procErr; } - -QString ctkCmdLineModuleFutureInterface::errorString() const -{ QMutexLocker lock(this->mutex()); return d->_errorString; } - -void ctkCmdLineModuleFutureInterface::reportErrorString(const QString& errorStr) -{ QMutexLocker lock(this->mutex()); d->_errorString = errorStr; } - -QString ctkCmdLineModuleFutureInterface::standardOutput() const -{ QMutexLocker lock(this->mutex()); return d->_stdOut; } - -void ctkCmdLineModuleFutureInterface::reportStandardOutput(const QString& stdOut) -{ QMutexLocker lock(this->mutex()); d->_stdOut = stdOut; } +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFuture::readAllErrorData() const +{ + return d.errorData(); +} -QString ctkCmdLineModuleFutureInterface::standardError() const -{ QMutexLocker lock(this->mutex()); return d->_stdErr; } +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleFuture::canCancel() const +{ + return d.canCancel(); +} -void ctkCmdLineModuleFutureInterface::reportStandardError(const QString& stdErr) -{ QMutexLocker lock(this->mutex()); d->_stdErr = stdErr; } +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleFuture::canPause() const +{ + return d.canPause(); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.h index 342691c728..a83e117892 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFuture.h @@ -1,93 +1,84 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ #ifndef CTKCMDLINEMODULEFUTURE_H #define CTKCMDLINEMODULEFUTURE_H -#include <ctkCommandLineModulesCoreExport.h> +#include "ctkCommandLineModulesCoreExport.h" -//#include <QFutureInterface> -//#include <QFutureWatcher> -#include <QProcess> +#include "ctkCmdLineModuleFutureInterface.h" + +#include <QFuture> /** - * \ingroup CommandLineModulesCore + * \class ctkCmdLineModuleFuture + * \brief QFuture sub-class for enhanced communication with running modules. + * \ingroup CommandLineModulesCore_API + * + * Please see the QFuture documentation of Qt for details. This sub-class provides + * additional query methods to check if a module can be paused and/or canceled and + * also provides the ability to retrieve the arbitrary output and error data + * from the module. + * + * \see ctkCmdLineModuleFutureWatcher */ -class ctkCmdLineModuleFuture +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleFuture : public QFuture<ctkCmdLineModuleResult> { - public: - ctkCmdLineModuleFuture() - : d(ctkCmdLineModuleFutureInterface::canceledResult()) - { } + ctkCmdLineModuleFuture(); - explicit ctkCmdLineModuleFuture(const ctkCmdLineModuleProcess& p) // internal - : d(*p) - { } + /** \cond */ + explicit ctkCmdLineModuleFuture(ctkCmdLineModuleFutureInterface* p); // internal + /** \endcond */ - ctkCmdLineModuleFuture(const ctkCmdLineModuleFuture &other) - : d(other.d) - { } + /** + * @brief Read all output data reported by the running module so far. + * @return Returns the reported output. + */ + QByteArray readAllOutputData() const; - ~ctkCmdLineModuleFuture() - { } + /** + * @brief Read all error data reported by the running module so far. + * @return Returns the reported error. + */ + QByteArray readAllErrorData() const; - ctkCmdLineModuleFuture& operator=(const ctkCmdLineModuleFuture& other); - bool operator==(const ctkCmdLineModuleFuture& other) const { return (d == other.d); } - bool operator!=(const ctkCmdLineModuleFuture& other) const { return (d != other.d); } + /** + * @brief Check if this module can be canceled via cancel(). + * @return \c true if this module can be canceled, \c false otherwise. + */ + bool canCancel() const; - void cancel() { d.cancel(); } - bool isCanceled() const { return d.isCanceled(); } + /** + * @brief Check if this module can be paused via pause() and similar QFuture methods. + * @return \c true if this module can be paused, \c false otherwise. + */ + bool canPause() const; - bool isStarted() const { return d.isStarted(); } - bool isFinished() const { return d.isFinished(); } - bool isRunning() const { return d.isRunning(); } - - int exitCode() const { return d.exitCode(); } - int exitStatus() const { return d.exitStatus(); } - QProcess::ProcessError error() const { return d.error(); } - QString errorString() const { return d.errorString(); } - - QString standardOutput() const { return d.standardOutput(); } - QString standardError() const { return d.standardError(); } - - int progressValue() const { return d.progressValue(); } - int progressMinimum() const { return d.progressMinimum(); } - int progressMaximum() const { return d.progressMaximum(); } - QString progressText() const { return d.progressText(); } - void waitForFinished() { d.waitForFinished(); } - -private: - - friend class ctkCmdLineModuleFutureWatcher; - - mutable ctkCmdLineModuleProcess d; }; - -inline ctkCmdLineModuleFuture& ctkCmdLineModuleFuture::operator=(const ctkCmdLineModuleFuture& other) +inline ctkCmdLineModuleFuture ctkCmdLineModuleFutureInterface::future() { - d = other.d; - return *this; + return ctkCmdLineModuleFuture(this); } #endif // CTKCMDLINEMODULEFUTURE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.cpp new file mode 100644 index 0000000000..81e829478e --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.cpp @@ -0,0 +1,201 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFutureInterface.h" +#include "ctkCmdLineModuleFutureInterface_p.h" + +const int ctkCmdLineModuleFutureCallOutEvent::TypeId = QEvent::registerEventType(); + +//---------------------------------------------------------------------------- +// ctkCmdLineModuleFutureInterfacePrivate + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFutureInterfacePrivate::ctkCmdLineModuleFutureInterfacePrivate(ctkCmdLineModuleFutureInterface* q) + : RefCount(1) + , CanCancel(false) + , CanPause(false) + , q(q) +{ +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFutureInterfacePrivate::sendCallOut(const ctkCmdLineModuleFutureCallOutEvent &callOutEvent) +{ + if (OutputConnections.isEmpty()) + { + return; + } + + for (int i = 0; i < OutputConnections.count(); ++i) + { + OutputConnections.at(i)->postCmdLineModuleCallOutEvent(callOutEvent); + } +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFutureInterfacePrivate::connectOutputInterface(ctkCmdLineModuleFutureCallOutInterface *iface) +{ + QMutexLocker locker(&Mutex); + + if (q->isStarted()) + { + if (!this->OutputData.isEmpty()) + { + iface->postCmdLineModuleCallOutEvent(ctkCmdLineModuleFutureCallOutEvent(ctkCmdLineModuleFutureCallOutEvent::OutputReady)); + } + if (!this->ErrorData.isEmpty()) + { + iface->postCmdLineModuleCallOutEvent(ctkCmdLineModuleFutureCallOutEvent(ctkCmdLineModuleFutureCallOutEvent::ErrorReady)); + } + } + + OutputConnections.append(iface); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFutureInterfacePrivate::disconnectOutputInterface(ctkCmdLineModuleFutureCallOutInterface *iface) +{ + QMutexLocker lock(&Mutex); + const int index = OutputConnections.indexOf(iface); + if (index == -1) + return; + OutputConnections.removeAt(index); + + iface->cmdLineModuleCallOutInterfaceDisconnected(); +} + +//---------------------------------------------------------------------------- +// QFutureInterface<ctkCmdLineModuleResult> + +//---------------------------------------------------------------------------- +QFutureInterface<ctkCmdLineModuleResult>::QFutureInterface(State initialState) + : QFutureInterfaceBase(initialState) + , d(new ctkCmdLineModuleFutureInterfacePrivate(this)) +{ +} + +//---------------------------------------------------------------------------- +QFutureInterface<ctkCmdLineModuleResult>::QFutureInterface(const QFutureInterface& other) + : QFutureInterfaceBase(other) + , d(other.d) +{ + d->RefCount.ref(); +} + +//---------------------------------------------------------------------------- +QFutureInterface<ctkCmdLineModuleResult>::~QFutureInterface() +{ + if (referenceCountIsOne()) + resultStore().clear(); + + if (!d->RefCount.deref()) + { + delete d; + } +} + +//---------------------------------------------------------------------------- +QFutureInterface<ctkCmdLineModuleResult> QFutureInterface<ctkCmdLineModuleResult>::canceledResult() +{ + return QFutureInterface(State(Started | Finished | Canceled)); +} + +//---------------------------------------------------------------------------- +QFutureInterface<ctkCmdLineModuleResult>& +QFutureInterface<ctkCmdLineModuleResult>::operator=(const QFutureInterface& other) +{ + if (referenceCountIsOne()) + resultStore().clear(); + + QFutureInterfaceBase::operator=(other); + + other.d->RefCount.ref(); + if (!d->RefCount.deref()) + delete d; + d = other.d; + + // update the q pointer in the private implementation + d->q = this; + + return *this; +} + +//---------------------------------------------------------------------------- +bool QFutureInterface<ctkCmdLineModuleResult>::canCancel() const +{ + return d->CanCancel; +} + +//---------------------------------------------------------------------------- +void QFutureInterface<ctkCmdLineModuleResult>::setCanCancel(bool canCancel) +{ + d->CanCancel = canCancel; +} + +//---------------------------------------------------------------------------- +bool QFutureInterface<ctkCmdLineModuleResult>::canPause() const +{ + return d->CanPause; +} + +//---------------------------------------------------------------------------- +void QFutureInterface<ctkCmdLineModuleResult>::setCanPause(bool canPause) +{ + d->CanPause = canPause; +} + +//---------------------------------------------------------------------------- +void QFutureInterface<ctkCmdLineModuleResult>::reportOutputData(const QByteArray& outputData) +{ + QMutexLocker l(&d->Mutex); + + if (isCanceled() || isFinished()) return; + d->OutputData.append(outputData); + d->sendCallOut(ctkCmdLineModuleFutureCallOutEvent(ctkCmdLineModuleFutureCallOutEvent::OutputReady)); +} + +//---------------------------------------------------------------------------- +void QFutureInterface<ctkCmdLineModuleResult>::reportErrorData(const QByteArray& errorData) +{ + QMutexLocker l(&d->Mutex); + + if (isCanceled() || isFinished()) return; + d->ErrorData.append(errorData); + d->sendCallOut(ctkCmdLineModuleFutureCallOutEvent(ctkCmdLineModuleFutureCallOutEvent::ErrorReady)); +} + +//---------------------------------------------------------------------------- +QByteArray QFutureInterface<ctkCmdLineModuleResult>::outputData(int position, int size) const +{ + QMutexLocker l(&d->Mutex); + if (size < 0) size = d->OutputData.size(); + if (size > d->OutputData.size() - position) size = d->OutputData.size() - position; + return QByteArray(d->OutputData.data() + position, size); +} + +//---------------------------------------------------------------------------- +QByteArray QFutureInterface<ctkCmdLineModuleResult>::errorData(int position, int size) const +{ + QMutexLocker l(&d->Mutex); + if (size < 0) size = d->ErrorData.size(); + if (size > d->ErrorData.size() - position) size = d->ErrorData.size() - position; + return QByteArray(d->ErrorData.data() + position, size); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.h new file mode 100644 index 0000000000..85270dd049 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface.h @@ -0,0 +1,177 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFUTUREINTERFACE_H +#define CTKCMDLINEMODULEFUTUREINTERFACE_H + +#include <ctkCommandLineModulesCoreExport.h> + +#include "ctkCmdLineModuleResult.h" + +#include <QFutureInterface> + +class ctkCmdLineModuleFuture; +class ctkCmdLineModuleFutureInterfacePrivate; + +/** + * \ingroup CommandLineModulesCore_API + * + * \brief A QFutureInterface specialization. + * + * This QFutureInterface must be used by custom backend implementations to retrieve + * a suitable QFuture object and to report state changes to it via this interface. + */ +template <> +class CTK_CMDLINEMODULECORE_EXPORT QFutureInterface<ctkCmdLineModuleResult> : public QFutureInterfaceBase +{ + +public: + + QFutureInterface(State initialState = NoState); + + QFutureInterface(const QFutureInterface &other); + + ~QFutureInterface(); + + static QFutureInterface canceledResult(); + + QFutureInterface& operator=(const QFutureInterface& other); + + inline ctkCmdLineModuleFuture future(); // implemented in ctkCmdLineModuleFuture.h + + bool canCancel() const; + void setCanCancel(bool canCancel); + bool canPause() const; + void setCanPause(bool canPause); + + inline void reportResult(const ctkCmdLineModuleResult *result, int index = -1); + inline void reportResult(const ctkCmdLineModuleResult &result, int index = -1); + inline void reportResults(const QVector<ctkCmdLineModuleResult> &results, int beginIndex = -1, int count = -1); + inline void reportFinished(const ctkCmdLineModuleResult *result = 0); + + void reportOutputData(const QByteArray& outputData); + void reportErrorData(const QByteArray& errorData); + + inline const ctkCmdLineModuleResult &resultReference(int index) const; + inline const ctkCmdLineModuleResult *resultPointer(int index) const; + inline QList<ctkCmdLineModuleResult> results(); + + QByteArray outputData(int position = 0, int size = -1) const; + QByteArray errorData(int position = 0, int size = -1) const; + +private: + + friend struct ctkCmdLineModuleFutureWatcherPrivate; + + QtConcurrent::ResultStore<ctkCmdLineModuleResult> &resultStore() + { return static_cast<QtConcurrent::ResultStore<ctkCmdLineModuleResult> &>(resultStoreBase()); } + const QtConcurrent::ResultStore<ctkCmdLineModuleResult> &resultStore() const + { return static_cast<const QtConcurrent::ResultStore<ctkCmdLineModuleResult> &>(resultStoreBase()); } + + ctkCmdLineModuleFutureInterfacePrivate* d; +}; + +inline void QFutureInterface<ctkCmdLineModuleResult>::reportResult(const ctkCmdLineModuleResult *result, int index) +{ + QMutexLocker locker(mutex()); + if (this->queryState(Canceled) || this->queryState(Finished)) { + return; + } + + QtConcurrent::ResultStore<ctkCmdLineModuleResult> &store = resultStore(); + + + if (store.filterMode()) { + const int resultCountBefore = store.count(); + store.addResult(index, result); + this->reportResultsReady(resultCountBefore, resultCountBefore + store.count()); + } else { + const int insertIndex = store.addResult(index, result); + this->reportResultsReady(insertIndex, insertIndex + 1); + } +} + +inline void QFutureInterface<ctkCmdLineModuleResult>::reportResult(const ctkCmdLineModuleResult &result, int index) +{ + reportResult(&result, index); +} + +inline void QFutureInterface<ctkCmdLineModuleResult>::reportResults(const QVector<ctkCmdLineModuleResult> &_results, int beginIndex, int count) +{ + QMutexLocker locker(mutex()); + if (this->queryState(Canceled) || this->queryState(Finished)) { + return; + } + + QtConcurrent::ResultStore<ctkCmdLineModuleResult> &store = resultStore(); + + if (store.filterMode()) { + const int resultCountBefore = store.count(); + store.addResults(beginIndex, &_results, count); + this->reportResultsReady(resultCountBefore, store.count()); + } else { + const int insertIndex = store.addResults(beginIndex, &_results, count); + this->reportResultsReady(insertIndex, insertIndex + _results.count()); + } +} + +inline void QFutureInterface<ctkCmdLineModuleResult>::reportFinished(const ctkCmdLineModuleResult *result) +{ + if (result) + reportResult(result); + QFutureInterfaceBase::reportFinished(); +} + +inline const ctkCmdLineModuleResult &QFutureInterface<ctkCmdLineModuleResult>::resultReference(int index) const +{ + QMutexLocker lock(mutex()); + return resultStore().resultAt(index).value(); +} + +inline const ctkCmdLineModuleResult *QFutureInterface<ctkCmdLineModuleResult>::resultPointer(int index) const +{ + QMutexLocker lock(mutex()); + return resultStore().resultAt(index).pointer(); +} + +inline QList<ctkCmdLineModuleResult> QFutureInterface<ctkCmdLineModuleResult>::results() +{ + if (this->isCanceled()) { + exceptionStore().throwPossibleException(); + return QList<ctkCmdLineModuleResult>(); + } + QFutureInterfaceBase::waitForResult(-1); + + QList<ctkCmdLineModuleResult> res; + QMutexLocker lock(mutex()); + + QtConcurrent::ResultIterator<ctkCmdLineModuleResult> it = resultStore().begin(); + while (it != resultStore().end()) { + res.append(it.value()); + ++it; + } + + return res; +} + +typedef QFutureInterface<ctkCmdLineModuleResult> ctkCmdLineModuleFutureInterface; + +#endif // CTKCMDLINEMODULEFUTUREINTERFACE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface_p.h new file mode 100644 index 0000000000..ad3cf37250 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureInterface_p.h @@ -0,0 +1,90 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFUTUREINTERFACE_P_H +#define CTKCMDLINEMODULEFUTUREINTERFACE_P_H + +#include <QEvent> +#include <QAtomicInt> +#include <QMutex> + +class ctkCmdLineModuleFutureCallOutEvent : public QEvent +{ +public: + + static const int TypeId; + + enum CallOutType { + OutputReady, + ErrorReady + }; + + ctkCmdLineModuleFutureCallOutEvent() + : QEvent(static_cast<QEvent::Type>(TypeId)) + , callOutType(CallOutType(0)) + {} + + ctkCmdLineModuleFutureCallOutEvent(CallOutType callOutType) + : QEvent(static_cast<QEvent::Type>(TypeId)) + , callOutType(callOutType) + {} + + ctkCmdLineModuleFutureCallOutEvent* clone() const + { + return new ctkCmdLineModuleFutureCallOutEvent(callOutType); + } + + CallOutType callOutType; +}; + +class ctkCmdLineModuleFutureCallOutInterface +{ +public: + virtual ~ctkCmdLineModuleFutureCallOutInterface() {} + virtual void postCmdLineModuleCallOutEvent(const ctkCmdLineModuleFutureCallOutEvent &) = 0; + virtual void cmdLineModuleCallOutInterfaceDisconnected() = 0; +}; + +class ctkCmdLineModuleFutureInterfacePrivate +{ +public: + + ctkCmdLineModuleFutureInterfacePrivate(ctkCmdLineModuleFutureInterface* q); + + QAtomicInt RefCount; + mutable QMutex Mutex; + + QList<ctkCmdLineModuleFutureCallOutInterface *> OutputConnections; + + bool CanCancel; + bool CanPause; + + QByteArray OutputData; + QByteArray ErrorData; + + ctkCmdLineModuleFutureInterface* q; + + void sendCallOut(const ctkCmdLineModuleFutureCallOutEvent &callOut); + void connectOutputInterface(ctkCmdLineModuleFutureCallOutInterface *iface); + void disconnectOutputInterface(ctkCmdLineModuleFutureCallOutInterface *iface); +}; + +#endif // CTKCMDLINEMODULEFUTUREINTERFACE_P_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.cpp new file mode 100644 index 0000000000..8d8b713233 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.cpp @@ -0,0 +1,216 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFutureWatcher.h" + +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleFutureInterface_p.h" + +#include <QThread> +#include <QCoreApplication> + +//---------------------------------------------------------------------------- +struct ctkCmdLineModuleFutureWatcherPrivate : public ctkCmdLineModuleFutureCallOutInterface +{ + ctkCmdLineModuleFutureWatcherPrivate(ctkCmdLineModuleFutureWatcher* q) + : q(q) + , pendingOutputReadyEvent(NULL) + , pendingErrorReadyEvent(NULL) + , outputPos(0) + , errorPos(0) + {} + + void connectOutputInterface() + { + q->futureInterface().d->connectOutputInterface(this); + } + + void disconnectOutputInterface(bool pendingAssignment = false) + { + if (pendingAssignment) + { + delete this->pendingOutputReadyEvent; + this->pendingOutputReadyEvent = NULL; + delete this->pendingErrorReadyEvent; + this->pendingErrorReadyEvent = NULL; + } + + q->futureInterface().d->disconnectOutputInterface(this); + } + + void postCmdLineModuleCallOutEvent(const ctkCmdLineModuleFutureCallOutEvent& callOutEvent) + { + QCoreApplication::postEvent(q, callOutEvent.clone()); + } + + void cmdLineModuleCallOutInterfaceDisconnected() + { + QCoreApplication::removePostedEvents(q, ctkCmdLineModuleFutureCallOutEvent::TypeId); + } + + void sendCmdLineModuleCallOutEvent(ctkCmdLineModuleFutureCallOutEvent* event) + { + if (q->futureInterface().isCanceled()) return; + + switch (event->callOutType) + { + case ctkCmdLineModuleFutureCallOutEvent::OutputReady: + emit q->outputDataReady(); + break; + case ctkCmdLineModuleFutureCallOutEvent::ErrorReady: + emit q->errorDataReady(); + break; + default: break; + } + } + + ctkCmdLineModuleFutureWatcher* q; + + ctkCmdLineModuleFuture Future; + + ctkCmdLineModuleFutureCallOutEvent* pendingOutputReadyEvent; + ctkCmdLineModuleFutureCallOutEvent* pendingErrorReadyEvent; + int outputPos; + int errorPos; +}; + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFutureWatcher::ctkCmdLineModuleFutureWatcher(QObject* parent) + : QFutureWatcher<ctkCmdLineModuleResult>(parent) + , d(new ctkCmdLineModuleFutureWatcherPrivate(this)) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFutureWatcher::~ctkCmdLineModuleFutureWatcher() +{ + disconnectOutputInterface(); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFutureWatcher::setFuture(const ctkCmdLineModuleFuture& future) +{ + if (d->Future == future) return; + + d->outputPos = 0; + d->errorPos = 0; + + d->disconnectOutputInterface(true); + d->Future = future; + QFutureWatcher<ctkCmdLineModuleResult>::setFuture(future); + d->connectOutputInterface(); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleFutureWatcher::future() const +{ + return d->Future; +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleFutureWatcher::event(QEvent *event) +{ + if (event->type() == ctkCmdLineModuleFutureCallOutEvent::TypeId) + { + ctkCmdLineModuleFutureCallOutEvent* callOutEvent = static_cast<ctkCmdLineModuleFutureCallOutEvent*>(event); + + if (futureInterface().isPaused()) + { + if (callOutEvent->callOutType == ctkCmdLineModuleFutureCallOutEvent::OutputReady && + d->pendingOutputReadyEvent == NULL) + { + d->pendingOutputReadyEvent = callOutEvent->clone(); + } + if (callOutEvent->callOutType == ctkCmdLineModuleFutureCallOutEvent::ErrorReady && + d->pendingErrorReadyEvent == NULL) + { + d->pendingErrorReadyEvent = callOutEvent->clone(); + } + return true; + } + + d->sendCmdLineModuleCallOutEvent(callOutEvent); + return true; + } + else if (event->type() == QEvent::FutureCallOut) + { + bool result = QFutureWatcher<ctkCmdLineModuleResult>::event(event); + + if (futureInterface().isRunning()) + { + // send all pending call outs + if (d->pendingOutputReadyEvent) + { + d->sendCmdLineModuleCallOutEvent(d->pendingOutputReadyEvent); + delete d->pendingOutputReadyEvent; + d->pendingOutputReadyEvent = NULL; + } + if (d->pendingErrorReadyEvent) + { + d->sendCmdLineModuleCallOutEvent(d->pendingErrorReadyEvent); + delete d->pendingErrorReadyEvent; + d->pendingErrorReadyEvent = NULL; + } + } + return result; + } + return QFutureWatcher<ctkCmdLineModuleResult>::event(event); +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFutureWatcher::readPendingOutputData() const +{ + QByteArray output = futureInterface().outputData(d->outputPos); + d->outputPos += output.size(); + return output; +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFutureWatcher::readPendingErrorData() const +{ + QByteArray errorOutput = futureInterface().errorData(d->errorPos); + d->errorPos += errorOutput.size(); + return errorOutput; +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFutureWatcher::readAllOutputData() const +{ + return d->Future.readAllOutputData(); +} + +//---------------------------------------------------------------------------- +QByteArray ctkCmdLineModuleFutureWatcher::readAllErrorData() const +{ + return d->Future.readAllErrorData(); +} + +//---------------------------------------------------------------------------- +const ctkCmdLineModuleFutureInterface& ctkCmdLineModuleFutureWatcher::futureInterface() const +{ + return d->Future.d; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFutureInterface& ctkCmdLineModuleFutureWatcher::futureInterface() +{ + return d->Future.d; +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.h new file mode 100644 index 0000000000..64381e95f1 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleFutureWatcher.h @@ -0,0 +1,98 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFUTUREWATCHER_H +#define CTKCMDLINEMODULEFUTUREWATCHER_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include "ctkCmdLineModuleResult.h" +#include "ctkCmdLineModuleFutureInterface.h" + +#include <QFutureWatcher> + +class ctkCmdLineModuleFuture; +struct ctkCmdLineModuleFutureWatcherPrivate; + +/** + * @ingroup CommandLineModulesCore_API + * + * @brief The ctkCmdLineModuleFutureWatcher class provides enhanced monitoring of a + * ctkCmdLineModuleFuture using signals and slots. + * + * This class enhances the standard QFutureWatcher class by adding the two signals + * outputDataReady() and errorDataReady(). These signals are fired whenever the watched + * future reports new output data (usually text written to the standard output channel) or + * new error data (usually text written to the standard error channel). + * + * Use readPendingOutputData() or readPendingErrorData() to get the newly added data. + * + * \warning While you could use a QFutureWatcher<ctkCmdLineModuleResult> instance directly (and + * provide a ctkCmdLineModuleFuture via QFutureWatcher<ctkCmdLineModuleResult>::setFuture(future) + * by virtue of "slicing") this is discouraged. The reason is that a member variable of type + * QFutureWatcher<ctkCmdLineModuleResult> will have a different size, depending on the inclusion + * of the ctkCmdLineModuleFutureInterface.h header (this header provides a specialization of the + * QFutureInterface template which adds a data member). This can lead to subtle heap corruptions. + * Since the code compiles with or without the ctkCmdLindeModuleFutureInterface.h inclusion, you + * should always use ctkCmdLineModuleFutureWatcher when working with ctkCmdLineModuleFuture objects + * to avoid runtime heap corruptions. + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleFutureWatcher : public QFutureWatcher<ctkCmdLineModuleResult> +{ + Q_OBJECT + +public: + + ctkCmdLineModuleFutureWatcher(QObject* parent = 0); + + ~ctkCmdLineModuleFutureWatcher(); + + void setFuture(const ctkCmdLineModuleFuture& future); + + ctkCmdLineModuleFuture future() const; + + bool event(QEvent* event); + + QByteArray readPendingOutputData() const; + QByteArray readPendingErrorData() const; + + QByteArray readAllOutputData() const; + QByteArray readAllErrorData() const; + +Q_SIGNALS: + + void outputDataReady(); + void errorDataReady(); + +private: + + friend struct ctkCmdLineModuleFutureWatcherPrivate; + + QScopedPointer<ctkCmdLineModuleFutureWatcherPrivate> d; + + const ctkCmdLineModuleFutureInterface& futureInterface() const; + ctkCmdLineModuleFutureInterface& futureInterface(); + + // not imlemented + void setFuture(const QFuture<ctkCmdLineModuleResult>&); +}; + +#endif // CTKCMDLINEMODULEFUTUREWATCHER_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.cpp deleted file mode 100644 index a3fd16953e..0000000000 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "ctkCmdLineModuleInstance.h" -#include "ctkCmdLineModuleDescription.h" -#include "ctkCmdLineModuleParameter.h" -#include "ctkCmdLineModuleParameterGroup.h" -#include "ctkCmdLineModuleReference.h" -#include "ctkCmdLineModuleProcess_p.h" - -#include "ctkException.h" - -#include <QStringList> -#include <QDebug> -#include <QProcess> - - -struct ctkCmdLineModuleInstancePrivate -{ - ctkCmdLineModuleInstancePrivate(ctkCmdLineModuleInstance* qq, - const ctkCmdLineModuleReference& moduleRef) - : ModuleReference(moduleRef), q(qq) - { - - } - - QString normalizeFlag(const QString& flag) - { - return flag.trimmed().remove(QRegExp("^-*")); - } - - ctkCmdLineModuleReference ModuleReference; - - QList<QString> ParameterNames; - -private: - - ctkCmdLineModuleInstance* q; - -}; - - -ctkCmdLineModuleInstance::ctkCmdLineModuleInstance(const ctkCmdLineModuleReference& moduleRef) - : d(new ctkCmdLineModuleInstancePrivate(this, moduleRef)) -{ -} - -ctkCmdLineModuleInstance::~ctkCmdLineModuleInstance() -{ -} - -QList<QString> ctkCmdLineModuleInstance::parameterNames() const -{ - if (!d->ParameterNames.isEmpty()) return d->ParameterNames; - - foreach (ctkCmdLineModuleParameterGroup paramGroup, - moduleReference().description().parameterGroups()) - { - foreach (ctkCmdLineModuleParameter param, paramGroup.parameters()) - { - d->ParameterNames.push_back(param.name()); - } - } - return d->ParameterNames; -} - -ctkCmdLineModuleReference ctkCmdLineModuleInstance::moduleReference() const -{ - return d->ModuleReference; -} - -QString ctkCmdLineModuleInstance::location() const -{ - return d->ModuleReference.location(); -} - -QStringList ctkCmdLineModuleInstance::commandLineArguments() const -{ - QStringList cmdLineArgs; - QHash<int, QString> indexedArgs; - - QHash<QString,QVariant> currentValues = values(); - ctkCmdLineModuleDescription description = moduleReference().description(); - QHashIterator<QString,QVariant> valuesIter(currentValues); - while(valuesIter.hasNext()) - { - valuesIter.next(); - ctkCmdLineModuleParameter parameter = description.parameter(valuesIter.key()); - if (parameter.index() > -1) - { - indexedArgs.insert(parameter.index(), valuesIter.value().toString()); - } - else - { - QString argFlag; - if (parameter.longFlag().isEmpty()) - { - argFlag = QString("-") + d->normalizeFlag(parameter.flag()); - } - else - { - argFlag = QString("--") + d->normalizeFlag(parameter.longFlag()); - } - - QStringList args; - if (parameter.multiple()) - { - args = valuesIter.value().toString().split(',', QString::SkipEmptyParts); - } - else - { - args.push_back(valuesIter.value().toString()); - } - - foreach(QString arg, args) - { - cmdLineArgs << argFlag << arg; - } - } - } - - QList<int> indexes = indexedArgs.keys(); - qSort(indexes.begin(), indexes.end()); - foreach(int index, indexes) - { - cmdLineArgs << indexedArgs[index]; - } - - return cmdLineArgs; -} - -struct ctkCmdLineModuleFuture {}; - -void ctkCmdLineModuleInstance::run() const -{ -// // TODO: manage memory - QStringList args = commandLineArguments(); - qDebug() << args; - QProcess p; - p.start(location(), args); - p.waitForFinished(); -// ctkCmdLineModuleProcessRunner* moduleProcess = -// new ctkCmdLineModuleProcessRunner(d->ModuleReference.location(), args); -// return moduleProcess->start(); -} - - -QHash<QString, QVariant> ctkCmdLineModuleInstance::values() const -{ - QHash<QString,QVariant> result; - foreach(QString parameterName, parameterNames()) - { - result.insert(parameterName, value(parameterName)); - } - return result; -} - -void ctkCmdLineModuleInstance::setValues(const QHash<QString, QVariant> &values) -{ - QHashIterator<QString,QVariant> iter(values); - while(iter.hasNext()) - { - iter.next(); - setValue(iter.key(), iter.value()); - } -} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.h deleted file mode 100644 index 303245d1c4..0000000000 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleInstance.h +++ /dev/null @@ -1,80 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#ifndef CTKCMDLINEMODULEINSTANCE_H -#define CTKCMDLINEMODULEINSTANCE_H - -#include "ctkCommandLineModulesCoreExport.h" - -#include <QObject> - -template<class K, class V> class QHash; - -class ctkCmdLineModuleFuture; -class ctkCmdLineModuleReference; -class ctkCmdLineModuleInstancePrivate; - -/** - * \ingroup CommandLineModulesCore - */ -class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleInstance : public QObject -{ - Q_OBJECT - -public: - - ~ctkCmdLineModuleInstance(); - - virtual QObject* guiHandle() const = 0; - - virtual QVariant value(const QString& parameter) const = 0; - virtual void setValue(const QString& parameter, const QVariant& value) = 0; - - virtual QList<QString> parameterNames() const; - - virtual QHash<QString,QVariant> values() const; - virtual void setValues(const QHash<QString,QVariant>& values); - - ctkCmdLineModuleReference moduleReference() const; - - QString location() const; - - QStringList commandLineArguments() const; - - void run() const; - - Q_SIGNAL void valueChanged(const QString& parameter, const QVariant& value); - -protected: - - ctkCmdLineModuleInstance(const ctkCmdLineModuleReference& moduleRef); - - //virtual QObject* parameterValueModel() const; - -private: - - friend class ctkCmdLineModuleInstancePrivate; - - QScopedPointer<ctkCmdLineModuleInstancePrivate> d; - -}; - -#endif // CTKCMDLINEMODULEINSTANCE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp index 8f8d34ae13..b9dba9f79e 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp @@ -21,101 +21,280 @@ #include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleBackend.h" +#include "ctkCmdLineModuleFrontend.h" +#include "ctkCmdLineModuleCache_p.h" +#include "ctkCmdLineModuleFuture.h" #include "ctkCmdLineModuleXmlValidator.h" #include "ctkCmdLineModuleReference.h" -#include "ctkCmdLineModuleReferencePrivate.h" -#include "ctkCmdLineModuleInstanceFactory.h" +#include "ctkCmdLineModuleReference_p.h" #include <ctkException.h> +#include <QFileInfo> +#include <QDir> #include <QStringList> #include <QBuffer> +#include <QUrl> +#include <QHash> +#include <QList> +#include <QMutex> -#include <QProcess> #include <QFuture> +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +extern int qHash(const QUrl& url); +#endif + +//---------------------------------------------------------------------------- struct ctkCmdLineModuleManagerPrivate { - ctkCmdLineModuleInstanceFactory* InstanceFactory; + ctkCmdLineModuleManagerPrivate(ctkCmdLineModuleManager::ValidationMode mode, const QString& cacheDir) + : ValidationMode(mode) + { + QFileInfo fileInfo(cacheDir); + if (!fileInfo.exists()) + { + if (!QDir().mkpath(cacheDir)) + { + qWarning() << "Command line module cache disabled. Directory" << cacheDir << "could not be created."; + return; + } + } + + if (fileInfo.isWritable()) + { + ModuleCache.reset(new ctkCmdLineModuleCache(cacheDir)); + } + else + { + qWarning() << "Command line module cache disabled. Directory" << cacheDir << "is not writable."; + } + } + + void checkBackends_unlocked(const QUrl& location) + { + if (!this->SchemeToBackend.contains(location.scheme())) + { + throw ctkInvalidArgumentException(QString("No suitable backend registered for module at ") + location.toString()); + } + } + + QMutex Mutex; + QHash<QString, ctkCmdLineModuleBackend*> SchemeToBackend; + QHash<QUrl, ctkCmdLineModuleReference> LocationToRef; + QScopedPointer<ctkCmdLineModuleCache> ModuleCache; - QHash<QString, ctkCmdLineModuleReference> Cache; + const ctkCmdLineModuleManager::ValidationMode ValidationMode; }; -ctkCmdLineModuleManager::ctkCmdLineModuleManager(ctkCmdLineModuleInstanceFactory *instanceFactory, - ValidationMode validationMode) - : d(new ctkCmdLineModuleManagerPrivate) +//---------------------------------------------------------------------------- +ctkCmdLineModuleManager::ctkCmdLineModuleManager(ValidationMode validationMode, const QString& cacheDir) + : d(new ctkCmdLineModuleManagerPrivate(validationMode, cacheDir)) { - d->InstanceFactory = instanceFactory; } +//---------------------------------------------------------------------------- ctkCmdLineModuleManager::~ctkCmdLineModuleManager() { - delete d->InstanceFactory; } -ctkCmdLineModuleReference -ctkCmdLineModuleManager::registerModule(const QString& location) +//---------------------------------------------------------------------------- +ctkCmdLineModuleManager::ValidationMode ctkCmdLineModuleManager::validationMode() const { - QProcess process; - process.setReadChannel(QProcess::StandardOutput); - process.start(location, QStringList("--xml")); + return d->ValidationMode; +} - ctkCmdLineModuleReference ref; - ref.d->Location = location; - if (!process.waitForFinished() || process.exitStatus() == QProcess::CrashExit || - process.error() != QProcess::UnknownError) +//---------------------------------------------------------------------------- +void ctkCmdLineModuleManager::registerBackend(ctkCmdLineModuleBackend *backend) +{ + QMutexLocker lock(&d->Mutex); + + QList<QString> supportedSchemes = backend->schemes(); + + // Check if there is already a backend registerd for any of the + // supported schemes. We only supported one backend per scheme. + foreach (QString scheme, supportedSchemes) + { + if (d->SchemeToBackend.contains(scheme)) + { + throw ctkInvalidArgumentException(QString("A backend for scheme %1 is already registered.").arg(scheme)); + } + } + + // All good + foreach (QString scheme, supportedSchemes) { - qWarning() << "The executable at" << location << "could not be started:" << process.errorString(); - return ref; + d->SchemeToBackend[scheme] = backend; } +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference +ctkCmdLineModuleManager::registerModule(const QUrl &location) +{ + QByteArray xml; + ctkCmdLineModuleBackend* backend = NULL; + { + QMutexLocker lock(&d->Mutex); - process.waitForReadyRead(); - QByteArray xml = process.readAllStandardOutput(); + d->checkBackends_unlocked(location); - qDebug() << xml; + // If the module is already registered, just return the reference + if (d->LocationToRef.contains(location)) + { + return d->LocationToRef[location]; + } - // validate the outputted xml description - QBuffer input(&xml); - input.open(QIODevice::ReadOnly); + backend = d->SchemeToBackend[location.scheme()]; + } + + bool fromCache = false; + qint64 newTimeStamp = 0; + if (d->ModuleCache) + { + newTimeStamp = backend->timeStamp(location); + if (d->ModuleCache->timeStamp(location) < newTimeStamp) + { + // newly fetch the XML description + try + { + xml = backend->rawXmlDescription(location); + } + catch (...) + { + // cache the failed attempt + d->ModuleCache->cacheXmlDescription(location, newTimeStamp, QByteArray()); + throw; + } + } + else + { + // use the cached XML description + xml = d->ModuleCache->rawXmlDescription(location); + fromCache = true; + } + } + else + { + xml = backend->rawXmlDescription(location); + } - ctkCmdLineModuleXmlValidator validator(&input); - if (!validator.validateInput()) + if (xml.isEmpty()) { - qCritical() << validator.errorString(); - return ref; + if (!fromCache && d->ModuleCache) + { + d->ModuleCache->cacheXmlDescription(location, newTimeStamp, QByteArray()); + } + throw ctkInvalidArgumentException(QString("No XML output available from ") + location.toString()); } + ctkCmdLineModuleReference ref; + ref.d->Location = location; ref.d->RawXmlDescription = xml; -// ref.d->objectRepresentation = descriptionFactory->createObjectRepresentationFromXML(ref.d->xml); -// ref.d->setGUI(descriptionFactory->createGUIFromXML(ref.d->xml)); + ref.d->Backend = backend; + + if (d->ValidationMode != SKIP_VALIDATION) + { + // validate the outputted xml description + QBuffer input(&xml); + input.open(QIODevice::ReadOnly); + + ctkCmdLineModuleXmlValidator validator(&input); + if (!validator.validateInput()) + { + if (d->ModuleCache) + { + // validation failed, cache the description anyway + d->ModuleCache->cacheXmlDescription(location, newTimeStamp, xml); + } - d->Cache[location] = ref; + if (d->ValidationMode == STRICT_VALIDATION) + { + throw ctkInvalidArgumentException(QString("Validating module at %1 failed: %2") + .arg(location.toString()).arg(validator.errorString())); + } + else + { + ref.d->XmlValidationErrorString = validator.errorString(); + } + } + else + { + if (d->ModuleCache && newTimeStamp > 0) + { + // successfully validated the xml, cache it + d->ModuleCache->cacheXmlDescription(location, newTimeStamp, xml); + } + } + } + else + { + if (!fromCache && d->ModuleCache) + { + // cache it + d->ModuleCache->cacheXmlDescription(location, newTimeStamp, xml); + } + } + + { + QMutexLocker lock(&d->Mutex); + // Check that we don't have a race condition + if (d->LocationToRef.contains(location)) + { + // Another thread registered a module with the same location + return d->LocationToRef[location]; + } + d->LocationToRef[location] = ref; + } + + emit moduleRegistered(ref); return ref; } -void ctkCmdLineModuleManager::unregisterModule(const ctkCmdLineModuleReference&) +//---------------------------------------------------------------------------- +void ctkCmdLineModuleManager::unregisterModule(const ctkCmdLineModuleReference& ref) { - throw ctkException("not implemented yet"); + if (!ref) return; + + { + QMutexLocker lock(&d->Mutex); + if (!d->LocationToRef.contains(ref.location())) + { + return; + } + d->LocationToRef.remove(ref.location()); + if (d->ModuleCache) + { + d->ModuleCache->removeCacheEntry(ref.location()); + } + } + emit moduleUnregistered(ref); } -ctkCmdLineModuleReference ctkCmdLineModuleManager::moduleReference(const QString& location) const +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference ctkCmdLineModuleManager::moduleReference(const QUrl &location) const { - throw ctkException("not implemented yet"); + QMutexLocker lock(&d->Mutex); + QHash<QUrl, ctkCmdLineModuleReference>::const_iterator iter = d->LocationToRef.find(location); + return (iter == d->LocationToRef.end()) ? ctkCmdLineModuleReference() : iter.value(); } +//---------------------------------------------------------------------------- QList<ctkCmdLineModuleReference> ctkCmdLineModuleManager::moduleReferences() const { - throw ctkException("not implemented yet"); + QMutexLocker lock(&d->Mutex); + return d->LocationToRef.values(); } -ctkCmdLineModuleInstance* -ctkCmdLineModuleManager::createModuleInstance(const ctkCmdLineModuleReference& moduleRef) +//---------------------------------------------------------------------------- +ctkCmdLineModuleFuture ctkCmdLineModuleManager::run(ctkCmdLineModuleFrontend *frontend) { - return d->InstanceFactory->create(moduleRef); -} + QMutexLocker lock(&d->Mutex); + d->checkBackends_unlocked(frontend->location()); -QList<ctkCmdLineModuleInstance*> -ctkCmdLineModuleManager::moduleInstances(const ctkCmdLineModuleReference& moduleRef) const -{ - throw ctkException("not implemented yet"); + ctkCmdLineModuleFuture future = d->SchemeToBackend[frontend->location().scheme()]->run(frontend); + frontend->setFuture(future); + emit frontend->started(); + return future; } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h index ea06e141cb..9d8531bcaa 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h @@ -26,18 +26,49 @@ #include <QStringList> #include <QString> +#include "ctkCmdLineModuleReference.h" -struct ctkCmdLineModuleInstanceFactory; +struct ctkCmdLineModuleBackend; +struct ctkCmdLineModuleFrontendFactory; +class ctkCmdLineModuleFrontend; +class ctkCmdLineModuleFuture; -class ctkCmdLineModuleInstance; -class ctkCmdLineModuleReference; -class ctkCmdLineModuleManagerPrivate; +struct ctkCmdLineModuleManagerPrivate; /** - * \ingroup CommandLineModulesCore + * @ingroup CommandLineModulesCore_API + * + * @brief Main manager class to handle loading and unloading of modules. + * + * This is the central managing class for CTK "command line modules". To register modules, + * you need a command line module back-end which is capable of handling the URL scheme + * under which you want to register your modules. + * + * A default back-end for handling "file" URLs which runs local executables is + * available in ctkCmdLineModuleBackendLocalProcess. + * + * To create and run a specific front-end for a given module, you need a concrete + * ctkCmdLineModuleFrontend implementation. The implementation provided in + * ctkCmdLineModuleFrontendQtGui creates a default Qt widgets based GUI for a given + * ctkCmdLineModuleReference instance. + * + * Here is an example how this and related classes could be used: + * + * First, we create ctkCmdLineModuleManager instance. + * \snippet ModuleManager/main.cpp instantiate-mm + * + * Then we need at least one back-end which can handle registering modules. + * \snippet ModuleManager/main.cpp register-backend + * + * Finally, we can register a module. + * \snippet ModuleManager/main.cpp register-module + * + * @see ctkCmdLineModuleFrontend + * @see ctkCmdLineModuleBackend */ -class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleManager +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleManager : public QObject { + Q_OBJECT public: @@ -53,20 +84,93 @@ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleManager WEAK_VALIDATION }; - ctkCmdLineModuleManager(ctkCmdLineModuleInstanceFactory* descriptionFactory, - ValidationMode = STRICT_VALIDATION); + /** + * @brief Create a module manager instance. + * @param validationMode The validation mode for the XML description of the module parameters. + * @param cacheDir The directory where to cache information about registered modules. + * + * If the <code>validationMode</code> argument is set to <code>SKIP_VALIDATION</code>, no XML validation + * takes place and certain front-ends might fail to generate a GUI. If it is set to + * <code>WEAK_VALIDATION</code>, module registrations will proceed on error but the error status + * is available via ctkCmdLineModuleReference::xmlValidationErrorString(). + */ + ctkCmdLineModuleManager(ValidationMode validationMode = STRICT_VALIDATION, + const QString& cacheDir = QString()); ~ctkCmdLineModuleManager(); - ctkCmdLineModuleReference registerModule(const QString& location); + /** + * @brief Get the validation mode. + * @return The validation mode. + */ + ValidationMode validationMode() const; + + /** + * @brief Registers a new back-end. + * @param backend The new back-end. + * @throws ctkInvalidArgumentException if another back-end was already registered handling + * one of the URL schemes this back-end claims to handle. + */ + void registerBackend(ctkCmdLineModuleBackend* backend); + + /** + * @brief Registers a module, identified by the given URL. + * @param location The URL for the new module. + * @return A module reference. + * @throws ctkInvalidArgumentException if no back-end for the given URL scheme was registered + * or the XML description for the module is invalid. + */ + ctkCmdLineModuleReference registerModule(const QUrl& location); + + /** + * @brief Unregister a previously registered module. + * @param moduleRef The reference for the module to unregister. + * + * This method does nothing if the module reference <code>moduleRef</code> is invalid or + * reference an un-registered module. + */ void unregisterModule(const ctkCmdLineModuleReference& moduleRef); - ctkCmdLineModuleReference moduleReference(const QString& location) const; + /** + * @brief Returns a ctkCmdLineModuleReference object for the given URL. + * @param location The location URL for which to get a module reference. + * @return The module reference for the location or an invalid module reference + * if no module was registered under the given location URL. + */ + ctkCmdLineModuleReference moduleReference(const QUrl& location) const; + + /** + * @brief Returns module references for all currently registered modules. + * @return A list of module references. + */ QList<ctkCmdLineModuleReference> moduleReferences() const; - ctkCmdLineModuleInstance* createModuleInstance(const ctkCmdLineModuleReference& moduleRef); - - QList<ctkCmdLineModuleInstance*> moduleInstances(const ctkCmdLineModuleReference& moduleRef) const; + /** + * @brief Run a module front-end. + * @param frontend The module front-end to run. + * @return A ctkCmdLineModuleFuture object which can be used to interact with the + * running front-end. + * + * This method takes a ctkCmdLineModuleFrontend pointer and uses the registered back-end + * for this module's location URL scheme to run it asynchronously. The returned future object + * is the only way to interact with the task started by the specific back-end. + * + * @see ctkCmdLineModuleFuture + * @see ctkCmdLineModuleFutureWatcher + */ + ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* frontend); + +Q_SIGNALS: + + /** + * @brief This signal is emitted whenever a module is registered. + */ + void moduleRegistered(const ctkCmdLineModuleReference&); + + /** + * @brief This signal is emitted whenever a module is un-registered. + */ + void moduleUnregistered(const ctkCmdLineModuleReference&); private: diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.cpp index 1a8f0d9a61..0588ceca20 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.cpp @@ -20,11 +20,37 @@ limitations under the License. #include "ctkCmdLineModuleParameter.h" -#include "ctkCmdLineModuleParameterPrivate.h" +#include "ctkCmdLineModuleParameter_p.h" #include <QStringList> #include <QTextStream> +//---------------------------------------------------------------------------- +ctkCmdLineModuleParameterPrivate::ctkCmdLineModuleParameterPrivate() + : Hidden(false), Constraints(false), Channel("input"), Index(-1), Multiple(false) +{} + +//---------------------------------------------------------------------------- +QStringList ctkCmdLineModuleParameterPrivate::splitAndTrim(const QString& str, const QString& separator) +{ + QStringList l = str.split(separator, QString::SkipEmptyParts); + l.removeDuplicates(); + // trim the strings + QMutableStringListIterator i(l); + while(i.hasNext()) + { + QString& n = i.next(); + n = n.trimmed(); + } + return l; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleParameterPrivate::setFileExtensionsAsString(const QString& extensions) +{ + FileExtensions = splitAndTrim(extensions, ","); + FileExtensionsAsString = FileExtensions.join(","); +} //---------------------------------------------------------------------------- ctkCmdLineModuleParameter::ctkCmdLineModuleParameter() @@ -55,25 +81,12 @@ QString ctkCmdLineModuleParameter::tag() const return d->Tag; } -////---------------------------------------------------------------------------- -//QString ctkCmdLineModuleParameter::cppType() const -//{ -// Q_D(const ctkCmdLineModuleParameter); -// return d->CPPType; -//} - //---------------------------------------------------------------------------- QString ctkCmdLineModuleParameter::type() const { return d->Type; } -//---------------------------------------------------------------------------- -QString ctkCmdLineModuleParameter::reference() const -{ - return d->Reference; -} - //---------------------------------------------------------------------------- bool ctkCmdLineModuleParameter::hidden() const { @@ -83,9 +96,8 @@ bool ctkCmdLineModuleParameter::hidden() const //---------------------------------------------------------------------------- bool ctkCmdLineModuleParameter::isReturnParameter() const { - // could check for tag == float, int, float-vector, ... - if (d->Channel == "output" - && !this->isFlagParameter() && !this->isIndexParameter()) + if (d->Channel == "output" && this->isIndexParameter() && + this->index() == 1000) { return true; } @@ -104,19 +116,6 @@ bool ctkCmdLineModuleParameter::isIndexParameter() const return (d->Index > -1); } -//---------------------------------------------------------------------------- -QString ctkCmdLineModuleParameter::argType() const -{ - return d->ArgType; -} - -////---------------------------------------------------------------------------- -//QString ctkCmdLineModuleParameter::stringToType() const -//{ -// Q_D(const ctkCmdLineModuleParameter); -// return d->StringToType; -//} - //---------------------------------------------------------------------------- QString ctkCmdLineModuleParameter::name() const { @@ -243,12 +242,6 @@ bool ctkCmdLineModuleParameter::multiple() const return d->Multiple; } -//---------------------------------------------------------------------------- -QString ctkCmdLineModuleParameter::aggregate() const -{ - return d->Aggregate; -} - //---------------------------------------------------------------------------- QString ctkCmdLineModuleParameter::fileExtensionsAsString() const { @@ -273,12 +266,6 @@ QStringList ctkCmdLineModuleParameter::elements() const return d->Elements; } -//---------------------------------------------------------------------------- -//QStringList& ctkCmdLineModuleParameter::elements() -//{ -// return d->Elements; -//} - //---------------------------------------------------------------------------- QTextStream& operator<<(QTextStream& os, const ctkCmdLineModuleParameter& parameter) { @@ -288,11 +275,7 @@ QTextStream& operator<<(QTextStream& os, const ctkCmdLineModuleParameter& parame os << " " << "Description: " << parameter.description() << '\n'; os << " " << "Label: " << parameter.label() << '\n'; os << " " << "Type: " << parameter.type() << '\n'; - os << " " << "Reference: " << parameter.reference() << '\n'; os << " " << "Hidden: " << (parameter.hidden() ? "true" : "false") << '\n'; - //os << " " << "CPPType: " << parameter.cppType() << '\n'; - os << " " << "ArgType: " << parameter.argType() << '\n'; - //os << " " << "StringToType: " << parameter.stringToType() << '\n'; os << " " << "Default: " << parameter.defaultValue() << '\n'; os << " " << "Elements: " << parameter.elements().join(", ") << '\n'; os << " " << "Constraints: " << (parameter.constraints() ? "true" : "false") << '\n'; @@ -308,7 +291,6 @@ QTextStream& operator<<(QTextStream& os, const ctkCmdLineModuleParameter& parame os << " " << "Channel: " << parameter.channel() << '\n'; os << " " << "Index: " << parameter.index() << '\n'; os << " " << "Multiple: " << (parameter.multiple() ? "true" : "false") << '\n'; - os << " " << "Aggregate: " << parameter.aggregate() << '\n'; os << " " << "FileExtensions: " << parameter.fileExtensionsAsString() << '\n'; os << " " << "CoordinateSystem: " << parameter.coordinateSystem() << '\n'; return os; diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.h index 2a6fb7fb07..1acd17cdee 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter.h @@ -28,18 +28,18 @@ class QTextStream; class QStringList; -class ctkCmdLineModuleParameterPrivate; +struct ctkCmdLineModuleParameterPrivate; /** - * \ingroup CommandLineModulesCore + * \class ctkCmdLineModuleParameter + * \brief Single parameter to a module, like a threshold of a filter. + * \ingroup CommandLineModulesCore_API * - * \brief Single parameter to a module, like a threshold of a filter. * - * ctkCmdLineModuleParameter describes a single parameters to a + * ctkCmdLineModuleParameter describes a single parameter for a * module. Information on the parameter type, name, flag, label, * description, channel, index, default, and constraints can be - * stored. - * + * retrieved. */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleParameter { @@ -51,81 +51,177 @@ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleParameter ctkCmdLineModuleParameter& operator=(const ctkCmdLineModuleParameter& other); + /** + * @return The tag name, e.g. "integer" or "image". + */ QString tag() const; -// QString cppType() const; - + /** + * @return The type of the parameter, e.g. "scalar" or "vector" for image parameters. + */ QString type() const; - QString reference() const; - + /** + * @return <code>true</code> if the parameter is declared hidden, <code>false</code> + * otherwise. + */ bool hidden() const; - // Simple return types are parameters on output channel with no - // flags and without a specified index + /** + * Simple return types are parameters on the output channel with no + * flags and with a dummy index of 1000. + * + * @return <code>true</code> if the parameter is a simple return type, + * <code>false</code> otherwise. + */ bool isReturnParameter() const; - // Has a flag or a long flag? + /** + * @return <code>true</code> if the parameter has a flag (long or short), + * <code>false</code> otherwise. + */ bool isFlagParameter() const; - // Is an index type? + /** + * @return <code>true</code> if the parameter has an index, <code>false</code> otherwise. + */ bool isIndexParameter() const; - QString argType() const; - - //void setStringToType(const QString& stringToType); - //QString stringToType() const; - + /** + * @return The parameter name. + */ QString name() const; + /** + * @return The (possibly empty) long flag for this parameter. + */ QString longFlag() const; + /** + * @return A (possibly empty) comma separated string of aliases for the long flag. + */ QString longFlagAliasesAsString() const; + + /** + * @return A (possibly empty) list of long flag aliases. + */ QStringList longFlagAliases() const; + /** + * @return A (possibly empty) comma separated string of deprecated long flag aliases. + */ QString deprecatedLongFlagAliasesAsString() const; + + /** + * @return A (possibly empty) list of deprectated long flag aliases. + */ QStringList deprecatedLongFlagAliases() const; + /** + * @return The human-readable name of this parameter. + */ QString label() const; + /** + * @return <code>true</code> if this parameter imposes constraints on the set of allowed values. + */ bool constraints() const; + /** + * @return The maximum value constraint. + */ QString maximum() const; + /** + * @return The minimum value constraint. + */ QString minimum() const; + /** + * @return The value step size constraint. + */ QString step() const; + /** + * @return A longer description of this parameter. + */ QString description() const; + /** + * @return The string "input" for input parameters and "output" for output parameters. + */ QString channel() const; + /** + * @return The parameter index or <code>-1</code> if this is not an indexed parameter. + * @sa flag() + * @sa longFlag() + */ int index() const; + /** + * @return The default value. + */ QString defaultValue() const; + /** + * @return The (possibly empty) flag for this parameter. + * @sa index() + */ QString flag() const; + /** + * @return A (possibly empty) comma separated string of flag aliases. + */ QString flagAliasesAsString() const; + + /** + * @return A (possibly empty) list of flag aliases. + */ QStringList flagAliases() const; + /** + * @return A (possibly empty) comma separated string of deprecated flag aliases. + */ QString deprecatedFlagAliasesAsString() const; + + /** + * @return A (possibly empty) list of deprecated flag aliases. + */ QStringList deprecatedFlagAliases() const; + /** + * @return <code>true</code> if this parameter can appear multiple time in the argument list, + * <code>false</code> otherwise. + */ bool multiple() const; - QString aggregate() const; - + /** + * @return A (possibly empty) comma separated list of file extensions (e.g. "*.nrrd,*.mhd") for + * the "file", "image", or "geometry" parameter tags. + */ QString fileExtensionsAsString() const; + + /** + * @return A (possibly empty) list of file extensions. + * @sa fileExtensionsAsString() + */ QStringList fileExtensions() const; + /** + * @return The coordinate system (either "lps", "ras", oder "ijk") for the "point" or "region" + * parameter tags. + */ QString coordinateSystem() const; + /** + * @return The list of valid elements for enumeration parameters (e.g. "string-enumeration"). + */ QStringList elements() const; private: - friend class ctkCmdLineModuleParameterParser; + friend struct ctkCmdLineModuleParameterParser; friend class ctkCmdLineModuleXmlParser; ctkCmdLineModuleParameter(); diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.cpp index 1a2edb2f33..a22d4f0c0d 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.cpp @@ -21,7 +21,7 @@ limitations under the License. #include "ctkCmdLineModuleParameterGroup.h" #include "ctkCmdLineModuleParameter.h" -#include "ctkCmdLineModuleParameterGroupPrivate.h" +#include "ctkCmdLineModuleParameterGroup_p.h" #include "ctkException.h" diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.h index a4979620b3..b2dcd9dc81 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup.h @@ -29,13 +29,12 @@ class QTextStream; class ctkCmdLineModuleParameter; -class ctkCmdLineModuleParameterGroupPrivate; +struct ctkCmdLineModuleParameterGroupPrivate; /** - * \ingroup CommandLineModulesCore - * - * \brief Group of parameters - * + * \class ctkCmdLineModuleParameterGroup + * \brief Group of parameters + * \ingroup CommandLineModulesCore_API */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleParameterGroup { diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroupPrivate.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup_p.h similarity index 100% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroupPrivate.h rename to Libs/CommandLineModules/Core/ctkCmdLineModuleParameterGroup_p.h diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterParsers_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterParsers_p.h index a561189060..5585662b78 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterParsers_p.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterParsers_p.h @@ -25,7 +25,7 @@ #include <QXmlStreamReader> #include "ctkCmdLineModuleParameter.h" -#include "ctkCmdLineModuleParameterPrivate.h" +#include "ctkCmdLineModuleParameter_p.h" namespace { @@ -115,6 +115,10 @@ struct ctkCmdLineModuleParameterParser { moduleParamPrivate->Index = xmlReader.readElementText().toInt(); } + else if (name.compare("channel", Qt::CaseInsensitive) == 0) + { + moduleParamPrivate->Channel = xmlReader.readElementText().trimmed(); + } else { xmlReader.skipCurrentElement(); @@ -237,35 +241,14 @@ class ctkCmdLineModulePointParameterParser : public ctkCmdLineModuleMultiplePara } }; -class ctkCmdLineModuleChannelParameterParser : public ctkCmdLineModuleMultipleParameterParser -{ - -protected: - - bool handleSubElement(ctkCmdLineModuleParameterPrivate* moduleParamPrivate, QXmlStreamReader& xmlReader) - { - QStringRef name = xmlReader.name(); - - if (name.compare("channel", Qt::CaseInsensitive) == 0) - { - moduleParamPrivate->Channel = xmlReader.readElementText().trimmed(); - return true; - } - else - { - return ctkCmdLineModuleMultipleParameterParser::handleSubElement(moduleParamPrivate, xmlReader); - } - } -}; - -class ctkCmdLineModuleFileParameterParser : public ctkCmdLineModuleChannelParameterParser +class ctkCmdLineModuleFileParameterParser : public ctkCmdLineModuleMultipleParameterParser { protected: void handleAttributes(ctkCmdLineModuleParameterPrivate* moduleParamPrivate, QXmlStreamReader& xmlReader) { - ctkCmdLineModuleChannelParameterParser::handleAttributes(moduleParamPrivate, xmlReader); + ctkCmdLineModuleMultipleParameterParser::handleAttributes(moduleParamPrivate, xmlReader); moduleParamPrivate->FileExtensionsAsString =xmlReader.attributes().value("fileExtensions").toString().trimmed(); } }; @@ -283,14 +266,14 @@ class ctkCmdLineModuleGeometryParameterParser : public ctkCmdLineModuleMultipleP } }; -class ctkCmdLineModuleImageParameterParser : public ctkCmdLineModuleChannelParameterParser +class ctkCmdLineModuleImageParameterParser : public ctkCmdLineModuleMultipleParameterParser { protected: void handleAttributes(ctkCmdLineModuleParameterPrivate* moduleParamPrivate, QXmlStreamReader& xmlReader) { - ctkCmdLineModuleChannelParameterParser::handleAttributes(moduleParamPrivate, xmlReader); + ctkCmdLineModuleMultipleParameterParser::handleAttributes(moduleParamPrivate, xmlReader); moduleParamPrivate->setFileExtensionsAsString(xmlReader.attributes().value("fileExtensions").toString().trimmed()); moduleParamPrivate->Type = xmlReader.attributes().value("type").toString().trimmed(); } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.cpp deleted file mode 100644 index 49e5382674..0000000000 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "ctkCmdLineModuleParameterPrivate.h" - -ctkCmdLineModuleParameterPrivate::ctkCmdLineModuleParameterPrivate() - : Hidden(false), Constraints(false), Index(-1), Multiple(false), Aggregate("false") -{} - -QStringList ctkCmdLineModuleParameterPrivate::splitAndTrim(const QString& str, const QString& separator) -{ - QStringList l = str.split(separator, QString::SkipEmptyParts); - l.removeDuplicates(); - // trim the strings - QMutableStringListIterator i(l); - while(i.hasNext()) - { - QString& n = i.next(); - n = n.trimmed(); - } - return l; -} - -void ctkCmdLineModuleParameterPrivate::setFileExtensionsAsString(const QString& extensions) -{ - FileExtensions = splitAndTrim(extensions, ","); - FileExtensionsAsString = FileExtensions.join(","); -} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter_p.h similarity index 94% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.h rename to Libs/CommandLineModules/Core/ctkCmdLineModuleParameter_p.h index a6cafd56fe..46b246ad96 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleParameterPrivate.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleParameter_p.h @@ -39,12 +39,8 @@ struct ctkCmdLineModuleParameterPrivate : public QSharedData QString Name; QString Description; QString Label; - //QString CPPType; QString Type; - QString Reference; bool Hidden; - QString ArgType; - //QString StringToType; QString Default; QString Flag; QString LongFlag; @@ -55,7 +51,6 @@ struct ctkCmdLineModuleParameterPrivate : public QSharedData QString Channel; int Index; int Multiple; - QString Aggregate; QString FileExtensionsAsString; QStringList FileExtensions; QString CoordinateSystem; diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.cpp new file mode 100644 index 0000000000..6af74eaea6 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModulePathBuilder.h" + +//---------------------------------------------------------------------------- +ctkCmdLineModulePathBuilder::~ctkCmdLineModulePathBuilder() +{ +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.h b/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.h new file mode 100644 index 0000000000..5d38450106 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModulePathBuilder.h @@ -0,0 +1,41 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEPATHBUILDER_H +#define CTKCMDLINEMODULEPATHBUILDER_H + +#include <QStringList> + +#include "ctkCommandLineModulesCoreExport.h" + +/** + * \class ctkCmdLineModulePathBuilder + * \brief Prototype interface for objects that can build + * up a list of file paths, stored in a QStringList. + * \author m.clarkson@ucl.ac.uk + */ +struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModulePathBuilder +{ + virtual ~ctkCmdLineModulePathBuilder(); + + virtual QStringList build() = 0; +}; + +#endif // CTKCMDLINEMODULEPATHBUILDER_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleProcess.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleProcess.cpp deleted file mode 100644 index bca2161175..0000000000 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleProcess.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "ctkCmdLineModuleProcess_p.h" - - -ctkCmdLineModuleProcess::ctkCmdLineModuleProcess(const QString& location, const QStringList& args) - : process(), location(location), args(args) -{ -} - -ctkCmdLineModuleFuture ctkCmdLineModuleProcess::start() -{ - this->reportStarted(); - ctkCmdLineModuleFuture future(this); - connect(&process, SIGNAL(started()), this, SLOT(processStarted())); - connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); - connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - - process.start(location, args); - return future; -} - -void ctkCmdLineModuleProcess::processStarted() -{ - qDebug() << "Reporting process started"; - this->reportStarted(); -} - -void ctkCmdLineModuleProcess::processFinished(int exitCode, QProcess::ExitStatus status) -{ - Q_UNUSED(exitCode) - Q_UNUSED(status) - qDebug() << "Reporting process finished"; - this->reportExitCode(exitCode); - this->reportExitStatus(status); - this->reportProcessError(process.error()); - this->reportErrorString(process.errorString()); - this->reportStandardOutput(process.readAllStandardOutput()); - this->reportStandardError(process.readAllStandardError()); - this->reportFinished(); -} - -void ctkCmdLineModuleProcess::processError(QProcess::ProcessError) -{ - //qDebug() << "Reporting process error"; - //this->reportException(ctkCmdLineModuleProcessException(process.errorString(), process.exitCode(), - // process.exitStatus())); -} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.cpp index fd60290f5e..d48e03cfce 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.cpp @@ -20,43 +20,114 @@ =============================================================================*/ #include "ctkCmdLineModuleReference.h" -#include "ctkCmdLineModuleReferencePrivate.h" +#include "ctkCmdLineModuleReference_p.h" +#include "ctkCmdLineModuleXmlParser_p.h" +#include "ctkCmdLineModuleXmlException.h" +#include <QBuffer> + + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReferencePrivate::ctkCmdLineModuleReferencePrivate() + : Backend(NULL), XmlException(NULL) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleReferencePrivate::~ctkCmdLineModuleReferencePrivate() +{ + delete XmlException; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleDescription ctkCmdLineModuleReferencePrivate::description() const +{ + // If we already got an XML exception just throw it immediately, since + // the XML description cannot change for this module reference. + if (XmlException) + { + throw *XmlException; + } + + // Lazy creation. The title is a required XML element. + if (Description.title().isNull()) + { + QByteArray xml(RawXmlDescription); + QBuffer xmlInput(&xml); + ctkCmdLineModuleXmlParser parser(&xmlInput, &Description); + try + { + parser.doParse(); + } + catch (const ctkCmdLineModuleXmlException& e) + { + XmlException = e.clone(); + } + } + return Description; +} + +//---------------------------------------------------------------------------- ctkCmdLineModuleReference::ctkCmdLineModuleReference() : d(new ctkCmdLineModuleReferencePrivate()) {} +//---------------------------------------------------------------------------- ctkCmdLineModuleReference::~ctkCmdLineModuleReference() { } +//---------------------------------------------------------------------------- ctkCmdLineModuleReference::ctkCmdLineModuleReference(const ctkCmdLineModuleReference &ref) : d(ref.d) { } +//---------------------------------------------------------------------------- ctkCmdLineModuleReference &ctkCmdLineModuleReference::operator =(const ctkCmdLineModuleReference &ref) { d = ref.d; return *this; } -ctkCmdLineModuleReference::operator bool() +//---------------------------------------------------------------------------- +ctkCmdLineModuleReference::operator bool() const { return !d->RawXmlDescription.isEmpty(); } +//---------------------------------------------------------------------------- ctkCmdLineModuleDescription ctkCmdLineModuleReference::description() const { return d->description(); } +//---------------------------------------------------------------------------- QByteArray ctkCmdLineModuleReference::rawXmlDescription() const { return d->RawXmlDescription; } -QString ctkCmdLineModuleReference::location() const +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleReference::xmlValidationErrorString() const +{ + return d->XmlValidationErrorString; +} + +//---------------------------------------------------------------------------- +QUrl ctkCmdLineModuleReference::location() const { return d->Location; } + +//---------------------------------------------------------------------------- +ctkCmdLineModuleBackend *ctkCmdLineModuleReference::backend() const +{ + return d->Backend; +} + +//---------------------------------------------------------------------------- +uint qHash(const ctkCmdLineModuleReference& moduleRef) +{ + return qHash(moduleRef.d.data()); +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.h index a39d9f5e7b..9d9ed2f21d 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference.h @@ -25,37 +25,83 @@ #include <ctkCommandLineModulesCoreExport.h> #include <QSharedDataPointer> +#include <QMetaType> +struct ctkCmdLineModuleBackend; class ctkCmdLineModuleDescription; -class ctkCmdLineModuleReferencePrivate; +struct ctkCmdLineModuleReferencePrivate; /** - * \ingroup CommandLineModulesCore + * \class ctkCmdLineModuleReference + * \brief Defines a reference or handle to a module, including location, + * XML, description and access to the associated backend. + * \ingroup CommandLineModulesCore_API + * + * Instances of this class reference registered modules and can be used + * to retrieve information about their parameters or to create a specific + * front-end. */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleReference { public: + /** + * @brief Creates an invalid module reference. + */ ctkCmdLineModuleReference(); ~ctkCmdLineModuleReference(); ctkCmdLineModuleReference(const ctkCmdLineModuleReference& ref); ctkCmdLineModuleReference& operator=(const ctkCmdLineModuleReference& ref); - operator bool(); + /** + * @brief Conversion operator to test the validity of this module reference. + */ + operator bool() const; + /** + * @brief Get the module description for the parameters. + * @return The XML description as a class representation. + * @throws ctkCmdLineModuleXmlException if the raw XML description cannot be parsed. + */ ctkCmdLineModuleDescription description() const; + /** + * @brief Get the raw XML description, as supplied by the back-end. + * @return The raw XML description. + */ QByteArray rawXmlDescription() const; - QString location() const; + /** + * @brief Retrieve a validation error string. + * @return A non-empty string describing the validation error, if validation + * of the XML description was not successful. + */ + QString xmlValidationErrorString() const; + + /** + * @brief Get the URL under which the module was registered. + * @return The module location. + */ + QUrl location() const; + + /** + * @brief Get the back-end which was registered to handle this module. + * @return The back-end handling this module. + */ + ctkCmdLineModuleBackend* backend() const; private: friend class ctkCmdLineModuleManager; + friend uint CTK_CMDLINEMODULECORE_EXPORT qHash(const ctkCmdLineModuleReference&); QSharedDataPointer<ctkCmdLineModuleReferencePrivate> d; }; +Q_DECLARE_METATYPE(ctkCmdLineModuleReference) + +uint CTK_CMDLINEMODULECORE_EXPORT qHash(const ctkCmdLineModuleReference& moduleRef); + #endif // CTKCMDLINEMODULEREFERENCE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference_p.h similarity index 80% rename from Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.h rename to Libs/CommandLineModules/Core/ctkCmdLineModuleReference_p.h index 48ea8d1bf2..4427754fa0 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleReferencePrivate.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleReference_p.h @@ -25,18 +25,27 @@ #include <ctkCmdLineModuleDescription.h> #include <QSharedData> -#include <QString> +#include <QUrl> + +struct ctkCmdLineModuleBackend; +class ctkCmdLineModuleXmlException; struct ctkCmdLineModuleReferencePrivate : public QSharedData { + ctkCmdLineModuleReferencePrivate(); + ~ctkCmdLineModuleReferencePrivate(); + ctkCmdLineModuleDescription description() const; - QString Location; + ctkCmdLineModuleBackend* Backend; + QUrl Location; QByteArray RawXmlDescription; + QString XmlValidationErrorString; private: mutable ctkCmdLineModuleDescription Description; + mutable ctkCmdLineModuleXmlException* XmlException; }; #endif // CTKCMDLINEMODULEREFERENCEPRIVATE_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.cpp new file mode 100644 index 0000000000..92ab69186e --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.cpp @@ -0,0 +1,80 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleResult.h" + +#include <QVariant> +#include <QDebug> + +struct ctkCmdLineModuleResultPrivate : public QSharedData +{ + QString Parameter; + QVariant Value; +}; + +ctkCmdLineModuleResult::ctkCmdLineModuleResult() + : d(new ctkCmdLineModuleResultPrivate) +{} + +ctkCmdLineModuleResult::~ctkCmdLineModuleResult() +{ +} + +ctkCmdLineModuleResult::ctkCmdLineModuleResult(const ctkCmdLineModuleResult &other) + : d(other.d) +{ +} + +ctkCmdLineModuleResult::ctkCmdLineModuleResult(const QString& parameter, const QVariant& value) + : d(new ctkCmdLineModuleResultPrivate) +{ + d->Parameter = parameter; + d->Value = value; +} + +bool ctkCmdLineModuleResult::operator==(const ctkCmdLineModuleResult& other) const +{ + return d->Parameter == other.d->Parameter && d->Value == other.d->Value; +} + +QString ctkCmdLineModuleResult::parameter() const +{ + return d->Parameter; +} + +QVariant ctkCmdLineModuleResult::value() const +{ + return d->Value; +} + +QDebug operator<<(QDebug debug, const ctkCmdLineModuleResult& result) +{ + debug.nospace() << result.parameter() << "=" << result.value(); + return debug; +} + + +ctkCmdLineModuleResult &ctkCmdLineModuleResult::operator =(const ctkCmdLineModuleResult &other) +{ + d->Parameter = other.d->Parameter; + d->Value = other.d->Value; + return *this; +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.h new file mode 100644 index 0000000000..2442298705 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleResult.h @@ -0,0 +1,75 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULERESULT_H +#define CTKCMDLINEMODULERESULT_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include <QSharedPointer> + +struct ctkCmdLineModuleResultPrivate; + +/** + * @ingroup CommandLineModulesCore_API + * + * @brief Describes a reported result of a command line module. + * + * This class wraps a specific result reported by a running module via a + * ctkCmdLineModuleFuture instance. + * + * @see ctkCmdLineModuleFuture + * @see ctkCmdLineModuleFutureWatcher + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleResult +{ +public: + + ctkCmdLineModuleResult(); + ~ctkCmdLineModuleResult(); + + ctkCmdLineModuleResult(const ctkCmdLineModuleResult& other); + ctkCmdLineModuleResult& operator=(const ctkCmdLineModuleResult& other); + + ctkCmdLineModuleResult(const QString& parameter, const QVariant& value); + + bool operator==(const ctkCmdLineModuleResult& other) const; + + /** + * @brief Get the name of the output parameter for which this result was reported. + * @return The output parameter name. + */ + QString parameter() const; + + /** + * @brief Get the result value. + * @return The result value. + */ + QVariant value() const; + +private: + + QSharedPointer<ctkCmdLineModuleResultPrivate> d; +}; + +CTK_CMDLINEMODULECORE_EXPORT QDebug operator<<(QDebug debug, const ctkCmdLineModuleResult& result); + +#endif // CTKCMDLINEMODULERESULT_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.cpp new file mode 100644 index 0000000000..7b993fa99c --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.cpp @@ -0,0 +1,100 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleRunException.h" + +#include <QUrl> + +//---------------------------------------------------------------------------- +ctkCmdLineModuleRunException::ctkCmdLineModuleRunException(const QUrl &location, int errorCode, const QString &errorString) + : QtConcurrent::Exception(), + ctkException(QString("Running module \"%1\" failed with code %2: %3").arg(location.toString()).arg(errorCode).arg(errorString)), + Location(location), ErrorCode(errorCode), ErrorString(errorString) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleRunException::ctkCmdLineModuleRunException(const QUrl &location, int errorCode, const QString &errorString, + const ctkCmdLineModuleRunException& cause) + : QtConcurrent::Exception(), ctkException(location.toString(), cause), + Location(location), ErrorCode(errorCode), ErrorString(errorString) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleRunException::ctkCmdLineModuleRunException(const ctkCmdLineModuleRunException& o) + : QtConcurrent::Exception(o), ctkException(o), + Location(o.Location), ErrorCode(o.ErrorCode), ErrorString(o.ErrorString) +{ +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleRunException::~ctkCmdLineModuleRunException() throw() +{ +} + +//---------------------------------------------------------------------------- +QUrl ctkCmdLineModuleRunException::location() const throw() +{ + return Location; +} + +//---------------------------------------------------------------------------- +int ctkCmdLineModuleRunException::errorCode() const throw() +{ + return ErrorCode; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleRunException::errorString() const throw() +{ + return ErrorString; +} + +//---------------------------------------------------------------------------- +const char* ctkCmdLineModuleRunException::name() const throw() +{ + return "CTK CommandLineModule Run Exception"; +} + +//---------------------------------------------------------------------------- +const char* ctkCmdLineModuleRunException::className() const throw() +{ + return "ctkCmdLineModuleRunException"; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleRunException* ctkCmdLineModuleRunException::clone() const +{ + return new ctkCmdLineModuleRunException(*this); +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleRunException::rethrow() const +{ + throw *this; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleRunException::raise() const +{ + throw *this; +} diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.h new file mode 100644 index 0000000000..8eefc5b698 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.h @@ -0,0 +1,72 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULERUNEXCEPTION_H +#define CTKCMDLINEMODULERUNEXCEPTION_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include <ctkException.h> + +#include <QtCore> + +/** + * \class ctkCmdLineModuleRunException + * \brief Exception class to describe problems with running the module. + * \ingroup CommandLineModulesCore_API + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleRunException + : public QtConcurrent::Exception, public ctkException +{ +public: + + explicit ctkCmdLineModuleRunException(const QUrl& location, int errorCode, + const QString& errorString); + + ctkCmdLineModuleRunException(const QUrl& location, int errorCode, + const QString& errorString, const ctkCmdLineModuleRunException& cause); + + ctkCmdLineModuleRunException(const ctkCmdLineModuleRunException& o); + + ctkCmdLineModuleRunException& operator=(const ctkCmdLineModuleRunException& o); + + ~ctkCmdLineModuleRunException() throw(); + + QUrl location() const throw(); + int errorCode() const throw(); + QString errorString() const throw(); + + virtual const char* name() const throw(); + virtual const char* className() const throw(); + virtual ctkCmdLineModuleRunException* clone() const; + virtual void rethrow() const; + + virtual void raise() const; + +private: + + const QUrl Location; + const int ErrorCode; + const QString ErrorString; + +}; + +#endif // CTKCMDLINEMODULERUNEXCEPTION_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlException.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlException.h index ef4c0af252..21f3454e58 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlException.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlException.h @@ -27,7 +27,9 @@ #include "ctkCommandLineModulesCoreExport.h" /** - * \ingroup CommandLineModulesCore + * \class ctkCmdLineModuleXmlException + * \brief Exception class to describe problems with XML processing. + * \ingroup CommandLineModulesCore_API */ CTK_DECLARE_EXCEPTION(CTK_CMDLINEMODULECORE_EXPORT, ctkCmdLineModuleXmlException, ctkException) diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler.cpp index 797fdae131..01537dbf12 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler.cpp @@ -23,21 +23,25 @@ #include <QXmlStreamReader> +//---------------------------------------------------------------------------- QString ctkCmdLineModuleXmlMsgHandler::statusMessage() const { return Description; } +//---------------------------------------------------------------------------- int ctkCmdLineModuleXmlMsgHandler::line() const { return SourceLocation.line(); } +//---------------------------------------------------------------------------- int ctkCmdLineModuleXmlMsgHandler::column() const { return SourceLocation.column(); } +//---------------------------------------------------------------------------- void ctkCmdLineModuleXmlMsgHandler::handleMessage(QtMsgType type, const QString& description, const QUrl& identifier, const QSourceLocation& sourceLocation) { diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler_p.h index cec4d26285..8ed36dfdae 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler_p.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlMsgHandler_p.h @@ -24,6 +24,11 @@ #include <QAbstractMessageHandler> +/** + * \class QAbstractMessageHandler + * \brief Class to handle an XML message. + * \ingroup CommandLineModulesCore_API + */ class ctkCmdLineModuleXmlMsgHandler : public QAbstractMessageHandler { diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser.cpp index f5a706c237..b0b0ba61a8 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser.cpp @@ -28,9 +28,9 @@ limitations under the License. // CTK includes #include "ctkCmdLineModuleXmlParser_p.h" #include "ctkCmdLineModuleDescription.h" -#include "ctkCmdLineModuleDescriptionPrivate.h" +#include "ctkCmdLineModuleDescription_p.h" #include "ctkCmdLineModuleParameterGroup.h" -#include "ctkCmdLineModuleParameterGroupPrivate.h" +#include "ctkCmdLineModuleParameterGroup_p.h" #include "ctkCmdLineModuleParameterParsers_p.h" #include "ctkCmdLineModuleXmlException.h" @@ -67,7 +67,7 @@ ctkCmdLineModuleXmlParser::ctkCmdLineModuleXmlParser(QIODevice* device, _paramParsers["float-enumeration"] = new ctkCmdLineModuleEnumerationParameterParser; // type="enumerationType"/> _paramParsers["double-enumeration"] = new ctkCmdLineModuleEnumerationParameterParser; // type="enumerationType"/> _paramParsers["file"] = new ctkCmdLineModuleFileParameterParser; // type="fileType"/> - _paramParsers["directory"] = new ctkCmdLineModuleChannelParameterParser; // type="channelType"/> + _paramParsers["directory"] = new ctkCmdLineModuleMultipleParameterParser; // type="multipleType"/> _paramParsers["image"] = new ctkCmdLineModuleImageParameterParser; // type="imageType"/> _paramParsers["geometry"] = new ctkCmdLineModuleGeometryParameterParser; // type="geometryType"/> } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser_p.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser_p.h index af252e9d14..25dfbda30d 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser_p.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlParser_p.h @@ -27,10 +27,16 @@ class ctkCmdLineModuleDescription; class ctkCmdLineModuleParameter; -class ctkCmdLineModuleParameterParser; +struct ctkCmdLineModuleParameterParser; class QIODevice; +/** + * \class ctkCmdLineModuleXmlParser + * \brief Performs XML parsing, loading data into a ctkCmdLineModuleDescription + * \ingroup CommandLineModulesCore_API + * \see ctkCmdLineModuleDescription + */ class ctkCmdLineModuleXmlParser { diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.cpp new file mode 100644 index 0000000000..efd2846144 --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.cpp @@ -0,0 +1,278 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleXmlProgressWatcher.h" + +#include <QIODevice> +#include <QProcess> +#include <QXmlStreamReader> + +#include <QDebug> + +namespace { + +static QString FILTER_START = "filter-start"; +static QString FILTER_NAME = "filter-name"; +static QString FILTER_COMMENT = "filter-comment"; +static QString FILTER_PROGRESS = "filter-progress"; +static QString FILTER_PROGRESS_TEXT = "filter-progress-text"; +static QString FILTER_RESULT = "filter-result"; +static QString FILTER_END = "filter-end"; + +} + +//---------------------------------------------------------------------------- +class ctkCmdLineModuleXmlProgressWatcherPrivate +{ +public: + + ctkCmdLineModuleXmlProgressWatcherPrivate(QIODevice* input, ctkCmdLineModuleXmlProgressWatcher* qq) + : input(input), process(NULL), readPos(0), q(qq), error(false), currentProgress(0) + { + // wrap the content in an artifical root element + reader.addData("<module-root>"); + } + + ctkCmdLineModuleXmlProgressWatcherPrivate(QProcess* input, ctkCmdLineModuleXmlProgressWatcher* qq) + : input(input), process(input), readPos(0), q(qq), error(false), currentProgress(0) + { + // wrap the content in an artifical root element + reader.addData("<module-root>"); + } + + void _q_readyRead() + { + input->seek(readPos); + reader.addData(input->readAll()); + readPos = input->pos(); + parseProgressXml(); + } + + void _q_readyReadError() + { + emit q->errorDataAvailable(process->readAllStandardError()); + } + + void parseProgressXml() + { + QXmlStreamReader::TokenType type = reader.readNext(); + QByteArray outputData; + while(type != QXmlStreamReader::Invalid) + { + switch(type) + { + case QXmlStreamReader::NoToken: break; + case QXmlStreamReader::Characters: + { + if (stack.empty()) + { + QByteArray output(reader.text().toString().toAscii()); + // get rid of a possible newline after the last xml end tag + if (output.startsWith('\n')) output = output.remove(0,1); + outputData.append(output); + break; + } + + if (stack.size() == 2 && + (stack.front() == FILTER_START || stack.front() == FILTER_END)) + { + if (stack.back() == FILTER_NAME) + { + currentName = reader.text().toString().trimmed(); + } + else if (stack.back() == FILTER_COMMENT) + { + currentComment = reader.text().toString().trimmed(); + } + } + else if (stack.size() == 1 && stack.back() == FILTER_PROGRESS) + { + currentProgress = reader.text().toString().toFloat(); + } + else if (stack.size() == 1 && stack.back() == FILTER_PROGRESS_TEXT) + { + currentComment = reader.text().toString(); + } + else if (stack.size() == 1 && stack.back() == FILTER_RESULT) + { + currentResultValue = reader.text().toString(); + } + break; + } + case QXmlStreamReader::StartElement: + { + QStringRef name = reader.name(); + QString parent; + if (!stack.empty()) parent = stack.back(); + + if (name.compare("module-root") != 0) + { + stack.push_back(name.toString()); + } + + if (name.compare(FILTER_START, Qt::CaseInsensitive) == 0 || + name.compare(FILTER_PROGRESS, Qt::CaseInsensitive) == 0 || + name.compare(FILTER_PROGRESS_TEXT, Qt::CaseInsensitive) == 0 || + name.compare(FILTER_RESULT, Qt::CaseInsensitive) == 0 || + name.compare(FILTER_END, Qt::CaseInsensitive) == 0) + { + if (!parent.isEmpty()) + { + unexpectedNestedElement(name.toString()); + break; + } + + if (name.compare(FILTER_START, Qt::CaseInsensitive) == 0) + { + currentName = QString(); + currentComment = QString(); + currentProgress = 0; + } + else if (name.compare(FILTER_PROGRESS_TEXT, Qt::CaseInsensitive) == 0) + { + currentProgress = reader.attributes().value("progress").toString().toFloat(); + } + else if (name.compare(FILTER_RESULT, Qt::CaseInsensitive) == 0) + { + currentResultParameter = reader.attributes().value("name").toString(); + currentResultValue.clear(); + } + } + break; + } + case QXmlStreamReader::EndElement: + { + QStringRef name = reader.name(); + + QString curr; + QString parent; + if (!stack.empty()) + { + curr = stack.back(); + stack.pop_back(); + if (!stack.empty()) parent = stack.back(); + } + + if (parent.isEmpty()) + { + if (!outputData.isEmpty()) + { + emit q->outputDataAvailable(outputData); + outputData.clear(); + } + if (name.compare(FILTER_START, Qt::CaseInsensitive) == 0) + { + emit q->filterStarted(currentName, currentComment); + currentComment = QString(); + } + else if (name.compare(FILTER_PROGRESS, Qt::CaseInsensitive) == 0) + { + emit q->filterProgress(currentProgress, QString()); + } + else if (name.compare(FILTER_PROGRESS_TEXT, Qt::CaseInsensitive) == 0) + { + emit q->filterProgress(currentProgress, currentComment); + currentComment = QString(); + } + else if (name.compare(FILTER_RESULT, Qt::CaseInsensitive) == 0) + { + emit q->filterResult(currentResultParameter, currentResultValue); + } + else if (name.compare(FILTER_END, Qt::CaseInsensitive) == 0) + { + emit q->filterFinished(currentName, currentComment); + currentName = QString(); + currentComment = QString(); + } + } + break; + } + default: + break; + } + type = reader.readNext(); + } + + if (type == QXmlStreamReader::Invalid && reader.error() != QXmlStreamReader::PrematureEndOfDocumentError) + { + if (!error) + { + error = true; + emit q->filterXmlError(QString("Error parsing XML at line %1, column %2: ") + .arg(reader.lineNumber()).arg(reader.columnNumber()) + reader.errorString()); + } + } + } + + void unexpectedNestedElement(const QString& element) + { + if (!error) + { + error = true; + emit q->filterXmlError(QString("\"%1\" must be a top-level element, found at line %2.") + .arg(element).arg(reader.lineNumber())); + } + } + + QIODevice* input; + QProcess* process; + qint64 readPos; + ctkCmdLineModuleXmlProgressWatcher* q; + bool error; + QXmlStreamReader reader; + QList<QString> stack; + QString currentName; + QString currentComment; + float currentProgress; + QString currentResultParameter; + QString currentResultValue; +}; + + +//---------------------------------------------------------------------------- +ctkCmdLineModuleXmlProgressWatcher::ctkCmdLineModuleXmlProgressWatcher(QIODevice* input) + : d(new ctkCmdLineModuleXmlProgressWatcherPrivate(input, this)) +{ + if (d->input == NULL) return; + + if (!(d->input->openMode() & QIODevice::ReadOnly)) + { + input->open(QIODevice::ReadOnly); + } + connect(d->input, SIGNAL(readyRead()), SLOT(_q_readyRead())); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleXmlProgressWatcher::ctkCmdLineModuleXmlProgressWatcher(QProcess* input) + : d(new ctkCmdLineModuleXmlProgressWatcherPrivate(input, this)) +{ + if (d->input == NULL) return; + + connect(input, SIGNAL(readyReadStandardOutput()), SLOT(_q_readyRead())); + connect(input, SIGNAL(readyReadStandardError()), SLOT(_q_readyReadError())); +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleXmlProgressWatcher::~ctkCmdLineModuleXmlProgressWatcher() +{ +} + +#include "moc_ctkCmdLineModuleXmlProgressWatcher.h" diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.h new file mode 100644 index 0000000000..38d95f8ddc --- /dev/null +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlProgressWatcher.h @@ -0,0 +1,74 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEXMLPROGRESSWATCHER_H +#define CTKCMDLINEMODULEXMLPROGRESSWATCHER_H + +#include "ctkCommandLineModulesCoreExport.h" + +#include <QObject> + +class ctkCmdLineModuleXmlProgressWatcherPrivate; + +class QIODevice; +class QProcess; + +/** + * \class ctkCmdLineModuleXmlProgressWatcher + * \brief Provides progress updates of a module. + * \ingroup CommandLineModulesCore_API + * + * This class is usually only used by back-end implementators for modules + * which can report progress and results in the form of XML fragments written + * to a QIODevice. + */ +class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleXmlProgressWatcher : public QObject +{ + Q_OBJECT + +public: + + ctkCmdLineModuleXmlProgressWatcher(QIODevice* input); + ctkCmdLineModuleXmlProgressWatcher(QProcess* input); + ~ctkCmdLineModuleXmlProgressWatcher(); + +Q_SIGNALS: + + void filterStarted(const QString& name, const QString& comment); + void filterProgress(float progress, const QString& comment); + void filterResult(const QString& parameter, const QString& value); + void filterFinished(const QString& name, const QString& comment); + void filterXmlError(const QString& error); + + void outputDataAvailable(const QByteArray& outputData); + void errorDataAvailable(const QByteArray& errorData); + +private: + + friend class ctkCmdLineModuleXmlProgressWatcherPrivate; + + Q_PRIVATE_SLOT(d, void _q_readyRead()) + Q_PRIVATE_SLOT(d, void _q_readyReadError()) + + QScopedPointer<ctkCmdLineModuleXmlProgressWatcherPrivate> d; +}; + +#endif // CTKCMDLINEMODULEXMLPROGRESSWATCHER_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.cpp index cee8736dfa..86f3106d10 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.cpp @@ -30,37 +30,64 @@ #include <QDebug> +//---------------------------------------------------------------------------- +class ctkCmdLineModuleXmlValidatorPrivate +{ +public: + + ctkCmdLineModuleXmlValidatorPrivate() + : Input(NULL), InputSchema(NULL) + {} + + QIODevice* Input; + QIODevice* InputSchema; + + QString ErrorStr; +}; + + +//---------------------------------------------------------------------------- ctkCmdLineModuleXmlValidator::ctkCmdLineModuleXmlValidator(QIODevice *input) - : Input(input), InputSchema(0) + : d(new ctkCmdLineModuleXmlValidatorPrivate) +{ + d->Input = input; +} + +//---------------------------------------------------------------------------- +ctkCmdLineModuleXmlValidator::~ctkCmdLineModuleXmlValidator() { } +//---------------------------------------------------------------------------- void ctkCmdLineModuleXmlValidator::setInput(QIODevice *input) { - Input = input; + d->Input = input; } +//---------------------------------------------------------------------------- QIODevice* ctkCmdLineModuleXmlValidator::input() const { - return Input; + return d->Input; } +//---------------------------------------------------------------------------- void ctkCmdLineModuleXmlValidator::setInputSchema(QIODevice *input) { - InputSchema = input; + d->InputSchema = input; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleXmlValidator::validateInput() { - ErrorStr.clear(); + d->ErrorStr.clear(); - if (!Input) + if (!d->Input) { - ErrorStr = "No input set for validation."; + d->ErrorStr = "No input set for validation."; return false; } - QIODevice* inputSchema = InputSchema; + QIODevice* inputSchema = d->InputSchema; QScopedPointer<QIODevice> defaultInputSchema(new QFile(":/ctkCmdLineModule.xsd")); if (!inputSchema) { @@ -76,16 +103,16 @@ bool ctkCmdLineModuleXmlValidator::validateInput() if (!schema.load(inputSchema)) { QString msg("Invalid input schema at line %1, column %2: %3"); - ErrorStr = msg.arg(errorHandler.line()).arg(errorHandler.column()).arg(errorHandler.statusMessage()); + d->ErrorStr = msg.arg(errorHandler.line()).arg(errorHandler.column()).arg(errorHandler.statusMessage()); return false; } QXmlSchemaValidator validator(schema); - if (!validator.validate(Input)) + if (!validator.validate(d->Input)) { QString msg("Error validating CLI XML description, at line %1, column %2: %3"); - ErrorStr = msg.arg(errorHandler.line()).arg(errorHandler.column()) + d->ErrorStr = msg.arg(errorHandler.line()).arg(errorHandler.column()) .arg(errorHandler.statusMessage()); return false; } @@ -93,12 +120,14 @@ bool ctkCmdLineModuleXmlValidator::validateInput() return true; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleXmlValidator::error() const { - return !ErrorStr.isEmpty(); + return !d->ErrorStr.isEmpty(); } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleXmlValidator::errorString() const { - return ErrorStr; + return d->ErrorStr; } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.h index 1a426a93df..759ac53cef 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.h @@ -24,12 +24,17 @@ #include <ctkCommandLineModulesCoreExport.h> -#include <QString> +#include <QScopedPointer> + +class ctkCmdLineModuleXmlValidatorPrivate; class QIODevice; /** - * \ingroup CommandLineModulesCore + * @ingroup CommandLineModulesCore_API + * + * @brief Provides validation of an XML document against an XML schema. + * */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleXmlValidator { @@ -37,23 +42,48 @@ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleXmlValidator public: ctkCmdLineModuleXmlValidator(QIODevice* input = 0); + ~ctkCmdLineModuleXmlValidator(); + /** + * @brief Set the XML input. + * @param input The Xml input. + */ void setInput(QIODevice* input); + + /** + * @brief Get the XML input device. + * @return The XML input device. + */ QIODevice* input() const; + /** + * @brief Set the XML schema to be used during validation. + * @param input The XML schema. + */ void setInputSchema(QIODevice* input); + /** + * @brief Validate the XML input against the XML schema set via setInputSchema(). + * @return \c true if validation was successful, \c false otherwise. + */ virtual bool validateInput(); + /** + * @brief Get the error flag. + * @return \c true if an error occured during validation, \c false otherwise. + */ virtual bool error() const; + + /** + * @brief Get the error string. + * @return A description of the validation error, if any. + */ virtual QString errorString() const; private: - QIODevice* Input; - QIODevice* InputSchema; + QScopedPointer<ctkCmdLineModuleXmlValidatorPrivate> d; - QString ErrorStr; }; #endif // CTKCMDLINEMODULEXMLVALIDATOR_H diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.cpp b/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.cpp index afff3d67d7..aa9906a6bc 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.cpp +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.cpp @@ -1,70 +1,171 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -=============================================================================*/ - -#include "ctkCmdLineModuleXslTransform.h" -#include "ctkCmdLineModuleXmlMsgHandler_p.h" +=============================================================================*/ -#include <QFile> +// Qt includes #include <QBuffer> +#include <QDebug> +#include <QFile> +#include <QXmlQuery> #include <QXmlSchema> #include <QXmlSchemaValidator> -#include <QXmlQuery> +#include <QXmlFormatter> -#include <QDebug> +// CTK includes +#include "ctkCmdLineModuleXslTransform.h" +#include "ctkCmdLineModuleXmlMsgHandler_p.h" + +//---------------------------------------------------------------------------- +class ctkCmdLineModuleXslTransformPrivate +{ +public: + + ctkCmdLineModuleXslTransformPrivate(QIODevice *output) + : Validate(false) + , Format(false) + , OutputSchema(0) + , Transformation(0) + , Output(output) + , XslTransform(QXmlQuery::XSLT20) + { + this->XslTransform.setMessageHandler(&this->MsgHandler); + } + + bool validateOutput(); + + bool Validate; + bool Format; + + QIODevice* OutputSchema; + QIODevice* Transformation; + QIODevice* Output; + + QXmlQuery XslTransform; + QList<QIODevice*> ExtraTransformations; + ctkCmdLineModuleXmlMsgHandler MsgHandler; + + QString ErrorStr; +}; + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleXslTransformPrivate::validateOutput() +{ + this->ErrorStr.clear(); + + QIODevice* outputSchema = this->OutputSchema; + + // If there is no custom schema for validating the output, we just return. + // The QtDesigner.xsd schema below (which should be the default) exhausts + // the memory during validation. + if (!outputSchema) return true; + + QScopedPointer<QIODevice> defaultOutputSchema(new QFile(":/QtDesigner.xsd")); + if (!outputSchema) + { + outputSchema = defaultOutputSchema.data(); + outputSchema->open(QIODevice::ReadOnly); + } + outputSchema->reset(); + + QXmlSchema schema; + + ctkCmdLineModuleXmlMsgHandler msgHandler; + schema.setMessageHandler(&msgHandler); + + if (!schema.load(outputSchema)) + { + QString msg("Invalid output schema at line %1, column %2: %3"); + ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()).arg(msgHandler.statusMessage()); + return false; + } + QXmlSchemaValidator validator(schema); + validator.setMessageHandler(&msgHandler); + + if (!validator.validate(Output)) + { + QString msg("Error validating transformed XML input, at line %1, column %2: %3"); + ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()) + .arg(msgHandler.statusMessage()); + return false; + } + + return true; +} +//---------------------------------------------------------------------------- ctkCmdLineModuleXslTransform::ctkCmdLineModuleXslTransform(QIODevice *input, QIODevice *output) - : ctkCmdLineModuleXmlValidator(input), - Validate(false), OutputSchema(0), Transformation(0), Output(output) + : ctkCmdLineModuleXmlValidator(input) + , d(new ctkCmdLineModuleXslTransformPrivate(output)) { } +//---------------------------------------------------------------------------- +ctkCmdLineModuleXslTransform::~ctkCmdLineModuleXslTransform() +{ +} + +//---------------------------------------------------------------------------- void ctkCmdLineModuleXslTransform::setOutput(QIODevice* output) { - Output = output; + d->Output = output; } +//---------------------------------------------------------------------------- QIODevice* ctkCmdLineModuleXslTransform::output() const { - return Output; + return d->Output; } +//---------------------------------------------------------------------------- void ctkCmdLineModuleXslTransform::setOutputSchema(QIODevice *output) { - OutputSchema = output; + d->OutputSchema = output; +} + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleXslTransform::formatXmlOutput() const +{ + return d->Format; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleXslTransform::setFormatXmlOutput(bool format) +{ + d->Format = format; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleXslTransform::transform() { - if (!Output) + d->ErrorStr.clear(); + + if (!d->Output) { - ErrorStr = "No output device set"; + d->ErrorStr = "No output device set"; return false; } - - QIODevice* inputDevice = input(); + QIODevice* inputDevice = this->input(); if (!inputDevice) { - ErrorStr = "No input set for deriving an output."; + d->ErrorStr = "No input set for deriving an output."; return false; } else if (!(inputDevice->openMode() & QIODevice::ReadOnly)) @@ -73,123 +174,130 @@ bool ctkCmdLineModuleXslTransform::transform() } inputDevice->reset(); - ctkCmdLineModuleXmlMsgHandler msgHandler; - QXmlQuery xslTransform(QXmlQuery::XSLT20); - xslTransform.setMessageHandler(&msgHandler); - if (!xslTransform.setFocus(inputDevice)) + if (!d->XslTransform.setFocus(inputDevice)) { QString msg("Error transforming XML input: %1"); - ErrorStr = msg.arg(msgHandler.statusMessage()); + d->ErrorStr = msg.arg(d->MsgHandler.statusMessage()); + return false; + } + + if (!d->Transformation) + { + d->ErrorStr = "No XSL transformation set."; return false; } - QIODevice* transformation = Transformation; - QScopedPointer<QIODevice> defaultTransform(new QFile(":/ctkCmdLineModuleXmlToQtUi.xsl")); - if (!transformation) + d->Transformation->open(QIODevice::ReadOnly); + QString query(d->Transformation->readAll()); + QString extra; + foreach(QIODevice* extraIODevice, d->ExtraTransformations) { - transformation = defaultTransform.data(); - transformation->open(QIODevice::ReadOnly); + extraIODevice->open(QIODevice::ReadOnly); + extra += extraIODevice->readAll(); } - xslTransform.setQuery(transformation); + query.replace("<!-- EXTRA TRANSFORMATIONS -->", extra); +#if 0 + qDebug() << query; +#endif + d->XslTransform.setQuery(query); bool closeOutput = false; - if (!(Output->openMode() & QIODevice::WriteOnly)) + if (!(d->Output->openMode() & QIODevice::WriteOnly)) { - Output->open(QIODevice::WriteOnly); + d->Output->open(QIODevice::WriteOnly); closeOutput = true; } - if (!xslTransform.evaluateTo(Output)) + QScopedPointer<QXmlSerializer> xmlSerializer; + if (d->Format) + { + xmlSerializer.reset(new QXmlFormatter(d->XslTransform, d->Output)); + } + else + { + xmlSerializer.reset(new QXmlSerializer(d->XslTransform, d->Output)); + } + + if (!d->XslTransform.evaluateTo(xmlSerializer.data())) { QString msg("Error transforming XML input, at line %1, column %2: %3"); - ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()) - .arg(msgHandler.statusMessage()); + d->ErrorStr = msg.arg(d->MsgHandler.line()).arg(d->MsgHandler.column()) + .arg(d->MsgHandler.statusMessage()); return false; } +#if 0 + qDebug() << d->Output; +#endif + if (closeOutput) { - Output->close(); + d->Output->close(); } else { - Output->reset(); + d->Output->reset(); } - if (Validate) + if (d->Validate) { - return validateOutput(); + return d->validateOutput(); } return true; } +//---------------------------------------------------------------------------- void ctkCmdLineModuleXslTransform::setXslTransformation(QIODevice *transformation) { - Transformation = transformation; + d->Transformation = transformation; } -void ctkCmdLineModuleXslTransform::setValidateOutput(bool validate) +//---------------------------------------------------------------------------- +void ctkCmdLineModuleXslTransform::bindVariable(const QString& name, const QVariant& value) { - Validate = validate; + d->XslTransform.bindVariable(name, value); } -bool ctkCmdLineModuleXslTransform::validateOutput() +//---------------------------------------------------------------------------- +void ctkCmdLineModuleXslTransform::setXslExtraTransformation(QIODevice* transformation) { - ErrorStr.clear(); - - QIODevice* outputSchema = OutputSchema; - - // If there is no custom schema for validating the output, we just return. - // The QtDesigner.xsd schema below (which should be the default) exhausts - // the memory during validation. - if (!outputSchema) return true; - - QScopedPointer<QIODevice> defaultOutputSchema(new QFile(":/QtDesigner.xsd")); - if (!outputSchema) - { - outputSchema = defaultOutputSchema.data(); - outputSchema->open(QIODevice::ReadOnly); - } - outputSchema->reset(); - - QXmlSchema schema; - - ctkCmdLineModuleXmlMsgHandler msgHandler; - schema.setMessageHandler(&msgHandler); - - if (!schema.load(outputSchema)) - { - QString msg("Invalid output schema at line %1, column %2: %3"); - ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()).arg(msgHandler.statusMessage()); - return false; - } + QList<QIODevice*> transformations; + transformations<<transformation; + this->setXslExtraTransformations(transformations); +} - QXmlSchemaValidator validator(schema); - validator.setMessageHandler(&msgHandler); +//---------------------------------------------------------------------------- +void ctkCmdLineModuleXslTransform::setXslExtraTransformations(const QList<QIODevice *>& transformations) +{ + d->ExtraTransformations = transformations; +} - if (!validator.validate(Output)) - { - QString msg("Error validating transformed XML input, at line %1, column %2: %3"); - ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column()) - .arg(msgHandler.statusMessage()); - return false; - } +//---------------------------------------------------------------------------- +void ctkCmdLineModuleXslTransform::setValidateOutput(bool validate) +{ + d->Validate = validate; +} - return true; +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleXslTransform::validateOutput() const +{ + return d->Validate; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleXslTransform::error() const { - return ctkCmdLineModuleXmlValidator::error() || !ErrorStr.isEmpty(); + return ctkCmdLineModuleXmlValidator::error() || !d->ErrorStr.isEmpty(); } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleXslTransform::errorString() const { - QString errStr = ctkCmdLineModuleXmlValidator::errorString(); + QString errStr = this->ctkCmdLineModuleXmlValidator::errorString(); if (errStr.isEmpty()) { - return ErrorStr; + return d->ErrorStr; } return errStr; } diff --git a/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.h b/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.h index c99d6915f1..6fb0fdf58c 100644 --- a/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.h +++ b/Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.h @@ -1,78 +1,173 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ -#ifndef CTKCMDLINEMODULEXSLTRANSFORM_H -#define CTKCMDLINEMODULEXSLTRANSFORM_H +#ifndef __ctkCmdLineModuleXslTransform_h +#define __ctkCmdLineModuleXslTransform_h +// CTK includes #include "ctkCommandLineModulesCoreExport.h" #include "ctkCmdLineModuleXmlValidator.h" +class ctkCmdLineModuleXslTransformPrivate; -#include <QString> - +// Qt includes class QIODevice; /** - * \ingroup CommandLineModulesCore + * @ingroup CommandLineModulesCore_API + * + * @brief Transforms a given XML input using an XML stylesheet. + * + * You must call setInput(), setOutput() and setXslTransformation() before + * calling transform(). + * + * @see ctkCmdLineModuleXmlValidator */ class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleXslTransform - : public ctkCmdLineModuleXmlValidator + : public ctkCmdLineModuleXmlValidator { public: ctkCmdLineModuleXslTransform(QIODevice* input = 0, QIODevice* output = 0); + virtual ~ctkCmdLineModuleXslTransform(); + /** + * @brief Set the output device to which the transformation will be written. + * @param output The output device. + * + * If no output device is set, a default device will be used. + */ void setOutput(QIODevice* output); + + /** + * @brief Get the output device to which the transformation will be written. + * @return The output device. + */ QIODevice* output() const; + /** + * @brief Set an XML schema for output validation. + * @param output The XML schema against which the transformation will be validated. + * + * Output validation will only be done if validateOutput() returns \c true. + */ void setOutputSchema(QIODevice* output); + /** + * @brief Returns \c true if the XSL output will be formatted. + * @return \c true if the ouptut will be formatted, \c false otherwise. + */ + bool formatXmlOutput() const; + + /** + * @brief Formats the XSL output to be human-readable. + * + * It is assumed that the XSL output is valid XML. The output will be + * formatted with an indentation depth of four spaces. Note that setting + * \c format to \c true increases compuational overhead and memory + * requirements and is usually only done for testing or debugging purposes. + */ + void setFormatXmlOutput(bool format); + /** * @brief Transforms an XML input via a XSL transformation. * * This method assumes that the input set via setInput() or supplied - * in the constructor is a valid XML fragment. + * in the constructor is a valid, non empty XML fragment and that setOutput() + * and setXslTransformation() was called with non-null arguments. * * @return */ bool transform(); + /** + * @brief Sets the XSL transformation. + * + * Use this method to set the XSL transformation for this instance. Trying + * to transform the input without setting a transformation will result in + * runtime errors. + * + * @param transformation The XSL transformation. + */ void setXslTransformation(QIODevice* transformation); + /** + * @brief XSL to be injected in the main XSL. + * + * This can be used to potentially overwrite templates. + * To avoid ambiguity, specify a priority > 1 in your overwriting templates + * + * \param transformation Extra XSL transformation fragment. + */ + void setXslExtraTransformation(QIODevice* transformation); + void setXslExtraTransformations(const QList<QIODevice*>& transformations); + + /** + * @brief Binds the variable name to the value so that $name can be used + * from within the query to refer to the value. + * In the default XslTransformation, the widget classes are variable and can + * be replaced with a new binding. + * @sa QXmlQuery::bindVariable() + */ + void bindVariable(const QString& name, const QVariant& value); + + /** + * @brief Sets the output validation mode. + * @param validate If \c true, the output will be validated against the XML schema + * provided via setOutputSchema(). If \c validate is \c false, no output + * validation takes place. + */ void setValidateOutput(bool validate); - bool error() const; - QString errorString() const; + /** + * @brief Get the output validation mode. + * @return \c true if the output will be validated, \c false otherwise. + */ + bool validateOutput() const; -private: + /** + * @brief Returns true if an error occured. + * + * transform() sets the error flag if an error occured when transforming the + * XML file into XSL or validating the transformation. + * + * @sa errorString + */ + virtual bool error() const; - bool validateOutput(); + /** + * @brief Returns the error message if any. + * + * transform() sets the error message if an error occured when transforming + * the XML file into XSL. + * + * @sa error + */ + virtual QString errorString() const; - bool Validate; +private: - QIODevice* OutputSchema; - QIODevice* Transformation; + QScopedPointer<ctkCmdLineModuleXslTransformPrivate> d; - QIODevice* Output; - QString ErrorStr; }; + #endif // CTKCMDLINEMODULEXSLTRANSFORM_H diff --git a/Libs/CommandLineModules/Documentation/Backends.md b/Libs/CommandLineModules/Documentation/Backends.md new file mode 100644 index 0000000000..d408cef696 --- /dev/null +++ b/Libs/CommandLineModules/Documentation/Backends.md @@ -0,0 +1,7 @@ +Backends {#CommandLineModulesBackEnds_Page} +======== + +Here is a list of available back-ends: + +- \subpage CommandLineModulesBackendFunctionPointer_Page +- \subpage CommandLineModulesBackendLocalProcess_Page diff --git a/Libs/CommandLineModules/Documentation/CMakeLists.txt b/Libs/CommandLineModules/Documentation/CMakeLists.txt new file mode 100644 index 0000000000..635d769ca2 --- /dev/null +++ b/Libs/CommandLineModules/Documentation/CMakeLists.txt @@ -0,0 +1,12 @@ + +# Copy the xs3p.xsl stylesheet which is used in .xsd files to generate nice +# html output when opened in a browser. +execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/xs3p.xsl + ${CTK_BINARY_DIR}/Documentation/html/xs3p.xsl) + +if (BUILD_TESTING AND + CTK_LIB_CommandLineModules/Core AND CTK_LIB_CommandLineModules/Backend/LocalProcess) + + # Compile source code snippets + add_subdirectory(Snippets) +endif() diff --git a/Libs/CommandLineModules/Documentation/CTKCommandLineModules.dox b/Libs/CommandLineModules/Documentation/CTKCommandLineModules.dox index c138e9ec89..9be6780bc6 100644 --- a/Libs/CommandLineModules/Documentation/CTKCommandLineModules.dox +++ b/Libs/CommandLineModules/Documentation/CTKCommandLineModules.dox @@ -1,18 +1,54 @@ /** -\defgroup CommandLineModules Command Line Modules +\defgroup CommandLineModules_Group Command Line Modules \ingroup Projects -CTK provides an API for interfacing with command line modules through a -self-describing XML description of the supported command line arguments. +See the sub-modules for detailed API documentation of the available front and back-ends. -\defgroup CommandLineModulesCore Command Line Module Support +For a high-level overview, see \ref CommandLineModules_Page. + +\defgroup CommandLineModulesCore_API Command Line Module Core API +\ingroup Libs +\ingroup CommandLineModules_Group + +This is a list of types provided by the CTK Command Line Module Core library. See the +\ref CommandLineModulesCore_Page library page for general information. + +\defgroup CommandLineModulesBackEnd_Group Command Line Module Back-Ends \ingroup Libs -\ingroup CommandLineModules +\ingroup CommandLineModules_Group + +This module groups back-end implementations. See ctkCmdLineModuleBackend for details about back-ends. + +\defgroup CommandLineModulesBackendFunctionPointer_API Function Pointer API (experimental) +\ingroup CommandLineModulesBackEnd_Group + +This is a list of types provided by the CTK Command Line Module Function Pointer Backend library. See the +\ref CommandLineModulesBackendFunctionPointer_Page library page for general information. -\defgroup CommandLineModulesQtGui Command Line Module QtGui Support +\defgroup CommandLineModulesBackendLocalProcess_API Local Process API +\ingroup CommandLineModulesBackEnd_Group + +This is a list of types provided by the CTK Command Line Module Local Process Backend library. See the +\ref CommandLineModulesBackendLocalProcess_Page library page for general information. + +\defgroup CommandLineModulesFrontEnd_Group Command Line Module Front-Ends \ingroup Libs -\ingroup CommandLineModules +\ingroup CommandLineModules_Group + +This module groups front-end implementations. See ctkCmdLineModuleFrontend for details about front-ends. + +\defgroup CommandLineModulesFrontendQtGui_API QtGui API +\ingroup CommandLineModulesFrontEnd_Group + +This is a list of types provided by the CTK Command Line Module Qt Gui Frontend library. See the +\ref CommandLineModulesFrontendQtGui_Page library page for general information. + +\defgroup CommandLineModulesFrontendQtWebKit_API QtWebKit API (experimental) +\ingroup CommandLineModulesFrontEnd_Group + +This is a list of types provided by the CTK Command Line Module Qt WebKit Frontend library. See the +\ref CommandLineModulesFrontendQtWebKit_Page library page for general information. */ diff --git a/Libs/CommandLineModules/Documentation/Frontends.md b/Libs/CommandLineModules/Documentation/Frontends.md new file mode 100644 index 0000000000..b730296704 --- /dev/null +++ b/Libs/CommandLineModules/Documentation/Frontends.md @@ -0,0 +1,7 @@ +Frontends {#CommandLineModulesFrontEnds_Page} +========= + +Here is a list of available front-ends: + +- \subpage CommandLineModulesFrontendQtGui_Page +- \subpage CommandLineModulesFrontendQtWebKit_Page diff --git a/Libs/CommandLineModules/Documentation/Snippets/CMakeLists.txt b/Libs/CommandLineModules/Documentation/Snippets/CMakeLists.txt new file mode 100644 index 0000000000..fc9df1623d --- /dev/null +++ b/Libs/CommandLineModules/Documentation/Snippets/CMakeLists.txt @@ -0,0 +1,19 @@ + +set(_base_src_include_dir ${CMAKE_SOURCE_DIR}/Libs/CommandLineModules) +set(_base_bin_include_dir ${CMAKE_BINARY_DIR}/Libs/CommandLineModules) + +include_directories( + ${CTKCore_SOURCE_DIR} + ${CTKCore_BINARY_DIR} + ${_base_src_include_dir}/Core + ${_base_bin_include_dir}/Core + ${_base_src_include_dir}/Backend/LocalProcess + ${_base_bin_include_dir}/Backend/LocalProcess + ${_base_src_include_dir}/Frontend/QtGui + ${_base_bin_include_dir}/Frontend/QtGui +) + +ctkFunctionCompileSnippets("${CMAKE_CURRENT_SOURCE_DIR}" + CTKCommandLineModulesBackendLocalProcess + CTKCommandLineModulesFrontendQtGui +) diff --git a/Libs/CommandLineModules/Documentation/Snippets/ModuleManager/main.cpp b/Libs/CommandLineModules/Documentation/Snippets/ModuleManager/main.cpp new file mode 100644 index 0000000000..205a6aaafb --- /dev/null +++ b/Libs/CommandLineModules/Documentation/Snippets/ModuleManager/main.cpp @@ -0,0 +1,112 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleBackendLocalProcess.h" +#include "ctkCmdLineModuleFrontendFactoryQtGui.h" +#include "ctkCmdLineModuleFrontendQtGui.h" +#include "ctkCmdLineModuleFuture.h" +#include "ctkException.h" +#include "ctkCmdLineModuleRunException.h" + +#include <QApplication> +#include <QDesktopServices> +#include <QWidget> +#include <QUrl> +#include <QDebug> + +#include <cstdlib> + +int main(int argc, char** argv) +{ + QApplication myApp(argc, argv); + myApp.setOrganizationName("CommonTK"); + myApp.setApplicationName("ModuleManagerSnippet"); + + // [instantiate-mm] + // Instantiate a ctkCmdLineModuleManager class. + ctkCmdLineModuleManager moduleManager( + // Use "strict" validation mode, rejecting modules with non-valid XML descriptions. + ctkCmdLineModuleManager::STRICT_VALIDATION, + // Use the default cache location for this application + QDesktopServices::storageLocation(QDesktopServices::CacheLocation) + ); + // [instantiate-mm] + + // [register-backend] + // Instantiate a back-end for running executable modules in a local process. + // This back-end handles the "file" Url scheme. + QScopedPointer<ctkCmdLineModuleBackend> processBackend(new ctkCmdLineModuleBackendLocalProcess); + + // Register the back-end with the module manager. + moduleManager.registerBackend(processBackend.data()); + // [register-backend] + + // [register-module] + ctkCmdLineModuleReference moduleRef; + try + { + // Register a local executable as a module, the ctkCmdLineModuleBackendLocalProcess + // can handle it. + moduleRef = moduleManager.registerModule(QUrl::fromLocalFile("C:/modules/MyModule.exe")); + } + catch (const ctkInvalidArgumentException& e) + { + // Module validation failed. + qDebug() << e; + return EXIT_FAILURE; + } + // [register-module] + + // [create-frontend] + // We use the "Qt Gui" frontend factory. + QScopedPointer<ctkCmdLineModuleFrontendFactory> frontendFactory(new ctkCmdLineModuleFrontendFactoryQtGui); + myApp.addLibraryPath(QCoreApplication::applicationDirPath() + "/../"); + + QScopedPointer<ctkCmdLineModuleFrontend> frontend(frontendFactory->create(moduleRef)); + + // Create the actual GUI representation. + QWidget* gui = qobject_cast<QWidget*>(frontend->guiHandle()); + // [create-frontend] + Q_UNUSED(gui); + + // Now try and run the module (using the default values for the parameters) + // and print out any reported output and results. + // [run-module] + try + { + ctkCmdLineModuleFuture future = moduleManager.run(frontend.data()); + future.waitForFinished(); + qDebug() << "Console output:"; + qDebug() << future.readAllOutputData(); + qDebug() << "Error output:"; + qDebug() << future.readAllErrorData(); + qDebug() << "Results:"; + qDebug() << future.results(); + } + catch (const ctkCmdLineModuleRunException& e) + { + qWarning() << e; + } + // [run-module] + + return EXIT_SUCCESS; +} diff --git a/Libs/CommandLineModules/Documentation/xs3p.xsl b/Libs/CommandLineModules/Documentation/xs3p.xsl new file mode 100644 index 0000000000..f9b1e71ac8 --- /dev/null +++ b/Libs/CommandLineModules/Documentation/xs3p.xsl @@ -0,0 +1,8521 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 2002 + + The software contained on this media is the property of the + DSTC Pty Ltd. Use of this software is strictly in accordance + with the license agreement in the accompanying LICENSE file. + If your distribution of this software does not contain a + LICENSE file then you have no rights to use this software + in any manner and should contact DSTC at the address below + to determine an appropriate licensing arrangement. + + DSTC Pty Ltd + Level 7, General Purpose South + The University of Queensland + QLD 4072 Australia + Tel: +61 7 3365 4310 + Fax: +61 7 3365 4311 + Email: titanium_enquiries@dstc.edu.au + + This software is being provided "AS IS" without warranty of + any kind. In no event shall DSTC Pty Ltd be liable for + damage of any kind arising out of or in connection with + the use or performance of this software. +--> +<!-- + File: + xs3p.xsl + Description: + Stylesheet that generates XHTML documentation, given an XML + Schema document + Assumptions: + -Resulting documentation will only be displayed properly with + the latest browsers that support XHTML and CSS. Older + browsers are not supported. + -Assumed that XSD document conforms to the XSD recommendation. + No validity checking is done. + Constraints: + -Local schema components cannot contain two dashes in + 'documentation' elements within their 'annotation' element. + This is because the contents of those 'documentation' + elements are displayed in a separate window using Javascript. + This Javascript code is enclosed in comments, which do not + allow two dashes inside themselves. + Notes: + -Javascript code is placed within comments, even though in + strict XHTML, JavaScript code should be placed within CDATA + sections. This is because current browsers generate a syntax + error if the page contains CDATA sections. Placing Javascript + code within comments means that the code cannot contain two + dashes. + (See 'PrintJSCode' named template.) + Stylesheet Sections: + -Global Parameters + Specify parameters that can be set externally to customise + stylesheet + -Constants + Constants used by the stylesheet + -Main Document + Templates to generate the overall document and the top-level + sections within it + -Hierarchy table + Templates for displaying type hierarchy for simple and + complex types, and substitution group hierarchy for elements + -Properties table + Templates for displaying the properties of top-level schema + components + -XML Instance Representation table + Templates for displaying how an XML instance conforming to + the schema component would look like + -Schema Component Representation table + Templates for displaying the XML representation of the schema + component + -XML Pretty Printer + Templates for displaying arbitrary XML instances + -Handling Schema Component References + Templates for generating internal and external links + -General Utility Templates + General templates used by other templates in this stylesheet + To Do List: + -It is not clever when printing out element and attribute + wildcards in the XML Instance Representation tables. It prints + out all wildcards, rather than working out the actual wildcard + is from multiple wildcard instances. + -Same as above for simple type constraints, e.g. it doesn't + summarise multiple pattern constraints. +--> +<xsl:stylesheet + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns="http://www.w3.org/1999/xhtml" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:ppp="http://titanium.dstc.edu.au/xml/xs3p" + xmlns:xs3p="http://titanium.dstc.edu.au/xml/xs3p" + version="1.0" + exclude-result-prefixes="xsd ppp html"> + + <xsl:output + method="xml" + encoding="ISO-8859-1" + standalone="yes" + version="1.0" + doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" + doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" + indent="yes"/> + + <xsl:key name="type" match="/xsd:schema/xsd:complexType | /xsd:schema/xsd:simpleType | /xsd:schema/xsd:redefine/xsd:complexType | /xsd:schema/xsd:redefine/xsd:simpleType" use="@name" /> + <xsl:key name="complexType" match="/xsd:schema/xsd:complexType | /xsd:schema/xsd:redefine/xsd:complexType" use="@name" /> + <xsl:key name="simpleType" match="/xsd:schema/xsd:simpleType | /xsd:schema/xsd:redefine/xsd:simpleType" use="@name" /> + <xsl:key name="attributeGroup" match="/xsd:schema/xsd:attributeGroup | /xsd:schema/xsd:redefine/xsd:attributeGroup" use="@name" /> + <xsl:key name="group" match="/xsd:schema/xsd:group | /xsd:schema/xsd:redefine/xsd:group" use="@name" /> + <xsl:key name="attribute" match="/xsd:schema/xsd:attribute" use="@name" /> + <xsl:key name="element" match="/xsd:schema/xsd:element" use="@name" /> + + <!-- ******** Global Parameters ******** --> + + <!-- Title of XHTML document. --> + <xsl:param name="title" select="/xsd:schema/xsd:annotation/xsd:appinfo/xs3p:title"></xsl:param> + + <!-- If 'true', sorts the top-level schema components by type, + then name. Otherwise, displays the components by the order that + they appear in the schema. --> + <xsl:param name="sortByComponent">true</xsl:param> + + <!-- If 'true', XHTML document uses JavaScript for added + functionality, such as pop-up windows and information- + hiding. + Otherwise, XHTML document does not use JavaScript. --> + <xsl:param name="useJavaScript">true</xsl:param> + + <!-- If 'true', prints all super-types in the + type hierarchy box. + Otherwise, prints the parent type only in the + type hierarchy box. --> + <xsl:param name="printAllSuperTypes">true</xsl:param> + + <!-- If 'true', prints all sub-types in the + type hierarchy box. + Otherwise, prints the direct sub-types only in the + type hierarchy box. --> + <xsl:param name="printAllSubTypes">true</xsl:param> + + <!-- If 'true', prints out the Glossary section. --> + <xsl:param name="printGlossary" select="/xsd:schema/xsd:annotation/xsd:appinfo/xs3p:printGlossary">true</xsl:param> + + <!-- If 'true', prints out the Legend section. --> + <xsl:param name="printLegend" select="/xsd:schema/xsd:annotation/xsd:appinfo/xs3p:printLegend">true</xsl:param> + + <!-- If 'true', prints prefix matching namespace of schema + components in XML Instance Representation tables. --> + <xsl:param name="printNSPrefixes">true</xsl:param> + + <!-- If 'true', searches 'included' schemas for schema components + when generating links and XML Instance Representation tables. --> + <xsl:param name="searchIncludedSchemas">false</xsl:param> + + <!-- If 'true', searches 'imported' schemas for schema components + when generating links and XML Instance Representation tables. --> + <xsl:param name="searchImportedSchemas">false</xsl:param> + + <!-- File containing the mapping from file locations of external + (e.g. included, imported, refined) schemas to file locations + of their XHTML documentation. --> + <xsl:param name="linksFile"></xsl:param> + + <!-- Set the base URL for resolving links. --> + <xsl:param name="baseURL"></xsl:param> + + <!-- Uses an external CSS stylesheet rather than using + internally-declared CSS properties. --> + <xsl:param name="externalCSSURL"></xsl:param> + + + <!-- ******** Constants ******** --> + + <!-- XML Schema Namespace --> + <xsl:variable name="XSD_NS">http://www.w3.org/2001/XMLSchema</xsl:variable> + + <!-- XML Namespace --> + <xsl:variable name="XML_NS">http://www.w3.org/XML/1998/namespace</xsl:variable> + + <!-- Number of 'em' to indent from parent element's start tag to + child element's start tag --> + <xsl:variable name="ELEM_INDENT">1.5</xsl:variable> + + <!-- Number of 'em' to indent from parent element's start tag to + attribute's tag --> + <xsl:variable name="ATTR_INDENT">0.5</xsl:variable> + + <!-- Title to use if none provided --> + <xsl:variable name="DEFAULT_TITLE">XML Schema Documentation</xsl:variable> + + <!-- Prefixes used for anchor names --> + <!-- Type definitions --> + <xsl:variable name="TYPE_PREFIX">type_</xsl:variable> + <!-- Attribute declarations --> + <xsl:variable name="ATTR_PREFIX">attribute_</xsl:variable> + <!-- Attribute group definitions --> + <xsl:variable name="ATTR_GRP_PREFIX">attributeGroup_</xsl:variable> + <!-- Complex type definitions --> + <xsl:variable name="CTYPE_PREFIX" select="$TYPE_PREFIX"/> + <!-- Element declarations --> + <xsl:variable name="ELEM_PREFIX">element_</xsl:variable> + <!-- Key definitions --> + <xsl:variable name="KEY_PREFIX">key_</xsl:variable> + <!-- Group definitions --> + <xsl:variable name="GRP_PREFIX">group_</xsl:variable> + <!-- Notations --> + <xsl:variable name="NOTA_PREFIX">notation_</xsl:variable> + <!-- Namespace declarations --> + <xsl:variable name="NS_PREFIX">ns_</xsl:variable> + <!-- Simple type definitions --> + <xsl:variable name="STYPE_PREFIX" select="$TYPE_PREFIX"/> + <!-- Glossary terms --> + <xsl:variable name="TERM_PREFIX">term_</xsl:variable> + + <!-- The original schema needs to be stored because when + calculating links for references, the links have to be + relative to the original schema. See 'PrintCompRef' + template. --> + <xsl:variable name="ORIGINAL_SCHEMA" select="/xsd:schema"/> + + <!-- ******** Main Document ******** --> + + <!-- + Main template that starts the process + --> + <xsl:template match="/xsd:schema"> + <!-- Check that links file is provided if searching external + schemas for components. --> + <xsl:if test="$linksFile='' and (normalize-space(translate($searchIncludedSchemas, 'TRUE', 'true'))='true' or normalize-space(translate($searchImportedSchemas, 'TRUE', 'true'))='true')"> + <xsl:call-template name="HandleError"> + <xsl:with-param name="isTerminating">true</xsl:with-param> + <xsl:with-param name="errorMsg"> +'linksFile' variable must be provided if either +'searchIncludedSchemas' or 'searchImportedSchemas' is true. + </xsl:with-param> + </xsl:call-template> + </xsl:if> + + <!-- Get title of document --> + <xsl:variable name="actualTitle"> + <xsl:choose> + <xsl:when test="$title != ''"> + <xsl:value-of select="$title"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$DEFAULT_TITLE"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <html> + <head> + <!-- Set title bar --> + <title><xsl:value-of select="$actualTitle"/> + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +

Table of Contents

+ + + + +

Schema Document Properties

+ + + +

Declared Namespaces

+ + + + + + + + + +

Redefined Schema Components

+ +
+ + + + + + + +

Global Declarations

+ + + + +
+ + +

Global Definitions

+ + + + +
+
+ + +

Global Schema Components

+ +
+
+ + + +
+

Legend

+ + +
+
+ + + +
+

Glossary

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PrefixNamespace
+ Default namespace + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
  • + + + + + : + + +
  • +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +var pc = getElementObject("printerControls"); +if (pc != null) { + pc.style.display="block"; +} + + + + + + + +var gc = getElementObject("globalControls"); +if (gc != null) { + gc.style.display="block"; +} + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + +

    + + + + + + + + + + + + + : + + + + +

    +
    + + + + + +
    +
    + + + + + /* IDs of XML Instance Representation boxes */ + + var xiBoxes = new Array( + + + , + + ' + + + + _xibox' + + ); + + + /* IDs of Schema Component Representation boxes */ + + var scBoxes = new Array('schema_scbox' + + , ' + + + + _scbox' + + ); + + + +/** + * Can get the ID of the button controlling + * a collapseable box by concatenating + * this string onto the ID of the box itself. + */ +var B_SFIX = "_button"; + +/** + * Counter of documentation windows + * Used to give each window a unique name + */ +var windowCount = 0; + +/** + * Returns an element in the current HTML document. + * + * @param elementID Identifier of HTML element + * @return HTML element object + */ +function getElementObject(elementID) { + var elemObj = null; + if (document.getElementById) { + elemObj = document.getElementById(elementID); + } + return elemObj; +} + +/** + * Closes a collapseable box. + * + * @param boxObj Collapseable box + * @param buttonObj Button controlling box + */ +function closeBox(boxObj, buttonObj) { + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else { + // Change 'display' CSS property of box + boxObj.style.display="none"; + + // Change text of button + if (boxObj.style.display=="none") { + buttonObj.value=" + "; + } + } +} + +/** + * Opens a collapseable box. + * + * @param boxObj Collapseable box + * @param buttonObj Button controlling box + */ +function openBox(boxObj, buttonObj) { + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else { + // Change 'display' CSS property of box + boxObj.style.display="block"; + + // Change text of button + if (boxObj.style.display=="block") { + buttonObj.value=" - "; + } + } +} + +/** + * Sets the state of a collapseable box. + * + * @param boxID Identifier of box + * @param open If true, box is "opened", + * Otherwise, box is "closed". + */ +function setState(boxID, open) { + var boxObj = getElementObject(boxID); + var buttonObj = getElementObject(boxID+B_SFIX); + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else if (open) { + openBox(boxObj, buttonObj); + // Make button visible + buttonObj.style.display="inline"; + } else { + closeBox(boxObj, buttonObj); + // Make button visible + buttonObj.style.display="inline"; + } +} + +/** + * Switches the state of a collapseable box, e.g. + * if it's opened, it'll be closed, and vice versa. + * + * @param boxID Identifier of box + */ +function switchState(boxID) { + var boxObj = getElementObject(boxID); + var buttonObj = getElementObject(boxID+B_SFIX); + if (boxObj == null || buttonObj == null) { + // Box or button not found + } else if (boxObj.style.display=="none") { + // Box is closed, so open it + openBox(boxObj, buttonObj); + } else if (boxObj.style.display=="block") { + // Box is opened, so close it + closeBox(boxObj, buttonObj); + } +} + +/** + * Closes all boxes in a given list. + * + * @param boxList Array of box IDs + */ +function collapseAll(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + var boxObj = getElementObject(boxList[idx]); + var buttonObj = getElementObject(boxList[idx]+B_SFIX); + closeBox(boxObj, buttonObj); + } +} + +/** + * Open all boxes in a given list. + * + * @param boxList Array of box IDs + */ +function expandAll(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + var boxObj = getElementObject(boxList[idx]); + var buttonObj = getElementObject(boxList[idx]+B_SFIX); + openBox(boxObj, buttonObj); + } +} + +/** + * Makes all the control buttons of boxes appear. + * + * @param boxList Array of box IDs + */ +function viewControlButtons(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + buttonObj = getElementObject(boxList[idx]+B_SFIX); + if (buttonObj != null) { + buttonObj.style.display = "inline"; + } + } +} + +/** + * Makes all the control buttons of boxes disappear. + * + * @param boxList Array of box IDs + */ +function hideControlButtons(boxList) { + var idx; + for (idx = 0; idx < boxList.length; idx++) { + buttonObj = getElementObject(boxList[idx]+B_SFIX); + if (buttonObj != null) { + buttonObj.style.display = "none"; + } + } +} + +/** + * Sets the page for either printing mode + * or viewing mode. In printing mode, the page + * is made to be more readable when printing it out. + * In viewing mode, the page is more browsable. + * + * @param isPrinterVersion If true, display in + * printing mode; otherwise, + * in viewing mode + */ +function displayMode(isPrinterVersion) { + var obj; + if (isPrinterVersion) { + // Hide global control buttons + obj = getElementObject("globalControls"); + if (obj != null) { + obj.style.visibility = "hidden"; + } + // Hide Legend + obj = getElementObject("legend"); + if (obj != null) { + obj.style.display = "none"; + } + obj = getElementObject("legendTOC"); + if (obj != null) { + obj.style.display = "none"; + } + // Hide Glossary + obj = getElementObject("glossary"); + if (obj != null) { + obj.style.display = "none"; + } + obj = getElementObject("glossaryTOC"); + if (obj != null) { + obj.style.display = "none"; + } + + // Expand all XML Instance Representation tables + expandAll(xiBoxes); + // Expand all Schema Component Representation tables + expandAll(scBoxes); + + // Hide Control buttons + hideControlButtons(xiBoxes); + hideControlButtons(scBoxes); + } else { + // View global control buttons + obj = getElementObject("globalControls"); + if (obj != null) { + obj.style.visibility = "visible"; + } + // View Legend + obj = getElementObject("legend"); + if (obj != null) { + obj.style.display = "block"; + } + obj = getElementObject("legendTOC"); + if (obj != null) { + obj.style.display = "block"; + } + // View Glossary + obj = getElementObject("glossary"); + if (obj != null) { + obj.style.display = "block"; + } + obj = getElementObject("glossaryTOC"); + if (obj != null) { + obj.style.display = "block"; + } + + // Expand all XML Instance Representation tables + expandAll(xiBoxes); + // Collapse all Schema Component Representation tables + collapseAll(scBoxes); + + // View Control buttons + viewControlButtons(xiBoxes); + viewControlButtons(scBoxes); + } +} + +/** + * Opens up a window displaying the documentation + * of a schema component in the XML Instance + * Representation table. + * + * @param compDesc Description of schema component + * @param compName Name of schema component + * @param docTextArray Array containing the paragraphs + * of the new document + */ +function viewDocumentation(compDesc, compName, docTextArray) { + var width = 400; + var height = 200; + var locX = 100; + var locY = 200; + + /* Generate content */ + var actualText = "<html>"; + actualText += "<head><title>"; + actualText += compDesc; + if (compName != '') { + actualText += ": " + compName; + } + actualText += "</title></head>"; + actualText += "<body bgcolor=\"#FFFFEE\">"; + // Title + actualText += "<p style=\"font-family: Arial, sans-serif; font-size: 12pt; font-weight: bold; letter-spacing:1px;\">"; + actualText += compDesc; + if (compName != '') { + actualText += ": <span style=\"color:#006699\">" + compName + "</span>"; + } + actualText += "</p>"; + // Documentation + var idx; + for (idx = 0; idx < docTextArray.length; idx++) { + actualText += "<p style=\"font-family: Arial, sans-serif; font-size: 10pt;\">" + docTextArray[idx] + "</p>"; + } + // Link to close window + actualText += "<a href=\"javascript:void(0)\" onclick=\"window.close();\" style=\"font-family: Arial, sans-serif; font-size: 8pt;\">Close</a>"; + actualText += "</body></html>"; + + /* Display window */ + windowCount++; + var docWindow = window.open("", "documentation"+windowCount, "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable,alwaysRaised,dependent,titlebar=no,width="+width+",height="+height+",screenX="+locX+",left="+locX+",screenY="+locY+",top="+locY); + docWindow.document.write(actualText); +} + + + + + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* More-configurable styles */ + +/******** General ********/ + +/* Document body */ +body { + color: Black; + background-color: White; + font-family: Arial, sans-serif; + font-size: 10pt; +} +/* Horizontal rules */ +hr { + color: black; +} +/* Document title */ +h1 { + font-size: 18pt; + letter-spacing: 2px; + border-bottom: 1px #ccc solid; + padding-top: 5px; + padding-bottom: 5px; +} +/* Main section headers */ +h2 { + font-size: 14pt; + letter-spacing: 1px; +} +/* Sub-section headers */ +h3, h3 a, h3 span { + font-size: 12pt; + font-weight: bold; + color: black; +} +/* Table displaying the properties of the schema components or the + schema document itself */ +table.properties th, table.properties th a { + color: black; + background-color: #F99; /* Pink */ +} +table.properties td { + background-color: #eee; /* Gray */ +} + + +/******** Table of Contents Section ********/ + +/* Controls for switching between printing and viewing modes */ +div#printerControls { + color: #963; /* Orange-brown */ +} +/* Controls that can collapse or expand all XML Instance + Representation and Schema Component Representation boxes */ +div#globalControls { + border: 2px solid #999; +} + + +/******** Schema Document Properties Section ********/ + +/* Table displaying the namespaces declared in the schema */ +table.namespaces th { + background-color: #ccc; +} +table.namespaces td { + background-color: #eee; +} +/* Target namespace of the schema */ +span.targetNS { + color: #06C; + font-weight: bold; +} + + +/******** Schema Components' Sections ********/ + +/* Name of schema component */ +.name { + color: #F93; /* Orange */ +} + +/* Hierarchy table */ +table.hierarchy { + border: 2px solid #999; /* Gray */ +} + +/* XML Instance Representation table */ +div.sample div.contents { + border: 2px dashed black; +} + +/* Schema Component Representation table */ +div.schemaComponent div.contents { + border: 2px black solid; +} + + +/******** Glossary Section ********/ + +/* Glossary Terms */ +.glossaryTerm { + color: #036; /* Blue */ +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* Printer-version styles */ + +@media print { + +/* Ensures that controls are hidden when printing */ +div#printerControls { + visibility: hidden; +} +div#globalControls { + visibility: hidden; +} +#legend { + display: none; +} +#legendTOC { + display: none; +} +#glossary { + display: none; +} +#glossaryTOC { + display: none; +} + +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* Base styles */ + +/******** General ********/ + +/* Unordered lists */ +ul { + margin-left: 1.5em; + margin-bottom: 0em; +} +/* Tables */ +table { + margin-top: 10px; + margin-bottom: 10px; + margin-left: 2px; + margin-right: 2px; +} +table th, table td { + font-size: 10pt; + vertical-align: top; + padding-top: 3px; + padding-bottom: 3px; + padding-left: 10px; + padding-right: 10px; +} +table th { + font-weight: bold; + text-align: left; +} +/* Table displaying the properties of the schema components or the + schema document itself */ +table.properties { + width: 90%; +} +table.properties th { + width: 30%; +} +/* Boxes that can make its content appear and disappear*/ +div.box { + margin: 1em; +} + /* Box caption */ +div.box span.caption { + font-weight: bold; +} + /* Button to open and close the box */ +div.box input.control { + width: 1.4em; + height: 1.4em; + text-align: center; + vertical-align: middle; + font-size: 11pt; +} + /* Box contents */ +div.box div.contents { + margin-top: 3px; +} + + +/******** Table of Contents Section ********/ + +/* Controls for switching between printing and viewing modes */ +div#printerControls { + white-space: nowrap; + font-weight: bold; + padding: 5px; + margin: 5px; +} +/* Controls that can collapse or expand all XML Instance + Representation and Schema Component Representation boxes */ +div#globalControls { + padding: 10px; + margin: 5px; +} + + +/******** Schema Document Properties Section ********/ + +/* Table displaying the namespaces declared in the schema */ +table.namespaces th { +} +table.namespaces td { +} +/* Target namespace of the schema */ +span.targetNS { +} + + +/******** Schema Components' Sections ********/ + +/* Name of schema component */ +.name { +} + +/* Hierarchy table */ +table.hierarchy { + width: 90%; +} +table.hierarchy th { + font-weight: normal; + font-style: italic; + width: 20%; +} +table.hierarchy th, table.hierarchy td { + padding: 5px; +} + +/* XML Instance Representation table */ +div.sample { + width: 90%; +} +div.sample div.contents { + padding: 5px; + font-family: Courier New, sans-serif; + font-size: 10pt; +} + /* Normal elements and attributes */ +div.sample div.contents, div.sample div.contents a { + color: black; +} + /* Group Headers */ +div.sample div.contents .group, div.sample div.contents .group a { + color: #999; /* Light gray */ +} + /* Type Information */ +div.sample div.contents .type, div.sample div.contents .type a { + color: #999; /* Light gray */ +} + /* Occurrence Information */ +div.sample div.contents .occurs, div.sample div.contents .occurs a { + color: #999; /* Light gray */ +} + /* Fixed values */ +div.sample div.contents .fixed { + color: #063; /* Green */ + font-weight: bold; +} + /* Simple type constraints */ +div.sample div.contents .constraint, div.sample div.contents .constraint a { + color: #999; /* Light gray */ +} + /* Elements and attributes inherited from base type */ +div.sample div.contents .inherited, div.sample div.contents .inherited a { + color: #666; /* Dark gray */ +} + /* Elements and attributes added to or changed from base type */ +div.sample div.contents .newFields { + font-weight: bold; +} + /* Other type of information */ +div.sample div.contents .other, div.sample div.contents .other a { + color: #369; /* Blue */ + font-style: italic; +} + /* Link to open up window displaying documentation */ +div.sample div.contents a.documentation { + text-decoration: none; + padding-left: 3px; + padding-right: 3px; + padding-top: 0px; + padding-bottom: 0px; + font-weight: bold; + font-size: 11pt; + background-color: #FFD; + color: #069; +} + /* Invert colors when hovering over link to open up window + displaying documentation */ +div.sample div.contents a.documentation:hover { + color: #FFD; + background-color: #069; +} + +/* Schema Component Representation table */ +div.schemaComponent { + width: 90%; +} +div.schemaComponent div.contents { + font-family: Courier New, sans-serif; + font-size: 10pt; + padding: 5px; +} + /* Syntax characters */ +div.schemaComponent div.contents { + color: #00f; /* blue */ +} + /* Element and attribute tags */ +div.schemaComponent div.contents .scTag { + color: #933; /* maroon */ +} + /* Element and attribute content */ +div.schemaComponent div.contents .scContent, div.schemaComponent div.contents .scContent a { + color: black; + font-weight: bold; +} + /* Comments */ +div.schemaComponent div.contents .comment { + color: #999; /* Light gray */ +} + +/******** Legend Section ********/ + +div#legend table, div#legend div { + margin-bottom: 3px; +} +div#legend div.hint { + color: #999; /* Light gray */ + width: 90%; + margin-left: 1em; + margin-bottom: 2em; +} + + +/******** Glossary Section ********/ + +/* Glossary Terms */ +.glossaryTerm { + font-weight: bold; +} + + +/******** Footer ********/ + +.footer { + font-size: 8pt; +} + + + + + + +
    +

    Complex Type:

    +
    Schema Component Type
    +
    +
    +

    AusAddress

    +
    Schema Component Name
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Super-types: + Address + < + AusAddress + (by extension) + Parent type: + Address + (derivation method: extension) +
    Sub-types: +
      +
    • + QLDAddress + (by restriction) +
    • +
    +
    Direct sub-types: +
      +
    • + QLDAddress + (by restriction) +
    • +
    +
    +
    If this schema component is a type definition, its type hierarchy is shown in a gray-bordered box.
    + + + + + + + + + + + +
    NameAusAddress
    + Abstract + no
    +
    The table above displays the properties of this schema component.
    + + +
    +
    + XML Instance Representation +
    +
    + <... + + country="Australia" + + >
    + <unitNo> string </unitNo> [0..1]
    + <houseNo> string </houseNo> [1]
    + <street> string </street> [1]
    + Start Choice [1]
    + <city> string </city> [1]
    + <town> string </town> [1]
    + End Choice
    + + <state> AusStates </state> [1]
    + <postcode> string <<pattern = [1-9][0-9]{3}>> </postcode> [1] + + ? + +
    +
    + </...>
    +
    +
    +
    +

    The XML Instance Representation table above shows the schema component's content as an XML instance.

    +
      +
    • The minimum and maximum occurrence of elements and attributes are provided in square brackets, e.g. [0..1].
    • +
    • Model group information are shown in gray, e.g. Start Choice ... End Choice.
    • +
    • For type derivations, the elements and attributes that have been added to or changed from the base type's content are shown in bold.
    • +
    • If an element/attribute has a fixed value, the fixed value is shown in green, e.g. country="Australia".
    • +
    • Otherwise, the type of the element/attribute is displayed. +
        +
      • If the element/attribute's type is in the schema, a link is provided to it.
      • + +
      • For local simple type definitions, the constraints are displayed in angle brackets, e.g. <<pattern = [1-9][0-9]{3}>>.
      • +
      +
    • + +
    • If a local element/attribute has documentation, it will be displayed in a window that pops up when the question mark inside the attribute or next to the element is clicked, e.g. <postcode>.
    • +
      +
    +
    + + +
    +
    + Schema Component Representation +
    +
    + <complexType name="AusAddress">
    + <complexContent>
    + <extension base="Address">
    + <sequence>
    + <element name="state" type="AusStates"/>
    + <element name="postcode">
    + <simpleType>
    + <restriction base="string">
    + <pattern value="[1-9][0-9]{3}"/>
    + </restriction>
    + </simpleType>
    + </element>
    + </sequence>
    + <attribute name="country" type="string" fixed="Australia"/>
    + </extension>
    + </complexContent>
    + </complexType>
    +
    +
    +
    The Schema Component Representation table above displays the underlying XML representation of the schema component. (Annotations are not shown.)
    +
    + + + + + Abstract + Abstract + + (Applies to complex type definitions and element declarations). + An abstract element or complex type cannot used to validate an element instance. + If there is a reference to an abstract element, only element declarations that can substitute the abstract element can be used to validate the instance. + For references to abstract type definitions, only derived types can be used. + + + + + All + All Model Group + + Child elements can be provided + + in any order + + in instances. + + http://www.w3.org/TR/xmlschema-1/#element-all + + + + Choice + Choice Model Group + + + Only one + + from the list of child elements and model groups can be provided in instances. + + http://www.w3.org/TR/xmlschema-1/#element-choice + + + + CollapseWS + Collapse Whitespace Policy + Replace tab, line feed, and carriage return characters with space character (Unicode character 32). Then, collapse contiguous sequences of space characters into single space character, and remove leading and trailing space characters. + + + + ElemBlock + Disallowed Substitutions + + (Applies to element declarations). + If + substitution + is specified, then + + SubGroup + substitution group + + members cannot be used in place of the given element declaration to validate element instances. + + If + derivation methods + , e.g. extension, restriction, are specified, then the given element declaration will not validate element instances that have types derived from the element declaration's type using the specified derivation methods. + Normally, element instances can override their declaration's type by specifying an + xsi:type + attribute. + + + + + Key + Key Constraint + + Like + + Unique + Uniqueness Constraint + + , but additionally requires that the specified value(s) must be provided. + + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + KeyRef + Key Reference Constraint + + Ensures that the specified value(s) must match value(s) from a + + Key + Key Constraint + + or + + Unique + Uniqueness Constraint + + . + + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + ModelGroup + Model Group + + Groups together element content, specifying the order in which the element content can occur and the number of times the group of element content may be repeated. + + http://www.w3.org/TR/xmlschema-1/#Model_Groups + + + + Nillable + Nillable + + (Applies to element declarations). + If an element declaration is nillable, instances can use the + xsi:nil + attribute. + The + xsi:nil + attribute is the boolean attribute, + nil + , from the + http://www.w3.org/2001/XMLSchema-instance + namespace. + If an element instance has an + xsi:nil + attribute set to true, it can be left empty, even though its element declaration may have required content. + + + + + Notation + Notation + A notation is used to identify the format of a piece of data. Values of elements and attributes that are of type, NOTATION, must come from the names of declared notations. + http://www.w3.org/TR/xmlschema-1/#cNotation_Declarations + + + + PreserveWS + Preserve Whitespace Policy + Preserve whitespaces exactly as they appear in instances. + + + + TypeFinal + Prohibited Derivations + + (Applies to type definitions). + Derivation methods that cannot be used to create sub-types from a given type definition. + + + + + TypeBlock + Prohibited Substitutions + + (Applies to complex type definitions). + Prevents sub-types that have been derived using the specified derivation methods from validating element instances in place of the given type definition. + + + + + ReplaceWS + Replace Whitespace Policy + Replace tab, line feed, and carriage return characters with space character (Unicode character 32). + + + + Sequence + Sequence Model Group + + Child elements and model groups must be provided + + in the specified order + + in instances. + + http://www.w3.org/TR/xmlschema-1/#element-sequence + + + + SubGroup + Substitution Group + + Elements that are + + members + + of a substitution group can be used wherever the + + head + + element of the substitution group is referenced. + + + + + ElemFinal + Substitution Group Exclusions + + (Applies to element declarations). + Prohibits element declarations from nominating themselves as being able to substitute a given element declaration, if they have types that are derived from the original element's type using the specified derivation methods. + + + + + TargetNS + Target Namespace + The target namespace identifies the namespace that components in this schema belongs to. If no target namespace is provided, then the schema components do not belong to any namespace. + + + + Unique + Uniqueness Constraint + Ensures uniqueness of an element/attribute value, or a combination of values, within a specified scope. + http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions + + + + + + + + + + +

    + + + + + + + See: + + + + . + +

    +
    + + + + + + + + +
      + + + +
    +
    + + + true + + + + + + + + +
    +
      + + +
    • + This element can be used wherever the following element is referenced: +
        +
      • + + + +
      • +
      +
    • +
      + + +
    • + The following elements can be used wherever this element is referenced: + +
    • +
      +
    +
    +
    +
    + + + + + + + + + + + + + + +
    + + + Super-types: + + + Parent type: + + + + + + + + + + + None + + +
    + + + Sub-types: + + + Direct sub-types: + + + + + + +
    +
    + + + + + + + + + + + + + + +
    + + + Super-types: + + + Parent type: + + + + + + + + + + + None + + +
    + + + Sub-types: + + + Direct sub-types: + + + + + + +
    +
    + + + + + + + + + + + + + +
  • + + false + + Circular element reference to: + + + +
  • +
    + + + + + + + + + + + + + + + + + + +
  • + + + +
  • + + + + + + + +
    +
    +
    +
    + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Local type definition + + + + + + + + + + + + + + extension + + + restriction + + + + + + restriction + + + + + + + + + + + + < + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + < + + + + + + + + + + + + + + + + + + + (by + + ) + + + + + + + + + + + + + + + + + + + + + (derivation method: + + ) + + + + + + + + + + + true + + + + + + + + + + + +
      + +
    • + + + + + + + (by + + ) + + + + + false + + + +
    • +
      +
    +
    + + + + + + + None + + +
    +
    +
    + + + + + true + + + + + + + + + + + +
      + +
    • + + + + + + + (by restriction) + + + + + false + + + +
    • +
      +
    +
    + + +
      + +
    • + + + + + + + (by + + ) + + + + + false + + + +
    • +
      +
    +
    + + + + true + + + + + true + + + + + + + + + + + + + + None + + +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + More information at: + + + + . +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Type + + + Locally-defined simple type + + + + + + + + anySimpleType + + +
    Default Value
    Fixed Value
    +
    + + + + + + + + + + + + + +
    Name
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    + + Abstract + Abstract + + + + + +
    + + TypeFinal + Prohibited Derivations + +
    + + TypeBlock + Prohibited Substitutions + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Type + + + Locally-defined simple type + + + Locally-defined complex type + + + + + + + + anyType + + +
    + + Nillable + Nillable + + + + + +
    + + Abstract + Abstract + + + + + +
    Default Value
    Fixed Value
    + + ElemFinal + Substitution Group Exclusions + +
    + + ElemBlock + Disallowed Substitutions + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Public Identifier
    System Identifier
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + TargetNS + Target Namespace + + + + + + + + + + None + + +
    Version
    Language
    Element and Attribute Namespaces +
      +
    • Global element and attribute declarations belong to this schema's target namespace.
    • +
    • + + + By default, local element declarations belong to this schema's target namespace. + + + By default, local element declarations have no namespace. + + +
    • +
    • + + + By default, local attribute declarations belong to this schema's target namespace. + + + By default, local attribute declarations have no namespace. + + +
    • +
    +
    Schema Composition +
      + + +
    • + This schema imports schema(s) from the following namespace(s): +
        + +
      • + + + (at + + + + ) + +
      • +
        +
      +
    • +
      + + +
    • + This schema includes components from the following schema document(s): +
        + +
      • + + + +
      • +
        +
      +
    • +
      + + +
    • + This schema includes components from the following schema document(s), where some of the components have been redefined: +
        + +
      • + + + +
      • +
        +
      + See Redefined Schema Components section. +
    • +
      +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name
    Content + + + +
    + + TypeFinal + Prohibited Derivations + +
    +
    + + + + + + + + + + + Documentation + + +

    + +
    + + +
    + + + Application Data + + +

    + +
    + + +
    +
    + + + + + + + + + + + + + + + + +
    • + List of: + + + + + + + + + +
        +
      • + Locally defined type: + + + + +
      • +
      +
      +
      +
    +
    + + +
    • + Union of following types: +
        + + + + + type + true + + + + +
      • + Locally defined type: + + + + +
      • +
        +
      +
    +
    +
    +
    + + + + + + + + + + + + +
  • + + false + + Circular type reference to ' + + ' in type hierarchy. + + +
  • +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    • + Base XSD Type: + + + + + + + + + + + + +
    +
    + + + + + + + + + + + + + +
    • + ' + + ' super type was not found in this schema. + Its facets could not be printed out. +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      + +
    • + +
    • +
      +
    +
    +
    + + + + + + + + + + + + + + + + + + + sample + XML Instance Representation + + + + true + + + + + + + 0 + false + false + this + + + + + + Start + + All + All + + + + + + + + + + +
    + + + + + + + + + + + + + End All +
    +
    +
    + + + + 0 + +
    + + + + attribute + element + + + + + + + +
    +
    + + + + + false + false + 0 + false + this + + + + + + + + + + + + + + + + +
    + + + + + newFields + + + inherited + + + + + + + + + + + + + + + =" + + + + + + + + + + + + + + + + + + + + + + + anySimpleType + + + + + + + + + + + + + + + + + " + +
    +
    +
    + + + + + false + false + 0 + false + this + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + newFields + + + inherited + + + + + + + + + =" + + + + + + + + + + + + + + + + + " + +
    +
    +
    + + + + this + + + + + + true + + + false + + + + + + + + + + + + + + + false + false + 0 + + this + + + + + + + + + + + + + false + + Circular attribute group reference: + + + + + + + + + + attribute group + + + + + + +
    + Attribute group reference (not shown): + + + + + + + + +
    +
    + + + + + + + + + + true + + + + + + + + + + + + + true + + + +
    +
    +
    +
    + + + + 0 + false + false + + this + + + + + + Start + + Choice + Choice + + + + + + + + + + +
    + + + + + + + + + + + + + + End Choice +
    +
    +
    + + + + this + + + + + + + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + + complex type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + this + + + + + + + + + + + + 0 + false + false + + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Circular model group reference: + + + + + + + + +
    +
    + + + + + + group + + + + + + +
    + Model group reference (not shown): + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + + + + 0 + false + false + + this + + + + + + + + + + + + + + + Start + + Sequence + Sequence + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + End Sequence +
    +
    +
    +
    + + + + this + + + + + + + + + + + + this + + + + + + + + + + + + 0 + this + +
    + <!--
    + + + + + Unique + Uniqueness + + + + + Key + Key + + + + + KeyRef + Key Reference + + + + Constraint - + + + + + + + + + + + + + + +
    + + Selector - + + +
    + + Field(s) - + + + , + + + + + +
    + + + Refers to - + + + + +
    +
    + + --> +
    +
    + + + + + + + + + + + + + + + true + +A local schema component contains two dashes in +'documentation' elements within its 'annotation' element. + + + + + + , + + ' + + + More information at: + + + + . + + + + + + + + ' + + + + + + + + docArray = new Array( + + ); viewDocumentation(' + + + + ', ' + + + + + + + + + + + ', docArray); + + ? + + + + + + + + + + + + ' + \' + + + + + + " + \" + + + + + + + + 0 + false + false + + + + + + this + + + + + + + Start Group: + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + End Group: + +
    +
    +
    + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + newFields + + + inherited + + + + + < + + > + + + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + </ + + > + + + + + + + + + + + + +
    +
    + + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + + + + true + + + false + + + + +
    + + + newFields + + + inherited + + + + + < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + + + + + + true + + + + + + + + + /> + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + <!-- Restricts : + + + + + --> + +
    + + +
    + <!-- Extends : + + + + + --> + +
    +
    + + + +
    + <!-- Mixed content --> + +
    + + + + + + </ + + > +
    +
    +
    +
    +
    +
    + + + + + + false + false + false + 0 + this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + 0 + this + + + + + + + + + + + + + + + + + + + complex type + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + 0 + this + + + + + + + true + + + + + + + + 0 + false + false + false + true + this + + + +
    + + + + + + + + + + + + + + + + + + + complex type + + + + + + +
    + <!-- ' + + + + + ' super type was not found in this schema. Some elements and attributes may be missing. --> +
    +
    + + + + +
    + + + + + + + + + + + true + + + false + + + + + + + + +
    + + + + + + + + + + + + +
    + <-- Extends: + + + + + (Circular type hierarchy) --> +
    +
    + + + + + + complex type + + + + + + + + + + + + + + true + + + false + + + + + + false + + + + + + + +
    + <!-- ' + + + + + ' super type was not found in this schema. Some elements and attributes may be missing. --> +
    +
    + + + + + + + + + + true + + + false + + + + + + false + + + + + +
    + + + + + + + + + + + true + + + false + + + + + + + + +
    +
    +
    + + + + + + + + + +
    +
    + + + + + + + + + + +
    +
    + + + + + + + + + + + + + +
    +
    + + + + + 0 + false + false + this + + + + + + + + + + + + + + + + + + + this + + + + + + + + + + + + + + + + list of: + + + + + + + list of: [ + + + + + ] + + + + + + union of: [ + + + + true + + + + + + type + , + + + + + + , + + [ + + + + + ] + + + ] + + + + + + + + this + + + + + + + + + + false + + Circular type reference to ' + + ' in type hierarchy. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ( + + + + ) + + + ( + + ) + + + ( + + ) + + + ( + + + + ) + + + ( + + + + ) + + + ( + + ) + + + ( + + + + ) + + + + + + + + + + + + + + + + + + schemaComponent + Schema Component Representation + + + + false + + + + + + 0 + + + + + + + + name + + + + + + type + + + + + + + + + + + *name+*type+ + + + *annotation+ + + + + + + 0 + + + + + + + + name + + + + + + *name+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + ref + + + + + + + + + + *ref+ + + + *annotation+ + + + + + + 0 + + + + + + + + + source + + + + + + + + + + + *source+ + + + true + + + + + + 0 + + + + + + + + name + + + + + refer + + + + + + + + + + + + *name+*refer+ + + + *annotation+ + + + + + + 0 + + + + + + + + + base + + + + + + + + + + + *base+ + + + *annotation+ + + + + + + 0 + + + + + + + + + itemType + + + + + + + + + + + *itemType+ + + + *annotation+ + + + + + + 0 + + + + + + + + + memberTypes + + + + type + + + + + + + + *memberTypes+ + + + *annotation+ + + + + + + 0 + + + + + + + + + xml:lang + + + + + + + *lang+ + + + *include+*import+*redefine+ + + + + + + 0 + + + + + + + + + + *annotation+ + + + + + + 0 + +
    + <-- + + --> +
    +
    + + + + + + 0 + false + + + + + + + + + true + + + + +
    + + < + + + + + + + + + + + + + +
    ...
    +
    + + + + + + + + +
    + +
    +
    + + + + + + +
    +
    + + + + + + > + + + + + + </ + + + + > + + + + + /> + + +
    +
    + + + + + + + + + + + =" + + + + + + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + html + + + + + + + + + + + + + + xpp + + + + + + + + + + + +
    + <-- + + --> +
    +
    + + + + + xpp + + + < + + + + + + + + + + + + + =" + + " + + + + + + > + + + + + + +
    + +
    +
    +
    + + </ + + + + + + + + + > +
    + + + /> + +
    +
    + + + + + + + + + + + + + + + + + + [term] + + + + + + + + + + + + + + this + + + + + + + + + + + + + + + + + + + Unknown namespace prefix, + + . + + + + + + + + + + + + + + this + + + + + + attribute + + + + + + + attribute + + + + + + + + + + + this + + + + + + attribute group + + + + + + + attribute group + + + + + + + + + + + this + + + + + + element + + + + + + + element + + + + + + + + + + + this + + + + + + group + + + + + + + group + + + + + + + + + + + this + + + + + + uniqueness/key constraint + + + + + + + uniqueness/key constraint + + + + + + + + + + + this + + + + + + + type + + + + + + + + + type + + + + + + + + + + + + + this + + + + + + + declaration + + + definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "" + + + + + + + Jump to " + + " + + + + + + (located in external schema documentation) + + + + . + + + + + + + + javascript:void(0) + + + + + + + + + + + + + + + externalLink + + + + + + alert(' + + '); + + + + + + + + + + + + this + + + + + + declaration + + + definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + could not be found + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xsd + + + + xml + + + + + + + + this + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + +
    +
    + + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    + + + setState('', ); + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + All Model Group + + + Attribute + + + Attribute Group + + + Choice Model Group + + + Complex Type + + + Element + + + Model Group + + + Notation + + + Sequence Model Group + + + Simple Type + + + + true + +Unknown schema component, . + + + + + + + + + + + + + schema + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + +Unknown schema component, . + + + + + + + + + + + + + Notation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + +Documentation file for the schema at, , +was not specified in the links file, . + + + + + + + + + + + + + + yes + + + no + + + + + + + + false + this + + + + + + + + + + + + + + : + + + + + + + + + + + + + + + + 1 + + + 0 + + + + + + + + + + + + + 1 + + + + + + + + + + + + 0 + + + 1 + + + + + + + * + + + + + + + + + 1 + + + + + + + + + + [1] + + + [ + + .. + + ] + + + + + + + + + + + + restriction, extension, substitution + + + + + + + + + + + + + + restriction, extension + + + + + + + + + + + + + + restriction, list, union + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + true + this + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + true + this + + + + + + + + + + + + + + + + + + +
  • + +
  • +
    + + + + + + + + + + + + + + +
    +
    + + + + element + + + + + + Allow any + + s from + + + + + any namespace + + + + a namespace other than this schema's namespace + + + + + + + true + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + no namespace + + + + + + and + + + , + + + this schema's namespace + + + + + + , and + + + and + + + the following namespace(s): + + + , + + + + + + ( + + + + + + strict + + + validation) + . + + + + + + + + + + + + + + + + + pattern + = + + + + + + + + + + total no. of digits + = + + + + + + + + + + + no. of fraction digits + + = + + + + + + + + + + value + comes from list: { + + + + | + + ' + + ' + + + } + + + + + + + + + + + length + + = + + + + + length + + >= + + + + + length + + <= + + + + + + + + + + + + + + Whitespace policy: + + PreserveWS + preserve + + + + Whitespace policy: + + ReplaceWS + replace + + + + Whitespace policy: + + CollapseWS + collapse + + + + + + + + + + + + + + + <= + + + + < + + + + value + + + + <= + + + + < + + + + + + + value + + >= + + + + + value + + > + + + + + value + + <= + + + + + value + + < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + XS3P ERROR: + + + + + + ERROR: + + + + + + diff --git a/Libs/CommandLineModules/QtGui/CMakeLists.txt b/Libs/CommandLineModules/Frontend/QtGui/CMakeLists.txt similarity index 80% rename from Libs/CommandLineModules/QtGui/CMakeLists.txt rename to Libs/CommandLineModules/Frontend/QtGui/CMakeLists.txt index 71e64bbb4b..642ad8f2ff 100644 --- a/Libs/CommandLineModules/QtGui/CMakeLists.txt +++ b/Libs/CommandLineModules/Frontend/QtGui/CMakeLists.txt @@ -1,4 +1,4 @@ -project(CTKCommandLineModulesQtGui) +project(CTKCommandLineModulesFrontendQtGui) # # 3rd party dependencies @@ -14,16 +14,20 @@ set(KIT_export_directive "CTK_CMDLINEMODULEQTGUI_EXPORT") # Source files set(KIT_SRCS - ctkCmdLineModuleInstanceFactoryQtGui.cpp - ctkCmdLineModuleInstanceQtGui_p.h - ctkCmdLineModuleInstanceQtGui.cpp + ctkCmdLineModuleFrontendFactoryQtGui.cpp + ctkCmdLineModuleFrontendQtGui.cpp + ctkCmdLineModuleQtUiLoader.cpp ctkCmdLineModuleObjectTreeWalker_p.h ctkCmdLineModuleObjectTreeWalker.cpp ) # Headers that should run through moc set(KIT_MOC_SRCS - + ctkCmdLineModuleQtUiLoader.h +) + +qt4_generate_mocs( + ctkCmdLineModuleQtUiLoader.cpp ) # UI files @@ -32,7 +36,7 @@ set(KIT_UI_FORMS # Resources set(KIT_resources - + Resources/ctkCmdLineModulesFrontendQtGui.qrc ) set(QT_USE_QTUITOOLS 1) @@ -66,5 +70,5 @@ endif() # Testing if(BUILD_TESTING) -# add_subdirectory(Testing) + add_subdirectory(Testing) endif() diff --git a/Libs/CommandLineModules/Frontend/QtGui/README.md b/Libs/CommandLineModules/Frontend/QtGui/README.md new file mode 100644 index 0000000000..7dd2fb85ea --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/README.md @@ -0,0 +1,11 @@ +Qt Gui Frontend {#CommandLineModulesFrontendQtGui_Page} +=============== + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModulesFrontendQtGui_Page.html) form. \endinternal + +The Qt Gui front-end uses a customizable XML stylesheet to transform the raw XML module +description into Qt .ui file. For details about the configuration possibilities of the GUI +generation process see the ctkCmdLineModuleFrontendQtGui class. + +See the \ref CommandLineModulesFrontendQtGui_API module for the API documentation. diff --git a/Libs/CommandLineModules/Core/Resources/QtDesigner.xsd b/Libs/CommandLineModules/Frontend/QtGui/Resources/QtDesigner.xsd similarity index 100% rename from Libs/CommandLineModules/Core/Resources/QtDesigner.xsd rename to Libs/CommandLineModules/Frontend/QtGui/Resources/QtDesigner.xsd diff --git a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModuleXmlToQtUi.xsl b/Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModuleXmlToQtUi.xsl similarity index 55% rename from Libs/CommandLineModules/Core/Resources/ctkCmdLineModuleXmlToQtUi.xsl rename to Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModuleXmlToQtUi.xsl index c383999da9..5d1a60d504 100644 --- a/Libs/CommandLineModules/Core/Resources/ctkCmdLineModuleXmlToQtUi.xsl +++ b/Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModuleXmlToQtUi.xsl @@ -9,7 +9,57 @@ exclude-result-prefixes="xs xdt err fn"> + + + + + + true + + QWidget + ctkCollapsibleGroupBox + QCheckBox + QSpinBox + QDoubleSpinBox + QLineEdit + QComboBox + ctkPathLineEdit + ctkPathLineEdit + ctkPathLineEdit + ctkPathLineEdit + ctkPathLineEdit + ctkCoordinatesWidget + QLabel + + checked + value + value + coordinates + coordinates + currentPath + currentPath + currentPath + currentPath + currentPath + currentPath + currentPath + text + currentEnumeration + + - checked - coordinates - currentPath - text - currentText + + + + + + + + + + + + + + + + + + + + + value @@ -58,21 +125,6 @@ - - - - - BrowseButton - clicked() - parameter: - browse() - - - - - - + + + + + + + + + + + + + + + + + + + @@ -110,10 +178,6 @@ - - - - - + false + + + + false + + - - - + + + + + + + + + + + + + + + @@ -166,13 +248,12 @@ Match elements from the XML description =================================================================== --> - - + @@ -186,34 +267,42 @@ - - - + - + - - + + + + + + + + - + - + - + @@ -305,7 +394,7 @@ - + @@ -320,60 +409,83 @@ - + - - - + + + - ctkPathLineEdit::Files + ctkPathLineEdit::Files,ctkPathLineEdit::Readable - - - - - Browse... + + + + + + + ctkPathLineEdit::Files,ctkPathLineEdit::Writable - - + + - + - + - - - + + + + - ctkPathLineEdit::Dirs + ctkPathLineEdit::Files,ctkPathLineEdit::Readable - - - - - Browse... + + + + + + + ctkPathLineEdit::Files,ctkPathLineEdit::Writable - - + + + + + + + + + + + + + + ctkPathLineEdit::Dirs + + @@ -386,7 +498,7 @@ - + @@ -401,7 +513,7 @@ - + <html><head><meta name="qrichtext" content="1" /><style type="text/css">p, li { white-space: pre-wrap; }</style></head><body><p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">Element '' not supported yet.</span></p></body></html> @@ -412,4 +524,6 @@ + + diff --git a/Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModulesFrontendQtGui.qrc b/Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModulesFrontendQtGui.qrc new file mode 100644 index 0000000000..04d5ffd57e --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Resources/ctkCmdLineModulesFrontendQtGui.qrc @@ -0,0 +1,6 @@ + + + ctkCmdLineModuleXmlToQtUi.xsl + QtDesigner.xsd + + diff --git a/Plugins/org.commontk.slicermodule/Testing/CMakeLists.txt b/Libs/CommandLineModules/Frontend/QtGui/Testing/CMakeLists.txt similarity index 95% rename from Plugins/org.commontk.slicermodule/Testing/CMakeLists.txt rename to Libs/CommandLineModules/Frontend/QtGui/Testing/CMakeLists.txt index cdeb442a1d..d6112f6eb2 100644 --- a/Plugins/org.commontk.slicermodule/Testing/CMakeLists.txt +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/CMakeLists.txt @@ -1 +1,2 @@ + add_subdirectory(Cpp) diff --git a/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/CMakeLists.txt b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/CMakeLists.txt new file mode 100644 index 0000000000..708999cded --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/CMakeLists.txt @@ -0,0 +1,48 @@ +set(KIT ${PROJECT_NAME}) +set(LIBRARY_NAME ${PROJECT_NAME}) + +create_test_sourcelist(Tests ${KIT}CppTests.cpp + ctkCmdLineModuleFrontendQtGuiTest.cpp + ctkCmdLineModuleQtXslTransformTest.cpp + ) + +set(TestsToRun ${Tests}) +remove(TestsToRun ${KIT}CppTests.cpp) + +set(Tests_SRCS ${Tests_SRCS} +) + +set(Tests_MOC_SRCS ${Tests_MOC_SRCS} +) + +set(Tests_RESOURCES + ctkCmdLineModuleFrontendQtGuiTestResources.qrc +) + +include_directories( + ${CMAKE_SOURCE_DIR}/Libs/Testing + ${CMAKE_CURRENT_BINARY_DIR} + ) + +set(Tests_MOC_CPP) +QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS}) +QT4_GENERATE_MOCS( + ctkCmdLineModuleFrontendQtGuiTest.cpp + ctkCmdLineModuleQtXslTransformTest.cpp + ) +set(Tests_UI_CPP) +if(TEST_UI_FORMS) + QT4_WRAP_UI(Tests_UI_CPP ${Tests_UI_FORMS}) +endif() +set(Tests_RESOURCES_SRCS) +QT4_ADD_RESOURCES(Tests_RESOURCES_SRCS ${Tests_RESOURCES}) + +add_executable(${KIT}CppTests ${Tests} ${Tests_SRCS} ${Tests_MOC_CPP} ${Tests_UI_CPP} ${Tests_RESOURCES_SRCS}) +target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES}) + +# +# Add Tests +# + +SIMPLE_TEST(ctkCmdLineModuleQtXslTransformTest) +SIMPLE_TEST(ctkCmdLineModuleFrontendQtGuiTest) diff --git a/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTest.cpp b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTest.cpp new file mode 100644 index 0000000000..c25ee104ab --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTest.cpp @@ -0,0 +1,243 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +Q_DECLARE_METATYPE(QVariant) +#endif + +// CTK includes +#include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleBackend.h" +#include "ctkCmdLineModuleFrontendQtGui.h" +#include "ctkCmdLineModuleFuture.h" +#include "ctkCmdLineModuleDescription.h" +#include "ctkCmdLineModuleParameter.h" + +#include "ctkTest.h" + +#if (QT_VERSION < QT_VERSION_CHECK(4,7,0)) +extern int qHash(const QUrl& url); +#endif + +namespace { + +class BackendMockUp : public ctkCmdLineModuleBackend +{ + +public: + + void addModule(const QUrl& location, const QByteArray& xml) + { + this->UrlToXml[location] = xml; + } + + virtual QString name() const { return "Mockup"; } + virtual QString description() const { return "Test Mock-up"; } + virtual QList schemes() const { return QList() << "test"; } + virtual qint64 timeStamp(const QUrl& /*location*/) const { return 0; } + virtual QByteArray rawXmlDescription(const QUrl& location) + { + return UrlToXml[location]; + } + +protected: + + virtual ctkCmdLineModuleFuture run(ctkCmdLineModuleFrontend* /*frontend*/) + { + return ctkCmdLineModuleFuture(); + } + +private: + + QHash UrlToXml; +}; + +} + +// ---------------------------------------------------------------------------- +class ctkCmdLineModuleFrontendQtGuiTester: public QObject +{ + Q_OBJECT + +private: + + QScopedPointer Backend; + ctkCmdLineModuleManager Manager; + + ctkCmdLineModuleReference ModuleRef; + + QString ChangedParameter; + QVariant ChangedParameterValue; + +private Q_SLOTS: + + void valueChanged(const QString& parameter, const QVariant& value); + +private Q_SLOTS: + + void initTestCase(); + + void init(); + + void testValueSetterAndGetter(); + void testValueSetterAndGetter_data(); + +}; + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGuiTester::valueChanged(const QString ¶meter, const QVariant &value) +{ + this->ChangedParameter = parameter; + this->ChangedParameterValue = value; +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGuiTester::initTestCase() +{ + BackendMockUp* backend = new BackendMockUp; + this->Backend.reset(backend); + + QFile xmlFile(":/ctkCmdLineModuleFrontendQtGuiTestModule1.xml"); + QVERIFY(xmlFile.open(QIODevice::ReadOnly)); + + backend->addModule(QUrl("test://module1"), xmlFile.readAll()); + + this->Manager.registerBackend(backend); + + this->ModuleRef = this->Manager.registerModule(QUrl("test://module1")); + QVERIFY(this->ModuleRef); +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGuiTester::init() +{ + this->ChangedParameter.clear(); + this->ChangedParameterValue.clear(); +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGuiTester::testValueSetterAndGetter() +{ + + QScopedPointer frontend(new ctkCmdLineModuleFrontendQtGui(this->ModuleRef)); + // force the creation of the frontend gui + frontend->guiHandle(); + + connect(frontend.data(), SIGNAL(valueChanged(QString,QVariant)), SLOT(valueChanged(QString,QVariant))); + + QFETCH(QString, parameter); + QFETCH(QVariant, currentValue); + QFETCH(QVariant, newValue); + QFETCH(QVariant, expectedValue); + QFETCH(int, role); + + if (role == -1) + { + // test with default role argument + QCOMPARE(frontend->value(parameter), currentValue); + } + else + { + QCOMPARE(frontend->value(parameter, role), currentValue); + } + + // test setting values + if (newValue.isValid()) + { + frontend->setValue(parameter, newValue); + if (role == -1) + { + QCOMPARE(frontend->value(parameter), expectedValue); + } + else + { + QCOMPARE(frontend->value(parameter, role), expectedValue); + if (role == ctkCmdLineModuleFrontend::DisplayRole) + { + QWidget* widget = frontend->guiHandle()->findChild("parameter:" + parameter); + QVERIFY(widget != NULL); + QString tag = this->ModuleRef.description().parameter(parameter).tag(); + if (tag == "integer") + { + QSpinBox* spinBox = qobject_cast(widget); + QVERIFY(spinBox); + QCOMPARE(spinBox->value(), expectedValue.toInt()); + } + else if (tag.endsWith("enumeration")) + { + QComboBox* comboBox = qobject_cast(widget); + QVERIFY(comboBox); + QCOMPARE(comboBox->currentText(), expectedValue.toString()); + } + else + { + QFAIL("Missing widget sub-class test code."); + } + } + } + + QCOMPARE(this->ChangedParameter, parameter); + QCOMPARE(this->ChangedParameterValue, expectedValue); + } + +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGuiTester::testValueSetterAndGetter_data() +{ + QTest::addColumn("parameter"); + QTest::addColumn("currentValue"); + QTest::addColumn("newValue"); + QTest::addColumn("expectedValue"); + QTest::addColumn("role"); + + QTest::newRow("intParamDefaultDefaultRole") << "intParam" << QVariant(1) << QVariant(2) << QVariant(2) << -1; + QTest::newRow("intParamDefaultDisplayRole") << "intParam" << QVariant(1) << QVariant(2) << QVariant(2) << static_cast(ctkCmdLineModuleFrontend::DisplayRole); + QTest::newRow("intParmaDefaultLRRole") << "intParam" << QVariant(1) << QVariant(2) << QVariant(2) << static_cast(ctkCmdLineModuleFrontend::LocalResourceRole); + + // newValue too low + QTest::newRow("intParamTooLowDefaultRole") << "intParam" << QVariant(1) << QVariant(-6) << QVariant(-5) << -1; + QTest::newRow("intParamTooLowDisplayRole") << "intParam" << QVariant(1) << QVariant(-6) << QVariant(-5) << static_cast(ctkCmdLineModuleFrontend::DisplayRole); + QTest::newRow("intParmaTooLowLRRole") << "intParam" << QVariant(1) << QVariant(-6) << QVariant(-5) << static_cast(ctkCmdLineModuleFrontend::LocalResourceRole); + + // newValue too high + QTest::newRow("intParamTooHighDefaultRole") << "intParam" << QVariant(1) << QVariant(200) << QVariant(60) << -1; + QTest::newRow("intParamTooHighDisplayRole") << "intParam" << QVariant(1) << QVariant(200) << QVariant(60) << static_cast(ctkCmdLineModuleFrontend::DisplayRole); + QTest::newRow("intParmaTooHighLRRole") << "intParam" << QVariant(1) << QVariant(200) << QVariant(60) << static_cast(ctkCmdLineModuleFrontend::LocalResourceRole); + + QTest::newRow("stringEnumDefaultRole") << "stringEnumParam" << QVariant("yes") << QVariant("no") << QVariant("no") << -1; + QTest::newRow("stringEnumDisplayRole") << "stringEnumParam" << QVariant("yes") << QVariant("no") << QVariant("no") << static_cast(ctkCmdLineModuleFrontend::DisplayRole); + QTest::newRow("stringEnumLRRole") << "stringEnumParam" << QVariant("yes") << QVariant("no") << QVariant("no") << static_cast(ctkCmdLineModuleFrontend::LocalResourceRole); + + QTest::newRow("intOutputParamDefaultRole") << "intOutputParam" << QVariant(0) << QVariant(3) << QVariant(3) << -1; + QTest::newRow("intOutputParamDisplayRole") << "intOutputParam" << QVariant(0) << QVariant(3) << QVariant(3) << static_cast(ctkCmdLineModuleFrontend::DisplayRole); + QTest::newRow("intOutputParamLRRole") << "intOutputParam" << QVariant(0) << QVariant(3) << QVariant(3) << static_cast(ctkCmdLineModuleFrontend::LocalResourceRole); +} + + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleFrontendQtGuiTest) +#include "moc_ctkCmdLineModuleFrontendQtGuiTest.cpp" diff --git a/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestModule1.xml b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestModule1.xml new file mode 100644 index 0000000000..3d7885badf --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestModule1.xml @@ -0,0 +1,36 @@ + + + Test Module + Test Module. + + + Test parameters. + + intParam + i + Integer + + 1 + + -5 + 60 + 1 + + + + stringEnumParam + 0 + Enumeration parameter + + yes + no + + + intOutputParam + 1000 + Integer + + output + + + diff --git a/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestResources.qrc b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestResources.qrc new file mode 100644 index 0000000000..19a40454fe --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTestResources.qrc @@ -0,0 +1,5 @@ + + + ctkCmdLineModuleFrontendQtGuiTestModule1.xml + + diff --git a/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleQtXslTransformTest.cpp b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleQtXslTransformTest.cpp new file mode 100644 index 0000000000..c043f917b5 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleQtXslTransformTest.cpp @@ -0,0 +1,467 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include + +// CTK includes +#include "ctkCmdLineModuleFrontendFactoryQtGui.h" +#include "ctkCmdLineModuleXslTransform.h" +#include "ctkTest.h" + +// ---------------------------------------------------------------------------- +class ctkCmdLineModuleQtXslTransformTester: public QObject +{ + Q_OBJECT +private slots: + + void initTestCase(); + + void testTransform(); + void testTransform_data(); + + void testBindVariable(); + void testBindVariable_data(); + + void testXslExtraTransformation(); + void testXslExtraTransformation_data(); +}; + +QString invalidXml = + "" + ""; + +QString header = + "" + "" + " " + " A Test" + " " + " 0.1.0" + " " + " " + " " + " "; + +QString footer = + "\n"; + +QString mainWidgetHeader = + "\n" + " ATest\n" + " \n" + " \n"; + +QString mainWidgetFooter = + " \n" + " \n" + " \n" + " Qt::Vertical\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + +QString parametersHeader = + " " + " " + " "; + +QString parametersFooter = + " \n"; + +QString parametersWidgetHeader = + " \n" + " \n" + " \n" + " Parameters\n" + " \n" + " \n" + " Parameters\n" + " \n" + " \n" + " true\n" + " \n" + ; + +QString parametersWidgetEmptyLayout = + " \n"; + +QString parametersLayoutHeader = + " \n"; + +QString parametersLayoutFooter = + " \n"; + +QString parametersWidgetFooter = + " \n" + " \n" + ; +QString integer = + "" + " integer" + " -i" + " --integer" + " " + " " + " 1" + "" + ; + +QString integerWidgetLabel = + " \n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " \n" + " \n" + " \n" + " Integer\n" + " \n" + " \n" + " \n" + ; +QString integerWidgetSpinBoxHeader = + " \n" + ; +QString integerWidgetSpinBox = + " \n" + " \n" + " -999999999\n" + " \n" + " \n" + " 999999999\n" + " \n" + " \n" + " Integer description\n" + " \n" + " \n" + " value\n" + " \n" + " \n" + " 1\n" + " \n" + " \n" + ; +QString integerWidgetSpinBoxFooter = + " \n" + ; + + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::initTestCase() +{ + // Introduce a dummy linker dependency to CTKCommandLineModulesFrontendQtGui to + // get access to the ctkCmdLineModuleXmlToQtUi.xsl resource. + ctkCmdLineModuleFrontendFactoryQtGui guiFactory; +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testTransform() +{ + ctkCmdLineModuleXslTransform transformer; + + QFile transformation(":/ctkCmdLineModuleXmlToQtUi.xsl"); + transformer.setXslTransformation(&transformation); + + QFETCH(QString, input); + QByteArray inputByteArray = input.toUtf8(); + QBuffer inputBuffer(&inputByteArray); + transformer.setInput(&inputBuffer); + + QBuffer output; + output.open(QBuffer::ReadWrite); + transformer.setOutput(&output); + + transformer.setFormatXmlOutput(true); + + QFETCH(bool, expectedSuccess); + bool success = transformer.transform(); + if (!success) + { + qDebug() << transformer.errorString(); + QCOMPARE(transformer.error(), true); + QVERIFY(!transformer.errorString().isEmpty()); + } + QCOMPARE(success, expectedSuccess); + + QFETCH(QString, expectedOutput); + QCOMPARE(QString(output.readAll()), expectedOutput); +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testTransform_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedSuccess"); + QTest::addColumn("expectedOutput"); + + QTest::newRow("null") << QString() << false << QString(); + QTest::newRow("empty") << QString("") << false << QString(); + QTest::newRow("invalidXml") << invalidXml << false << QString(); + QString noParameter = header + footer; + QString noParameterUi = mainWidgetHeader + mainWidgetFooter; + QTest::newRow("no parameter") << noParameter << true << noParameterUi; + + QString justParameters = + header + + parametersHeader + + parametersFooter + + footer; + QString justParametersUi = + mainWidgetHeader + + parametersWidgetHeader + + parametersWidgetEmptyLayout + + parametersWidgetFooter + + mainWidgetFooter; + QTest::newRow("just parameters") << justParameters << true << justParametersUi; + + QString integerParameter = + header + + parametersHeader + + integer + + parametersFooter + + footer; + QString integerParameterUi = + mainWidgetHeader + + parametersWidgetHeader + + parametersLayoutHeader + + integerWidgetLabel + + integerWidgetSpinBoxHeader + + integerWidgetSpinBox + + integerWidgetSpinBoxFooter + + parametersLayoutFooter + + parametersWidgetFooter + + mainWidgetFooter; + QTest::newRow("integer") << integerParameter << true << integerParameterUi; + +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testBindVariable() +{ + ctkCmdLineModuleXslTransform transformer; + + QFile transformation(":/ctkCmdLineModuleXmlToQtUi.xsl"); + transformer.setXslTransformation(&transformation); + + QFETCH(QString, input); + QByteArray inputArray(input.toUtf8()); + QBuffer inputBuffer(&inputArray); + transformer.setInput(&inputBuffer); + + QBuffer output; + output.open(QBuffer::ReadWrite); + transformer.setOutput(&output); + + transformer.setFormatXmlOutput(true); + + QFETCH(QString, variableName); + QFETCH(QString, variableValue); + transformer.bindVariable(variableName, variableValue); + + transformer.transform(); + + QFETCH(QString, expectedOutput); + QCOMPARE(QString(transformer.output()->readAll()), expectedOutput); +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testBindVariable_data() +{ + QTest::addColumn("input"); + QTest::addColumn("variableName"); + QTest::addColumn("variableValue"); + QTest::addColumn("expectedOutput"); + + QString integerParameter = + header + + parametersHeader + + integer + + parametersFooter + + footer; + QString integerParameterUi = + mainWidgetHeader + + parametersWidgetHeader + + parametersLayoutHeader + + integerWidgetLabel + + integerWidgetSpinBoxHeader + + integerWidgetSpinBox + + integerWidgetSpinBoxFooter + + parametersLayoutFooter + + parametersWidgetFooter + + mainWidgetFooter; + integerParameterUi.replace("QSpinBox", "ctkSliderWidget"); + QTest::newRow("QSpinBox -> ctkSpinBox") << integerParameter + << QString("integerWidget") + << QString("ctkSliderWidget") + << integerParameterUi; +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testXslExtraTransformation() +{ + ctkCmdLineModuleXslTransform transformer; + + QFile transformation(":/ctkCmdLineModuleXmlToQtUi.xsl"); + transformer.setXslTransformation(&transformation); + + QFETCH(QString, input); + QByteArray inputArray(input.toUtf8()); + QBuffer inputBuffer(&inputArray); + transformer.setInput(&inputBuffer); + + QBuffer output; + output.open(QBuffer::ReadWrite); + transformer.setOutput(&output); + + transformer.setFormatXmlOutput(true); + + QFETCH(QString, extra); + QByteArray extraTransformationArray(extra.toUtf8()); + QBuffer extraTransformationBuffer(&extraTransformationArray); + transformer.setXslExtraTransformation(&extraTransformationBuffer); + + transformer.transform(); + + QFETCH(QString, expectedOutput); + //qDebug() << transformer.output(); + //qDebug() << expectedOutput; + QCOMPARE(QString(transformer.output()->readAll()), expectedOutput); +} + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtXslTransformTester::testXslExtraTransformation_data() +{ + QString extra = + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " -999999999\n" + " \n" + " \n" + " 999999999\n" + " \n" + " \n" + " \n" + " \n" + " " + " \n" + " \n" + " \n" + " \n" + " -999999999\n" + " \n" + " \n" + " 999999999\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n" + ; + QString integerWidgetSliderSpinBox = + " \n" + " \n" + " \n" + " \n" + " -999999999\n" + " \n" + " \n" + " 999999999\n" + " \n" + " \n" + " Integer description\n" + " \n" + " \n" + " value\n" + " \n" + " \n" + " 1\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " -999999999\n" + " \n" + " \n" + " 999999999\n" + " \n" + " \n" + " Integer description\n" + " \n" + " \n" + " value\n" + " \n" + " \n" + " 1\n" + " \n" + " \n" + " \n" + " \n" + ; + QTest::addColumn("input"); + QTest::addColumn("extra"); + QTest::addColumn("expectedOutput"); + + QString integerParameter = + header + + parametersHeader + + integer + + parametersFooter + + footer; + QString integerParameterUi = + mainWidgetHeader + + parametersWidgetHeader + + parametersLayoutHeader + + integerWidgetLabel + + integerWidgetSpinBoxHeader + + integerWidgetSliderSpinBox + + integerWidgetSpinBoxFooter + + parametersLayoutFooter + + parametersWidgetFooter + + mainWidgetFooter; + QTest::newRow("QSpinBox -> QSlider+QSpinBox") << integerParameter + << extra + << integerParameterUi; + +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleQtXslTransformTest) +#include "moc_ctkCmdLineModuleQtXslTransformTest.cpp" diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.cpp b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.cpp new file mode 100644 index 0000000000..f5e9a487e7 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.cpp @@ -0,0 +1,42 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFrontendFactoryQtGui.h" + +#include "ctkCmdLineModuleFrontendQtGui.h" + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontendQtGui *ctkCmdLineModuleFrontendFactoryQtGui::create(const ctkCmdLineModuleReference &moduleRef) +{ + return new ctkCmdLineModuleFrontendQtGui(moduleRef); +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleFrontendFactoryQtGui::name() const +{ + return "Qt Gui"; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleFrontendFactoryQtGui::description() const +{ + return "A frontend using the QtGui library."; +} diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.h b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.h new file mode 100644 index 0000000000..1429e479e7 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendFactoryQtGui.h @@ -0,0 +1,51 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFRONTENDFACTORYQTGUI_H +#define CTKCMDLINEMODULEFRONTENDFACTORYQTGUI_H + +#include "ctkCommandLineModulesFrontendQtGuiExport.h" + +#include "ctkCmdLineModuleFrontendFactory.h" +#include "ctkCmdLineModuleFrontendQtGui.h" + +/** + * \class ctkCmdLineModuleFrontendFactoryQtGui + * \brief Factory class to instantiate Qt widget based front-ends. + * \ingroup CommandLineModulesFrontendQtGui_API + * + * The created front-end instances assume that the CTKWidgetPlugin library (a Qt Designer plug-in) + * is available in the applications search path. See also ctkCmdLineModuleFrontendQtGui. + * + * @see ctkCmdLineModuleFrontendQtGui + */ +class CTK_CMDLINEMODULEQTGUI_EXPORT ctkCmdLineModuleFrontendFactoryQtGui : public ctkCmdLineModuleFrontendFactory +{ + +public: + + virtual QString name() const; + virtual QString description() const; + + virtual ctkCmdLineModuleFrontendQtGui* create(const ctkCmdLineModuleReference& moduleRef); +}; + +#endif // CTKCMDLINEMODULEFRONTENDFACTORYQTGUI_H diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.cpp b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.cpp new file mode 100644 index 0000000000..b25b51a791 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.cpp @@ -0,0 +1,222 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFrontendQtGui.h" + +#include "ctkCmdLineModuleReference.h" +#include "ctkCmdLineModuleXslTransform.h" +#include "ctkCmdLineModuleObjectTreeWalker_p.h" +#include "ctkCmdLineModuleQtUiLoader.h" + +#include +#include +#include +#include +#include +#include + +#include + +//----------------------------------------------------------------------------- +struct ctkCmdLineModuleFrontendQtGuiPrivate +{ + ctkCmdLineModuleFrontendQtGuiPrivate() + : Widget(NULL) + {} + + mutable QScopedPointer Loader; + mutable QScopedPointer xslFile; + mutable QScopedPointer Transform; + mutable QWidget* Widget; + + // Cache the list of parameter names + mutable QList ParameterNames; +}; + +//----------------------------------------------------------------------------- +ctkCmdLineModuleFrontendQtGui::ctkCmdLineModuleFrontendQtGui(const ctkCmdLineModuleReference& moduleRef) + : ctkCmdLineModuleFrontend(moduleRef), + d(new ctkCmdLineModuleFrontendQtGuiPrivate) +{ +} + + +//----------------------------------------------------------------------------- +ctkCmdLineModuleFrontendQtGui::~ctkCmdLineModuleFrontendQtGui() +{ +} + + +//----------------------------------------------------------------------------- +QUiLoader* ctkCmdLineModuleFrontendQtGui::uiLoader() const +{ + if (d->Loader == NULL) + { + d->Loader.reset(new ctkCmdLineModuleQtUiLoader()); + } + return d->Loader.data(); +} + + +//----------------------------------------------------------------------------- +ctkCmdLineModuleXslTransform* ctkCmdLineModuleFrontendQtGui::xslTransform() const +{ + if (d->Transform == NULL) + { + d->Transform.reset(new ctkCmdLineModuleXslTransform()); + d->xslFile.reset(new QFile(":/ctkCmdLineModuleXmlToQtUi.xsl")); + d->Transform->setXslTransformation(d->xslFile.data()); + } + return d->Transform.data(); +} + + +//----------------------------------------------------------------------------- +QVariant ctkCmdLineModuleFrontendQtGui::customValue(const QString& parameter, const QString& propertyName) const +{ + if (!d->Widget) return QVariant(); + + ctkCmdLineModuleObjectTreeWalker reader(d->Widget); + while(reader.readNextParameter()) + { + if(reader.name() == parameter) + { + return reader.value(propertyName); + } + } + return QVariant(); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGui::setCustomValue(const QString& parameter, const QVariant &value, + const QString& propertyName) +{ + if (!d->Widget) return; + + ctkCmdLineModuleObjectTreeWalker walker(d->Widget); + while(walker.readNextParameter()) + { + if(walker.name() == parameter && walker.value(propertyName) != value) + { + walker.setValue(value, propertyName); + break; + } + } +} + +//----------------------------------------------------------------------------- +QObject* ctkCmdLineModuleFrontendQtGui::guiHandle() const +{ + if (d->Widget) return d->Widget; + + QBuffer input; + input.setData(moduleReference().rawXmlDescription()); + + QBuffer uiForm; + uiForm.open(QIODevice::ReadWrite); + + ctkCmdLineModuleXslTransform* xslTransform = this->xslTransform(); + xslTransform->setInput(&input); + xslTransform->setOutput(&uiForm); + + if (!xslTransform->transform()) + { + // maybe throw an exception + qCritical() << xslTransform->errorString(); + return 0; + } + + QUiLoader* uiLoader = this->uiLoader(); +#ifdef CMAKE_INTDIR + QString appPath = QCoreApplication::applicationDirPath(); + if (appPath.endsWith(CMAKE_INTDIR)) + { + uiLoader->addPluginPath(appPath + "/../designer"); + } +#endif + d->Widget = uiLoader->load(&uiForm); + return d->Widget; +} + + +//----------------------------------------------------------------------------- +QVariant ctkCmdLineModuleFrontendQtGui::value(const QString ¶meter, int role) const +{ + Q_UNUSED(role) + + // This will always return data using the default property for parameter values, + // which holds the data for the DisplayRole. + return customValue(parameter); +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGui::setValue(const QString ¶meter, const QVariant &value, int role) +{ + if (role != DisplayRole) return; + + QVariant oldValue = this->customValue(parameter); + + // This sets the value of the default QObject property for the DisplayRole. + this->setCustomValue(parameter, value); + + // Before emitting the signal, get the actual value because it might be different + // (Widgets with constraints on the value domain might adapt the value). + QVariant currentValue = this->customValue(parameter); + if (currentValue != oldValue) + { + emit valueChanged(parameter, currentValue); + } +} + + +//----------------------------------------------------------------------------- +QList ctkCmdLineModuleFrontendQtGui::parameterNames() const +{ + if (!d->ParameterNames.empty()) return d->ParameterNames; + + // Compute the list of parameter names using the widget hierarchy + // if it has already created (otherwise fall back to the superclass + // implementation. + // This avoids creating a ctkCmdLineModuleDescription instance. + if (d->Widget == 0) return ctkCmdLineModuleFrontend::parameterNames(); + + ctkCmdLineModuleObjectTreeWalker walker(d->Widget); + while(walker.readNextParameter()) + { + d->ParameterNames.push_back(walker.name()); + } + return d->ParameterNames; +} + + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtGui::setParameterContainerEnabled(const bool& enabled) +{ + if (d->Widget == 0) return; + + ctkCmdLineModuleObjectTreeWalker walker(d->Widget); + while(walker.readNextParameterContainer()) + { + QVariant value(enabled); + walker.setValue(value, "enabled"); + } +} diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.h b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.h new file mode 100644 index 0000000000..4f321aa182 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleFrontendQtGui.h @@ -0,0 +1,212 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFRONTENDQTGUI_H +#define CTKCMDLINEMODULEFRONTENDQTGUI_H + +#include "ctkCmdLineModuleFrontend.h" + +#include "ctkCommandLineModulesFrontendQtGuiExport.h" + +class ctkCmdLineModuleReference; +class ctkCmdLineModuleXslTransform; + +class QUiLoader; +class QWidget; + +struct ctkCmdLineModuleFrontendQtGuiPrivate; + +/** + * \class ctkCmdLineModuleFrontendQtGui + * \brief A Qt based implementation of the module front end. + * \ingroup CommandLineModulesFrontendQtGui_API + * + * This class is able to generate a Qt widgets based GUI from the XML description of + * a given module. It uses a customizable XML stylesheet to transform the raw XML description + * into a .ui file which is fed into a QUiLoader to generate the GUI at runtime. + * + * Sub-classes have several possibilities to customize the generated GUI: + *
      + *
    • Override uiLoader() and provide your own QUiLoader or ctkCmdLineModuleQtUiLoader sub-class + * which knows how to instantiate widget types (see the table below for widget class names).
    • + *
    • Bind variables to the ctkCmdLineModuleXslTranform object returned by xslTransform() to + * customize widget class names and property names. This may require you to provide a Qt Designer + * plug-in for your custom widget classes if you do not implement your own widget instantiation + * code in a custom QUiLoader.
    • + *
    • Advanced: Override fragments of the XML stylesheet using ctkCmdLineModuleXslTranform::setXslExtraTransformation()
    • + *
    + * + * All widget classes are assumed to expose a readable and writable QObject property for storing and + * retrieving current front-end values via the DisplayRole role. + * + * The following table lists the available XSL parameters (setable via ctkCmdLineModuleXslTransform::bindVariable()), + * and their default values for all parameter types and created container widgets: + * + * \htmlonly + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Parameter TypeXSL Parameters
    Widget ClassDefaultProperty Name (DisplayRole)Default
    booleanbooleanWidgetQCheckBoxbooleanValuePropertychecked
    integerintegerWidgetQSpinBoxintegerValuePropertyvalue
    floatfloatingWidgetQDoubleSpinBoxfloatValuePropertyvalue
    doublefloatingWidgetQDoubleSpinBoxfloatValuePropertyvalue
    integer-vectorvectorWidgetQLineEditvectorValuePropertytext
    float-vectorvectorWidgetQLineEditvectorValuePropertyvalue
    double-vectorvectorWidgetQLineEditvectorValuePropertyvalue
    string-vectorvectorWidgetQLineEditvectorValuePropertyvalue
    integer-enumerationenumWidgetQComboBox (ctkCmdLineModuleQtUiLoader instantiates a custom private QComboBox sub-class)enumerationValuePropertycurrentEnumeration
    float-enumerationenumWidgetQComboBoxenumerationValuePropertycurrentEnumeration
    double-enumerationenumWidgetQComboBoxenumerationValuePropertycurrentEnumeration
    string-enumerationenumWidgetQComboBoxenumerationValuePropertycurrentEnumeration
    file (input channel)fileInputWidgetctkPathLineEditfileInputValuePropertycurrentPath
    file (output channel)fileOutputWidgetctkPathLineEditfileOutputValuePropertycurrentPath
    geometry (input channel)fileInputWidgetctkPathLineEditgeometryInputValuePropertycurrentPath
    geometry (output channel)fileOutputWidgetctkPathLineEditgeometryOutputValuePropertycurrentPath
    directorydirectoryWidgetctkPathLineEditdirectoryValuePropertycurrentPath
    pointpointWidgetctkCoordinatesWidgetpointValuePropertycoordinates
    regionpointWidgetctkCoordinatesWidgetpointValuePropertycoordinates
    image (input channel)imageInputWidgetctkPathLineEditimageInputValuePropertycurrentPath
    image (output channel)imageOutputWidgetctkPathLineEditimageOutputValuePropertycurrentPath
    [main container]executableWidgetQWidgetn/an/a
    [group container]parametersWidgetctkCollapsibleGroupBoxn/an/a
    [unknown type]unsupportedWidgetQLabeln/an/a
    + * \endhtmlonly + */ +class CTK_CMDLINEMODULEQTGUI_EXPORT ctkCmdLineModuleFrontendQtGui : public ctkCmdLineModuleFrontend +{ + +public: + + ctkCmdLineModuleFrontendQtGui(const ctkCmdLineModuleReference& moduleRef); + virtual ~ctkCmdLineModuleFrontendQtGui(); + + // ctkCmdLineModuleFrontend overrides + + /** + * @brief Create the actual Qt GUI. + * @return The parent widget for the created GUI. + * + * The returned object is either NULL or can always be casted to QWidget*. + */ + virtual QObject* guiHandle() const; + + /** + * @brief Retrieves the current parameter value using the default QObject property for + * parameter values. + * @param parameter + * @param role + * + * This implementation ignores the role argument and always returns + * the value held by the default property, which usually correspongs to the + * DisplayRole. + * + * @see ctkCmdLineModuleFrontend::value() + */ + virtual QVariant value(const QString& parameter, int role = LocalResourceRole) const; + + /** + * @brief Sets the parameter value. + * @param parameter + * @param value + * @param role + * + * This implementation does nothing if the role parameter does not equal + * ctkCmdLineModuleFrontend::DisplayRole. If it does, it sets the value of the default + * QObject property to the provided value. + * + * @see ctkCmdLiineModuleFrontend::setValue() + */ + virtual void setValue(const QString& parameter, const QVariant& value, int role = DisplayRole); + + virtual QList parameterNames() const; + + /** + * \brief There is a container QWidget within each group box, so you can + * set the container widget to enabled/disabled, thereby affecting all + * child widgets, without overwriting the enabled/disabled status of the + * child widget. + * @param enabled if true then enabled else disabled + */ + virtual void setParameterContainerEnabled(const bool& enabled); + +protected: + + /** + * @brief Get the QUiLoader for instantiating widgets. + * @return The QUiLoader. + * + * Override this method to provide your own QUiLoader sub-class for application-specific + * customization of certain widgets. + * + * @see ctkCmdLineModuleQtUiLoader + */ + virtual QUiLoader* uiLoader() const; + + /** + * @brief Get XSL transform used to transfrom the module XML description to a Qt .ui file. + * @return The XSL transform. + * + * Override this method to either customize the default transformation or completely provide + * your own. + * + * @see ctkCmdLineModuleXslTransform + */ + virtual ctkCmdLineModuleXslTransform* xslTransform() const; + + /** + * @brief Get the value of the given parameter using the given property name. + * @param parameter The parameter name. + * @param propertyName The property name from which to get the value. + * @return The parameter value for the given property name. + * + * If \c propertyName is empty, this method returns the value of the property used store the values + * for the DisplayRole role. + * + * Sub-classes can use this method to retrieve values for custom roles. + */ + QVariant customValue(const QString& parameter, const QString& propertyName = QString()) const; + + /** + * @brief Set the value of the given parameter to \c value using the given property name. + * @param parameter The parameter name. + * @param value The new value. + * @param propertyName The property name for which to set the value. + * + * If \c propertyName is empty, this method sets the value of the property used store the values + * for the DisplayRole role. + * + * Sub-classes can use this method to set values for custom roles. + */ + void setCustomValue(const QString& parameter, const QVariant& value, const QString& propertyName = QString()) ; + +private: + + QScopedPointer d; + +}; + +#endif // CTKCMDLINEMODULEFRONTENDQTGUI_H diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp similarity index 64% rename from Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp rename to Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp index f79105bdf9..797fe4e8a5 100644 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker.cpp @@ -29,26 +29,31 @@ namespace { static QString PREFIX_EXECUTABLE = "executable:"; static QString PREFIX_PARAMETER_GROUP = "paramGroup:"; +static QString PREFIX_PARAMETER_CONTAINER = "paramContainer:"; static QString PREFIX_PARAMETER = "parameter:"; } +//---------------------------------------------------------------------------- ctkCmdLineModuleObjectTreeWalker::ctkCmdLineModuleObjectTreeWalker(QObject *root) : RootObject(root), CurrentObject(0), CurrentToken(NoToken), AtEnd(false) { } +//---------------------------------------------------------------------------- ctkCmdLineModuleObjectTreeWalker::~ctkCmdLineModuleObjectTreeWalker() { } +//---------------------------------------------------------------------------- void ctkCmdLineModuleObjectTreeWalker::setRootObject(QObject* root) { RootObject = root; clear(); } +//---------------------------------------------------------------------------- void ctkCmdLineModuleObjectTreeWalker::clear() { CurrentToken = NoToken; @@ -56,21 +61,33 @@ void ctkCmdLineModuleObjectTreeWalker::clear() ObjectStack.clear(); } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::atEnd() const { return AtEnd || RootObject == 0; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::isParameterGroup() const { return CurrentToken == ParameterGroup; } + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleObjectTreeWalker::isParameterContainer() const +{ + return CurrentToken == ParameterContainer; +} + + +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::isParameter() const { return CurrentToken == Parameter; } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleObjectTreeWalker::name() const { if (CurrentObject == 0) return QString(); @@ -78,11 +95,13 @@ QString ctkCmdLineModuleObjectTreeWalker::name() const { case Executable: return CurrentObject->objectName().mid(PREFIX_EXECUTABLE.size()); case ParameterGroup: return CurrentObject->objectName().mid(PREFIX_PARAMETER_GROUP.size()); + case ParameterContainer: return CurrentObject->objectName().mid(PREFIX_PARAMETER_CONTAINER.size()); case Parameter: return CurrentObject->objectName().mid(PREFIX_PARAMETER.size()); default: return QString(); } } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleObjectTreeWalker::label() const { if (CurrentObject == 0) return QString(); @@ -90,50 +109,67 @@ QString ctkCmdLineModuleObjectTreeWalker::label() const { case Executable: return CurrentObject->objectName().mid(PREFIX_EXECUTABLE.size()); case ParameterGroup: return property("title").toString(); + case ParameterContainer: return property("name").toString(); case Parameter: return property("label").toString(); default: return QString(); } } -QVariant ctkCmdLineModuleObjectTreeWalker::value() const +//---------------------------------------------------------------------------- +QVariant ctkCmdLineModuleObjectTreeWalker::value(const QString &propertyName) const { - QString valProp = property("valueProperty").toString(); + QString valProp = propertyName; + if (valProp.isEmpty()) + { + valProp = property("valueProperty").toString(); + } return property(valProp); } -void ctkCmdLineModuleObjectTreeWalker::setValue(const QVariant& value) +//---------------------------------------------------------------------------- +void ctkCmdLineModuleObjectTreeWalker::setValue(const QVariant& value, const QString &propertyName) { - QVariant valProp = property("valueProperty"); - if (valProp.isValid()) + QString valProp = propertyName; + if (valProp.isEmpty()) + { + valProp = property("valueProperty").toString(); + } + + if (!valProp.isEmpty()) { - CurrentObject->setProperty(qPrintable(valProp.toString()), value); + CurrentObject->setProperty(qPrintable(valProp), value); } } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleObjectTreeWalker::flag() const { QVariant v = property("flag"); return v.isValid() ? v.toString() : QString(); } +//---------------------------------------------------------------------------- QString ctkCmdLineModuleObjectTreeWalker::longFlag() const { QVariant v = property("longflag"); return v.isValid() ? v.toString() : QString(); } +//---------------------------------------------------------------------------- int ctkCmdLineModuleObjectTreeWalker::index() const { QVariant v = property("index"); return v.isValid() ? v.toInt() : -1; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::isMultiple() const { QVariant v = property("multiple"); return v.isValid() ? v.toBool() : false; } +//---------------------------------------------------------------------------- QVariant ctkCmdLineModuleObjectTreeWalker::property(const QString &propName) const { if (CurrentObject == 0) return QVariant(); @@ -145,6 +181,7 @@ QVariant ctkCmdLineModuleObjectTreeWalker::property(const QString &propName) con return res; } +//---------------------------------------------------------------------------- ctkCmdLineModuleObjectTreeWalker::TokenType ctkCmdLineModuleObjectTreeWalker::readNext() { if (AtEnd) return NoToken; @@ -195,29 +232,43 @@ ctkCmdLineModuleObjectTreeWalker::TokenType ctkCmdLineModuleObjectTreeWalker::re return NoToken; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::readNextExecutable() { - while (!(readNext() == Executable || AtEnd)); + while (!(readNext() == Executable || AtEnd)) ; // deliberately empty loop body return !AtEnd; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::readNextParameterGroup() { - while (!(readNext() == ParameterGroup || AtEnd)); + while (!(readNext() == ParameterGroup || AtEnd)) ; // deliberately empty loop body + return !AtEnd; +} + + +//---------------------------------------------------------------------------- +bool ctkCmdLineModuleObjectTreeWalker::readNextParameterContainer() +{ + while (!(readNext() == ParameterContainer || AtEnd)) ; // deliberately empty loop body return !AtEnd; } + +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::readNextParameter() { - while (!(readNext() == Parameter || AtEnd)); + while (!(readNext() == Parameter || AtEnd)) ; // deliberately empty loop body return !AtEnd; } +//---------------------------------------------------------------------------- ctkCmdLineModuleObjectTreeWalker::TokenType ctkCmdLineModuleObjectTreeWalker::tokenType() const { return CurrentToken; } +//---------------------------------------------------------------------------- QVariant ctkCmdLineModuleObjectTreeWalker::prefixedProperty(const QString& propName) const { if (CurrentObject == 0) return QString(); @@ -227,6 +278,7 @@ QVariant ctkCmdLineModuleObjectTreeWalker::prefixedProperty(const QString& propN { case ctkCmdLineModuleObjectTreeWalker::Executable: prefixedName = PREFIX_EXECUTABLE + propName; case ctkCmdLineModuleObjectTreeWalker::ParameterGroup: prefixedName = PREFIX_PARAMETER_GROUP + propName; + case ctkCmdLineModuleObjectTreeWalker::ParameterContainer: prefixedName = PREFIX_PARAMETER_CONTAINER + propName; case ctkCmdLineModuleObjectTreeWalker::Parameter: prefixedName = PREFIX_PARAMETER + propName; default: ; } @@ -234,6 +286,7 @@ QVariant ctkCmdLineModuleObjectTreeWalker::prefixedProperty(const QString& propN return CurrentObject->property(qPrintable(prefixedName)); } +//---------------------------------------------------------------------------- ctkCmdLineModuleObjectTreeWalker::TokenType ctkCmdLineModuleObjectTreeWalker::token(QObject* obj) { @@ -241,10 +294,12 @@ ctkCmdLineModuleObjectTreeWalker::token(QObject* obj) QString name = obj->objectName(); if (name.startsWith(PREFIX_EXECUTABLE)) return ctkCmdLineModuleObjectTreeWalker::Executable; if (name.startsWith(PREFIX_PARAMETER_GROUP)) return ctkCmdLineModuleObjectTreeWalker::ParameterGroup; + if (name.startsWith(PREFIX_PARAMETER_CONTAINER)) return ctkCmdLineModuleObjectTreeWalker::ParameterContainer; if (name.startsWith(PREFIX_PARAMETER)) return ctkCmdLineModuleObjectTreeWalker::Parameter; return ctkCmdLineModuleObjectTreeWalker::NoToken; } +//---------------------------------------------------------------------------- bool ctkCmdLineModuleObjectTreeWalker::setCurrent(QObject* obj) { ctkCmdLineModuleObjectTreeWalker::TokenType t = token(obj); diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h similarity index 82% rename from Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h rename to Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h index a396055f32..75adc8adf0 100644 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleObjectTreeWalker_p.h @@ -23,10 +23,16 @@ #define CTKCMDLINEMODULEOBJECTTREEWALKER_H #include +#include class QObject; class QVariant; +/** + * \class ctkCmdLineModuleObjectTreeWalker + * \brief Non-exported helper class to iterate through GUI widgets. + * \ingroup CommandLineModulesFrontendQtGui + */ class ctkCmdLineModuleObjectTreeWalker { @@ -36,6 +42,7 @@ class ctkCmdLineModuleObjectTreeWalker NoToken, Executable, ParameterGroup, + ParameterContainer, Parameter }; @@ -48,13 +55,14 @@ class ctkCmdLineModuleObjectTreeWalker bool atEnd() const; bool isParameterGroup() const; + bool isParameterContainer() const; bool isParameter() const; QString name() const; QString label() const; - QVariant value() const; + QVariant value(const QString& propertyName = QString()) const; - void setValue(const QVariant& value); + void setValue(const QVariant& value, const QString& propertyName = QString()); QString flag() const; QString longFlag() const; @@ -68,6 +76,7 @@ class ctkCmdLineModuleObjectTreeWalker TokenType readNext(); bool readNextExecutable(); bool readNextParameterGroup(); + bool readNextParameterContainer(); bool readNextParameter(); TokenType tokenType() const; diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.cpp b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.cpp new file mode 100644 index 0000000000..4208f50af3 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.cpp @@ -0,0 +1,94 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleQtUiLoader.h" + +#include + + +//----------------------------------------------------------------------------- +/** + * \class ctkCmdLineModuleQtComboBox + * \brief Private subclass of QComboBox, providing the currentEnumeration and setCurrentEnumeration methods. + * \author m.clarkson@ucl.ac.uk + * \ingroup CommandLineModulesFrontendQtGui + */ +class ctkCmdLineModuleQtComboBox : public QComboBox +{ + + Q_OBJECT + Q_PROPERTY(QString currentEnumeration READ currentEnumeration WRITE setCurrentEnumeration) + +public: + + ctkCmdLineModuleQtComboBox(QWidget* parent = 0) + : QComboBox(parent) + {} + + void setCurrentEnumeration(const QString& text) + { + int i = findText(text); + if (i == -1) + { + return; + } + this->setCurrentIndex(i); + } + + QString currentEnumeration() const + { + return this->currentText(); + } + +}; + +//----------------------------------------------------------------------------- +ctkCmdLineModuleQtUiLoader::ctkCmdLineModuleQtUiLoader(QObject *parent) + : QUiLoader(parent) +{ + +} + +//----------------------------------------------------------------------------- +ctkCmdLineModuleQtUiLoader::~ctkCmdLineModuleQtUiLoader() +{ + +} + + +//----------------------------------------------------------------------------- +QWidget* ctkCmdLineModuleQtUiLoader::createWidget(const QString& className, QWidget* parent, const QString& name) +{ + QWidget* widget = NULL; + + if (className == "QComboBox") + { + widget = new ctkCmdLineModuleQtComboBox(parent); + widget->setObjectName(name); + } + else + { + widget = QUiLoader::createWidget(className, parent, name); + } + + return widget; +} + +#include "moc_ctkCmdLineModuleQtUiLoader.cpp" diff --git a/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.h b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.h new file mode 100644 index 0000000000..c2a77eaba6 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtGui/ctkCmdLineModuleQtUiLoader.h @@ -0,0 +1,54 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) University College London + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEQTUILOADER_H +#define CTKCMDLINEMODULEQTUILOADER_H + +#include +#include "ctkCommandLineModulesFrontendQtGuiExport.h" + +/** + * \class ctkCmdLineModuleQtUiLoader + * \brief Derived from QUiLoader to enable us to instantiate custom widgets at runtime, + * where this class provides ctkCmdLineModuleQtComboBox instead of QComboBox. + * \author m.clarkson@ucl.ac.uk + * \ingroup CommandLineModulesFrontendQtGui_API + */ +class CTK_CMDLINEMODULEQTGUI_EXPORT ctkCmdLineModuleQtUiLoader : public QUiLoader +{ + + Q_OBJECT + +public: + ctkCmdLineModuleQtUiLoader(QObject *parent=0); + virtual ~ctkCmdLineModuleQtUiLoader(); + + /** + * \brief If className is QComboBox, instantiates ctkCmdLineModuleQtGuiComboBox and + * otherwise delegates to base class. + * \see QUiLoader::createWidget() + */ + virtual QWidget* createWidget(const QString & className, QWidget * parent = 0, const QString & name = QString() ); + +private: + +}; // end class + +#endif // CTKCMDLINEMODULEQTUILOADER_H diff --git a/Libs/CommandLineModules/QtGui/target_libraries.cmake b/Libs/CommandLineModules/Frontend/QtGui/target_libraries.cmake similarity index 100% rename from Libs/CommandLineModules/QtGui/target_libraries.cmake rename to Libs/CommandLineModules/Frontend/QtGui/target_libraries.cmake diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/CMakeLists.txt b/Libs/CommandLineModules/Frontend/QtWebKit/CMakeLists.txt new file mode 100644 index 0000000000..b2d20a05cb --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/CMakeLists.txt @@ -0,0 +1,67 @@ +project(CTKCommandLineModulesFrontendQtWebKit) + +# +# 3rd party dependencies +# + +# +# See CTK/CMake/ctkMacroBuildLib.cmake for details +# + +set(KIT_export_directive "CTK_CMDLINEMODULEQTWEBKIT_EXPORT") + +# Additional directories to include + +# Source files +set(KIT_SRCS + ctkCmdLineModuleFrontendFactoryQtWebKit.cpp + ctkCmdLineModuleFrontendQtWebKit.cpp + ctkCmdLineModuleFrontendQtWebKit_p.h +) + +# Headers that should run through moc +set(KIT_MOC_SRCS +) + +# UI files +set(KIT_UI_FORMS +) + +# Resources +set(KIT_resources + Resources/ctkCmdLineModulesFrontendQtWebKit.qrc +) + +set(QT_USE_QTWEBKIT 1) +include(${QT_USE_FILE}) + +# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake +# The following macro will read the target libraries from the file 'target_libraries.cmake' +ctkFunctionGetTargetLibraries(KIT_target_libraries) + +ctkMacroBuildLib( + NAME ${PROJECT_NAME} + EXPORT_DIRECTIVE ${KIT_export_directive} + INCLUDE_DIRECTORIES ${KIT_include_directories} + SRCS ${KIT_SRCS} + MOC_SRCS ${KIT_MOC_SRCS} + UI_FORMS ${KIT_UI_FORMS} + TARGET_LIBRARIES ${KIT_target_libraries} + RESOURCES ${KIT_resources} + LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) + +target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES}) + +if(CTK_WRAP_PYTHONQT_FULL OR CTK_WRAP_PYTHONQT_LIGHT) + ctkMacroBuildLibWrapper( + TARGET ${PROJECT_NAME} + SRCS ${KIT_SRCS} + WRAPPER_LIBRARY_TYPE ${CTK_LIBRARY_MODE} + ) +endif() + +# Testing +if(BUILD_TESTING) +# add_subdirectory(Testing) +endif() diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/README.md b/Libs/CommandLineModules/Frontend/QtWebKit/README.md new file mode 100644 index 0000000000..e3106ecaef --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/README.md @@ -0,0 +1,13 @@ +Qt WebKit Frontend (experimental) {#CommandLineModulesFrontendQtWebKit_Page} +================================= + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModulesFrontendQtWebKit_Page.html) form. \endinternal + +This front-end uses an XML stylesheet to transform the raw XML description of a module +into a HTML document and uses Qt WebKit to render this document inside a Qt widget. + +The front-end is experimental and serves as a *proof-of-concept*, see also the +ctkCmdLineModuleFrontendQtWebKit class. + +See the \ref CommandLineModulesFrontendQtWebKit_API module for the API documentation. diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModuleXmlToPlainHtml.xsl b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModuleXmlToPlainHtml.xsl new file mode 100644 index 0000000000..8e6dd60a8d --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModuleXmlToPlainHtml.xsl @@ -0,0 +1,203 @@ + + + + + + + + + + + + bool + number + double + string + + + + + + + + + checked + coordinates + currentPath + text + currentText + value + + + + + + + + + + + +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + <xsl:value-of select="title"/> + + +
    +
    + + +
    +
    + + +
    + + + + + +
    + + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModulesFrontendQtWebKit.qrc b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModulesFrontendQtWebKit.qrc new file mode 100644 index 0000000000..90c9ab63a4 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/ctkCmdLineModulesFrontendQtWebKit.qrc @@ -0,0 +1,5 @@ + + + ctkCmdLineModuleXmlToPlainHtml.xsl + + diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/Resources/result.html b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/result.html new file mode 100644 index 0000000000..0c80e72a4c --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/Resources/result.html @@ -0,0 +1,78 @@ + + + 2D Blurring + + +
    +
    +
    +

    Scalar Parameters

    + + + Variations on scalar parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Integer Parameter + +
    Boolean Parameter + +
    Some file + +
    Some dir + +
    Some geom + +
    Double Parameter + +
    +
    +
    +

    Vector Parameters

    + + Variations on vector parameters + + + + + + + + + + +
    Float Vector Parameter + +
    String Vector Parameter + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.cpp b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.cpp new file mode 100644 index 0000000000..dabd4f238c --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.cpp @@ -0,0 +1,42 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFrontendFactoryQtWebKit.h" + +#include "ctkCmdLineModuleFrontendQtWebKit_p.h" + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontend* ctkCmdLineModuleFrontendFactoryQtWebKit::create(const ctkCmdLineModuleReference &moduleRef) +{ + return new ctkCmdLineModuleFrontendQtWebKit(moduleRef); +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleFrontendFactoryQtWebKit::name() const +{ + return "Qt WebKit (experimental)"; +} + +//---------------------------------------------------------------------------- +QString ctkCmdLineModuleFrontendFactoryQtWebKit::description() const +{ + return "An experimental frontend using the QtWebKit library."; +} diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.h b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.h new file mode 100644 index 0000000000..02811d288c --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendFactoryQtWebKit.h @@ -0,0 +1,48 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULEFRONTENDFACTORYQTWEBKIT_H +#define CTKCMDLINEMODULEFRONTENDFACTORYQTWEBKIT_H + +#include "ctkCommandLineModulesFrontendQtWebKitExport.h" + +#include "ctkCmdLineModuleFrontendFactory.h" + +/** + * \class ctkCmdLineModuleFrontendFactoryQtWebKit + * \brief QtWebKit specific implementation of ctkCmdLineModuleFrontendFactory + * \ingroup CommandLineModulesFrontendQtWebKit_API + * + * \warning This front-end is highly experimental and actually just serves as a + * proof-of-concept. + */ +class CTK_CMDLINEMODULEQTWEBKIT_EXPORT ctkCmdLineModuleFrontendFactoryQtWebKit : public ctkCmdLineModuleFrontendFactory +{ + +public: + + virtual QString name() const; + virtual QString description() const; + + virtual ctkCmdLineModuleFrontend* create(const ctkCmdLineModuleReference& moduleRef); +}; + +#endif // CTKCMDLINEMODULEFRONTENDFACTORYQTWEBKIT_H diff --git a/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit.cpp b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit.cpp new file mode 100644 index 0000000000..30c64dcf51 --- /dev/null +++ b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit.cpp @@ -0,0 +1,92 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleFrontendQtWebKit_p.h" + +#include "ctkCmdLineModuleXslTransform.h" +#include "ctkCmdLineModuleReference.h" + +#include +#include +#include +#include +#include + +#include + +//---------------------------------------------------------------------------- +ctkCmdLineModuleFrontendQtWebKit::ctkCmdLineModuleFrontendQtWebKit(const ctkCmdLineModuleReference& moduleRef) + : ctkCmdLineModuleFrontend(moduleRef) + , WebView(NULL) +{ + +} + +//---------------------------------------------------------------------------- +QObject* ctkCmdLineModuleFrontendQtWebKit::guiHandle() const +{ + if (WebView) return WebView; + + QBuffer input; + input.setData(moduleReference().rawXmlDescription()); + + QBuffer htmlOutput; + htmlOutput.open(QIODevice::ReadWrite); + ctkCmdLineModuleXslTransform xslTransform(&input, &htmlOutput); + QFile htmlTransformation(":/ctkCmdLineModuleXmlToPlainHtml.xsl"); + + xslTransform.setXslTransformation(&htmlTransformation); + if (!xslTransform.transform()) + { + // maybe throw an exception + qCritical() << xslTransform.errorString(); + return 0; + } + + this->WebView = new QWebView; + QByteArray htmlContent = htmlOutput.readAll(); + this->WebView->setHtml(htmlContent); + return this->WebView; +} + +//---------------------------------------------------------------------------- +QVariant ctkCmdLineModuleFrontendQtWebKit::value(const QString ¶meter, int role) const +{ + Q_UNUSED(role) + QWebElement webElement = this->WebView->page()->currentFrame()->findFirstElement("input[name=" + parameter + "]"); + if (webElement.isNull()) return QVariant(); + // Work around bug https://bugs.webkit.org/show_bug.cgi?id=32865 for input elements + QVariant value = webElement.evaluateJavaScript("this.value"); + qDebug() << "Found element" << webElement.tagName() << "with value" << value; + return value; +} + +//---------------------------------------------------------------------------- +void ctkCmdLineModuleFrontendQtWebKit::setValue(const QString ¶meter, const QVariant &value, int role) +{ + if (!this->WebView || role != DisplayRole) return; + + QWebElement webElement = this->WebView->page()->currentFrame()->findFirstElement("input[name=" + parameter + "]"); + if (webElement.isNull()) return; + + // Work around bug https://bugs.webkit.org/show_bug.cgi?id=32865 for input elements + webElement.evaluateJavaScript(QString("this.value='%1'").arg(value.toString())); +} diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui_p.h b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit_p.h similarity index 55% rename from Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui_p.h rename to Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit_p.h index 420c398fbd..c84b04feab 100644 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui_p.h +++ b/Libs/CommandLineModules/Frontend/QtWebKit/ctkCmdLineModuleFrontendQtWebKit_p.h @@ -1,53 +1,55 @@ /*============================================================================= - + Library: CTK - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + =============================================================================*/ -#ifndef CTKCMDLINEMODULEINSTANCEQTGUI_H -#define CTKCMDLINEMODULEINSTANCEQTGUI_H +#ifndef CTKCMDLINEMODULEFRONTENDQTWEBKIT_H +#define CTKCMDLINEMODULEFRONTENDQTWEBKIT_H -#include +#include "ctkCmdLineModuleFrontend.h" -class ctkCmdLineModuleReference; +class QWebView; -class ctkCmdLineModuleInstanceQtGui : public ctkCmdLineModuleInstance +/** + * \class ctkCmdLineModuleFrontendQtWebKit + * \brief QtWebKit specific implementation of ctkCmdLineModuleFrontend + * \ingroup CommandLineModulesFrontendQtWebKit_API + */ +class ctkCmdLineModuleFrontendQtWebKit : public ctkCmdLineModuleFrontend { public: - ctkCmdLineModuleInstanceQtGui(const ctkCmdLineModuleReference& moduleRef); + ctkCmdLineModuleFrontendQtWebKit(const ctkCmdLineModuleReference& moduleRef); - // ctkCmdLineModuleInstance overrides + // ctkCmdLineModuleFrontend overrides virtual QObject* guiHandle() const; - virtual QVariant value(const QString& parameter) const; - virtual void setValue(const QString& parameter, const QVariant& value); + virtual QVariant value(const QString& parameter, int role = LocalResourceRole) const; + virtual void setValue(const QString& parameter, const QVariant& value, int role = DisplayRole); - virtual QList parameterNames() const; + //virtual QList parameterNames() const; private: - mutable QWidget* WidgetTree; - - // Cache the list of parameter names - mutable QList ParameterNames; + mutable QWebView* WebView; }; -#endif // CTKCMDLINEMODULEINSTANCEQTGUI_H +#endif // CTKCMDLINEMODULEFRONTENDQTWEBKIT_H diff --git a/Plugins/org.commontk.slicermodule/target_libraries.cmake b/Libs/CommandLineModules/Frontend/QtWebKit/target_libraries.cmake similarity index 50% rename from Plugins/org.commontk.slicermodule/target_libraries.cmake rename to Libs/CommandLineModules/Frontend/QtWebKit/target_libraries.cmake index be60b8dba9..2eea359f6d 100644 --- a/Plugins/org.commontk.slicermodule/target_libraries.cmake +++ b/Libs/CommandLineModules/Frontend/QtWebKit/target_libraries.cmake @@ -1,10 +1,9 @@ -# See CMake/ctkMacroGetTargetLibraries.cmake # -# This file should list the libraries required to build the current CTK plugin. -# For specifying required plugins, see the manifest_headers.cmake file. +# See CMake/ctkMacroGetTargetLibraries.cmake +# +# This file should list the libraries required to build the current CTK libraries # set(target_libraries - CTKPluginFramework - CTKModuleDescription + CTKCommandLineModulesCore ) diff --git a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui.cpp b/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui.cpp deleted file mode 100644 index 22c94cd835..0000000000 --- a/Libs/CommandLineModules/QtGui/ctkCmdLineModuleInstanceQtGui.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "ctkCmdLineModuleInstanceQtGui_p.h" -#include "ctkCmdLineModuleReference.h" -#include "ctkCmdLineModuleXslTransform.h" -#include "ctkCmdLineModuleObjectTreeWalker_p.h" - -#include -#include -#include -#include - -#include - -ctkCmdLineModuleInstanceQtGui::ctkCmdLineModuleInstanceQtGui(const ctkCmdLineModuleReference& moduleRef) - : ctkCmdLineModuleInstance(moduleRef), - WidgetTree(NULL) -{ -} - -QObject* ctkCmdLineModuleInstanceQtGui::guiHandle() const -{ - if (WidgetTree) return WidgetTree; - - QBuffer input; - input.setData(moduleReference().rawXmlDescription()); - - QBuffer uiForm; - uiForm.open(QIODevice::ReadWrite); - ctkCmdLineModuleXslTransform xslTransform(&input, &uiForm); - if (!xslTransform.transform()) - { - // maybe throw an exception - qCritical() << xslTransform.errorString(); - return 0; - } - - QUiLoader uiLoader; - WidgetTree = uiLoader.load(&uiForm); - return WidgetTree; -} - -QVariant ctkCmdLineModuleInstanceQtGui::value(const QString ¶meter) const -{ - if (!WidgetTree) return QVariant(); - - ctkCmdLineModuleObjectTreeWalker reader(WidgetTree); - while(reader.readNextParameter()) - { - if(reader.name() == parameter) - { - return reader.value(); - } - } - return QVariant(); -} - -void ctkCmdLineModuleInstanceQtGui::setValue(const QString ¶meter, const QVariant &value) -{ - if (!WidgetTree) return; - - ctkCmdLineModuleObjectTreeWalker walker(WidgetTree); - while(walker.readNextParameter()) - { - if(walker.name() == parameter && walker.value() != value) - { - walker.setValue(value); - emit valueChanged(parameter, value); - } - } -} - -QList ctkCmdLineModuleInstanceQtGui::parameterNames() const -{ - if (!ParameterNames.empty()) return ParameterNames; - - // Compute the list of parameter names using the widget hierarchy - // if it has already created (otherwise fall back to the superclass - // implementation. - // This avoids creating a ctkCmdLineModuleDescription instance. - if (WidgetTree == 0) return ctkCmdLineModuleInstance::parameterNames(); - - ctkCmdLineModuleObjectTreeWalker walker(WidgetTree); - while(walker.readNextParameter()) - { - ParameterNames.push_back(walker.name()); - } - return ParameterNames; -} diff --git a/Libs/CommandLineModules/README.md b/Libs/CommandLineModules/README.md new file mode 100644 index 0000000000..4dea000c22 --- /dev/null +++ b/Libs/CommandLineModules/README.md @@ -0,0 +1,153 @@ +CTK Command Line Modules {#CommandLineModules_Page} +======================== + +\brief Overview about the Command Line Modules support in CTK. + +\internal This page is best viewed in its [Doxygen processed] +(http://www.commontk.org/docs/html/CommandLineModules_Page.html) form. \endinternal + +[TOC] + +CTK provides an API for interfacing with self-describing *runnable* modules which can provide an +XML description of their supported parameters. A runnable module is +usually (but not constrained to) a local executable and also referred to as a *command line module*. + +The XML schema for the parameter description and most of the supported feature set for a module +has been adopted from the [Slicer Execution Model](http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation). + +The API provided by CTK allows the management, GUI generation, and asynchronous execution +of such modules in a toolkit-independent and interoperable way. Application writers can rely on the +provided libraries and their API to quickly integrate command line modules into their applications. + +Developers who want to create command line modules which can be run by using the provided tools will +want to have a look at the *Creating Modules* section below. Everything else targets application writers +who want to host and mange these command line modules. + +CTK also comes with an example application, called *ctkCommandLineModuleExplorer* which can be used +to load different kinds of modules, to verify their correctness, to run - and finally inspect their output. + + +Features +-------- + +Here is short overview about the provided feature set: + +- Clear separation in front and back ends (see *Library Design* below) +- XML validation and error reporting +- Caching of XML descriptions +- Partially thread-safe, allowing to concurrently add and remove modules +- Asynchronous communication with running modules + + Start/pause/cancel support (back-end and operating system dependent) + + Result reporting + + Output reporting + +CTK also provides stable and feature-rich implementations of a Qt based front-end and a back-end handling local processes. + +### Qt Gui front-end + +The provided front-end implementation creates a Qt widgets based user interface. It also allows to customize the +GUI generation process, see the ctkCmdLineModuleFrontendQtGui class for more information. + +### Local process back-end + +The default back-end for running modules can handle local executables and runs them in a separate process. See +the ctkCmdLineModuleBackendLocalProcess class for details. + + +Creating Modules +---------------- + +Module writers usually need to create an XML file describing the parameters which are understood by the module (the actual +way how such an XML description is provided actually depends on the back-end for which the module is written). For locally +executable modules (e.g. command line programs), the XML description is usually emitted to the standard output channel by +the executable itself when it is called with a *--xml* command line argument. + +The valid XML structure for the parameter description is defined in the corresponding [schema documentation](ctkCmdLineModule.xsd) +([absolute link](http://www.commontk.org/docs/html/ctkCmdLineModule.xsd)). + +Please note that running a module may fail due to an invalid XML description. The strictness of validation is specific to the +application you are using to run the module. However, making sure the XML validates agains the given schema (raw schema file +[here](https://raw.github.com/commontk/CTK/master/Libs/CommandLineModules/Core/Resources/ctkCmdLineModule.xsd)). + +### Progress and Result reporting + +A module may report progress and intermediate results during its execution. The actual reporting mechanism depends on the type +of module. For a local executable being run for example by the ctkCmdLineModuleBackendLocalProcess back-end, reporting is done +by printing XML fragments to the standard output channel. + +For example a progress report containing a progress value and text would look like: + + + My Filter + Starting custom filter... + + Current progress: 0.2 (from [0,1.0]) + + +Here is the XML [progress and result schema documentation](ctkCmdLineModuleProcess.xsd) +([absolute link](http://www.commontk.org/docs/html/ctkCmdLineModuleProcess.xsd)) describing the valid XML fragments. The raw +schema file is available [here](https://raw.github.com/commontk/CTK/master/Libs/CommandLineModules/Backend/LocalProcess/Resources/ctkCmdLineModuleProcess.xsd). + + +Library Design +-------------- + +The Command Line Module support consists of a \subpage CommandLineModulesCore_Page library and so-called +\subpage CommandLineModulesBackEnds_Page and \subpage CommandLineModulesFrontEnds_Page. + +A front-end, a sub-class of ctkCmdLineModuleFrontend, represents a set of parameter values for a specific +module, usually associated with some kind of user interface. Front-end implementations need not be accessible outside +of the defining library, but may be exposed to allow the configuration of the GUI generation process by +sub-classing the corresponding ctkCmdLineModuleFrontendFactory implementation. A front-end can be "run" by +calling the ctkCmdLineModuleManager::run(ctkCmdLineModuleFrontend*) method and the object returned by the run() +method is used to communicate with the running module. A front-end can be "run" multiple times (with possibly different +parameter values) simultaneously. + +A back-end, a sub-class of ctkCmdLineModuleBackend, knows how to actually "run" a module. Back-end implementations +express their capabilities by overriding the ctkCmdLineModuleBackend::schemes() method and providing a list of URL +schemes this back-end can handle. For example, the ctkCmdLineModuleBackendLocalProcess back-end returns "file" since +it can handle URLs pointing to local resources (executables). Further, a back-end knows how to get a time-stamp and +the module XML description for a specific module. + +The central class for managing modules is the ctkCmdLineModuleManager. There must be at least one back-end registered +with the manager for module registrations to succeed. A module is registered by calling the +ctkCmdLineModuleManager::registerModule(const QUrl&) method, providing the URL to the module. If the URL scheme is not handled +by a previously registerd back-end, an exception is thrown. If registration succeeds, the method returns a +ctkCmdLineModuleReference object. + +Creating specific front-ends for a given module is actually independent of the ctkCmdLineModuleManager, except that a +ctkCmdLineModuleReference object is needed. To create a front-end, usually the +ctkCmdLineModuleFrontendFactory::create(const ctkCmdLineModuleReference&) method is called, returning a +ctkCmdLineModuleFrontend pointer. + +This separation of concerns in front and back ends allows for an extensible and flexible design. Front-ends and back-ends +work independent of each other and can be combined arbitrarly. + + +Quick Start +----------- + +Here is a small code example to get you started quickly. We first instantiate a ctkCmdLineModuleManager object, using +a strict validation mode and the built-in caching mechanism. + +\snippet ModuleManager/main.cpp instantiate-mm + +Next, we instantiate and register a back-end. + +\snippet ModuleManager/main.cpp register-backend + +Now we register an executable as a module with the manager. + +\snippet ModuleManager/main.cpp register-module + +To create a front-end, we use the Qt widgets implementation. + +\snippet ModuleManager/main.cpp create-frontend + +Last, we run the front-end instance, using the default values for the module parameters. + +\snippet ModuleManager/main.cpp run-module + +After the ctkCmdLineModuleManager::run() method returns, we wait for the running module to finish and print out +some data reported by it. + diff --git a/Libs/CommandLineModules/Testing/CMakeLists.txt b/Libs/CommandLineModules/Testing/CMakeLists.txt new file mode 100644 index 0000000000..ca3b9479ab --- /dev/null +++ b/Libs/CommandLineModules/Testing/CMakeLists.txt @@ -0,0 +1,5 @@ + +include_directories(${CTKCore_SOURCE_DIR} ${CTKCore_BINARY_DIR}) + +add_subdirectory(Modules) +add_subdirectory(Cpp) diff --git a/Libs/CommandLineModules/Testing/Cpp/CMakeLists.txt b/Libs/CommandLineModules/Testing/Cpp/CMakeLists.txt new file mode 100644 index 0000000000..ba61a8e665 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/CMakeLists.txt @@ -0,0 +1,102 @@ +set(KIT CTKCommandLineModules) +set(LIBRARY_NAME ${KIT}) + +set(_test_srcs) +set(_test_mocs) + +if(CTK_LIB_CommandLineModules/Frontend/QtGui) + set(QT_USE_QTUITOOLS 1) + include(${QT_USE_FILE}) + + if(CTK_LIB_CommandLineModules/Backend/LocalProcess) + set(_test_cpp_files + ctkCmdLineModuleFutureTest.cpp + ctkCmdLineModuleProcessXmlOutputTest.cpp + ) + list(APPEND _test_srcs ${_test_cpp_files}) + list(APPEND _test_mocs ${_test_cpp_files}) + endif() + if(CTK_LIB_CommandLineModules/Backend/FunctionPointer) + list(APPEND _test_srcs ctkCmdLineModuleQtCustomizationTest.cpp) + list(APPEND _test_mocs ctkCmdLineModuleQtCustomizationTest.cpp) + endif() +endif() + +create_test_sourcelist(Tests ${KIT}CppTests.cpp ${_test_srcs}) + +set(TestsToRun ${Tests}) +remove(TestsToRun ${KIT}CppTests.cpp) + +set(Tests_SRCS ${Tests_SRCS} + ctkCmdLineModuleSignalTester.cpp +) +set(Tests_MOC_SRCS ${Tests_MOC_SRCS} + ctkCmdLineModuleSignalTester.h +) +set(Tests_RESOURCES + ctkCmdLineModuleTestResources.qrc +) + +set(_base_src_include_dir ${CMAKE_SOURCE_DIR}/Libs/CommandLineModules) +set(_base_bin_include_dir ${CMAKE_BINARY_DIR}/Libs/CommandLineModules) + +include_directories( + ${CMAKE_SOURCE_DIR}/Libs/Testing + ${CMAKE_CURRENT_BINARY_DIR} + ${_base_src_include_dir}/Core + ${_base_bin_include_dir}/Core + ) + +set(_additional_link_libraries) + +if(CTK_LIB_CommandLineModules/Backend/LocalProcess) + include_directories(${_base_src_include_dir}/Backend/LocalProcess + ${_base_bin_include_dir}/Backend/LocalProcess) + list(APPEND _additional_link_libraries CTKCommandLineModulesBackendLocalProcess) +endif() + +if(CTK_LIB_CommandLineModules/Backend/FunctionPointer) + include_directories(${_base_src_include_dir}/Backend/FunctionPointer + ${_base_bin_include_dir}/Backend/FunctionPointer) + list(APPEND _additional_link_libraries CTKCommandLineModulesBackendFunctionPointer) +endif() + +if(CTK_LIB_CommandLineModules/Frontend/QtGui) + include_directories(${_base_src_include_dir}/Frontend/QtGui + ${_base_bin_include_dir}/Frontend/QtGui) + list(APPEND _additional_link_libraries CTKCommandLineModulesFrontendQtGui) +endif() + +set(Tests_MOC_CPP) +QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS}) + +if(_test_mocs) + QT4_GENERATE_MOCS(${_test_mocs}) +endif() + +set(Tests_UI_CPP) +if(TEST_UI_FORMS) + QT4_WRAP_UI(Tests_UI_CPP ${Tests_UI_FORMS}) +endif() +set(Tests_RESOURCES_SRCS) +QT4_ADD_RESOURCES(Tests_RESOURCES_SRCS ${Tests_RESOURCES}) + +add_executable(${KIT}CppTests ${Tests} ${Tests_SRCS} ${Tests_MOC_CPP} ${Tests_UI_CPP} ${Tests_RESOURCES_SRCS}) +target_link_libraries(${KIT}CppTests ${_additional_link_libraries}) +add_dependencies(${KIT}CppTests ctkCmdLineTestModules) + +if(TARGET CTKCommandLineModulesCoreCppTests) + add_dependencies(${KIT}CppTests CTKCommandLineModulesCoreCppTests) +endif() +if(TARGET CTKCommandLineModulesFrontendQtGuiCppTests) + add_dependencies(${KIT}CppTests CTKCommandLineModulesFrontendQtGuiCppTests) +endif() + +# +# Add Tests +# + +foreach(_src ${_test_srcs}) + get_filename_component(_test_name ${_src} NAME_WE) + SIMPLE_TEST(${_test_name}) +endforeach() diff --git a/Libs/CommandLineModules/Testing/Cpp/MyImageComboBoxTest.xsl b/Libs/CommandLineModules/Testing/Cpp/MyImageComboBoxTest.xsl new file mode 100644 index 0000000000..57ffb333c1 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/MyImageComboBoxTest.xsl @@ -0,0 +1,63 @@ + + + + + + + + + + + + + currentValue + + + + + + + + + + + ctkPathLineEdit::Files + + + + + + + Browse... + + + + + + + + + + + + + + + BrowseButton + clicked() + parameter: + browse() + + + + diff --git a/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleFutureTest.cpp b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleFutureTest.cpp new file mode 100644 index 0000000000..7c0b731ab7 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleFutureTest.cpp @@ -0,0 +1,479 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctkCmdLineModuleSignalTester.h" + +#include "ctkCmdLineModuleBackendLocalProcess.h" + +#include "ctkTest.h" + +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +class ctkCmdLineModuleFrontendMockupFactory : public ctkCmdLineModuleFrontendFactory +{ +public: + + virtual ctkCmdLineModuleFrontend* create(const ctkCmdLineModuleReference& moduleRef) + { + struct ModuleFrontendMockup : public ctkCmdLineModuleFrontend + { + ModuleFrontendMockup(const ctkCmdLineModuleReference& moduleRef) + : ctkCmdLineModuleFrontend(moduleRef) {} + + virtual QObject* guiHandle() const { return NULL; } + + virtual QVariant value(const QString& parameter, int role) const + { + Q_UNUSED(role) + QVariant value = currentValues[parameter]; + if (!value.isValid()) + return this->moduleReference().description().parameter(parameter).defaultValue(); + return value; + } + + virtual void setValue(const QString& parameter, const QVariant& value, int role = DisplayRole) + { + Q_UNUSED(role) + currentValues[parameter] = value; + } + + private: + + QHash currentValues; + }; + + return new ModuleFrontendMockup(moduleRef); + } + + virtual QString name() const { return "Mock-up"; } + virtual QString description() const { return "A mock-up factory for testing."; } +}; + +//----------------------------------------------------------------------------- +class ctkCmdLineModuleFutureTester : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + + void ouputDataReady(); + void errorDataReady(); + +private Q_SLOTS: + + void initTestCase(); + + void init(); + void cleanup(); + + void testStartFinish(); + void testProgress(); + void testPauseAndCancel(); + void testOutput(); + void testError(); + +private: + + QByteArray outputData; + QByteArray errorData; + + ctkCmdLineModuleFutureWatcher* currentWatcher; + + ctkCmdLineModuleFrontendMockupFactory factory; + ctkCmdLineModuleBackendLocalProcess backend; + + ctkCmdLineModuleManager manager; + + ctkCmdLineModuleReference moduleRef; + ctkCmdLineModuleFrontend* frontend; +}; + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::ouputDataReady() +{ + if (this->currentWatcher) + { + outputData.append(currentWatcher->readPendingOutputData()); + } +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::errorDataReady() +{ + if (this->currentWatcher) + { + errorData.append(currentWatcher->readPendingErrorData()); + } +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::initTestCase() +{ + manager.registerBackend(&backend); + + QUrl moduleUrl = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/ctkCmdLineModuleTestBed"); + moduleRef = manager.registerModule(moduleUrl); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::init() +{ + currentWatcher = 0; + frontend = factory.create(moduleRef); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::cleanup() +{ + delete frontend; + outputData.clear(); + errorData.clear(); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::testStartFinish() +{ + QList expectedSignals; + expectedSignals << "module.started" + + // the following signals are send when connecting a QFutureWatcher to + // an already started QFuture + << "module.progressRangeChanged(0,0)" + << "module.progressValueChanged(0)" + + << "module.progressRangeChanged(0,1002)" + + // the test module always reports error data when starting + << "module.errorReady" + + // the following two signals are send when the module reports "filter start" + << "module.progressValueChanged(1)" + << "module.progressTextChanged(Does nothing useful)" + + // imageOutput result + << "module.resultReadyAt(0,1)" + << "module.resultReadyAt(0)" + + // exitStatusOutput result + << "module.resultReadyAt(1,2)" + << "module.resultReadyAt(1)" + + // final progress report from the module + << "module.progressValueChanged(1000)" + + // progress value and text + << "module.progressValueChanged(1001)" + << "module.progressTextChanged(Finished successfully.)" + + // the following signal is sent at the end to report completion + << "module.progressValueChanged(1002)" + << "module.finished"; + + ctkCmdLineModuleSignalTester signalTester; + + ctkCmdLineModuleFuture future = manager.run(frontend); + signalTester.setFuture(future); + future.waitForFinished(); + + QCoreApplication::processEvents(); + QVERIFY(signalTester.checkSignals(expectedSignals)); + + QList results; + results << ctkCmdLineModuleResult("imageOutput", "/tmp/out.nrrd"); + results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit"); + QCOMPARE(signalTester.results(), results); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::testProgress() +{ + QList expectedSignals; + expectedSignals << "module.started" + + // the following signals are send when connecting a QFutureWatcher to + // an already started QFuture + << "module.progressRangeChanged(0,0)" + << "module.progressValueChanged(0)" + + << "module.progressRangeChanged(0,1002)" + + // the test module always reports error data when starting + << "module.errorReady" + + // the following two signals are send when the module reports "filter start" + << "module.progressValueChanged(1)" + << "module.progressTextChanged(Does nothing useful)" + + // the output data on the standard output channel + << "module.outputReady" + + // this signal is send when the module reports progress for "output1" + << "module.progressValueChanged(1000)" + << "module.progressTextChanged(Calculating output 2...)" + + // first resultNumberOutput result + << "module.resultReadyAt(0,1)" + << "module.resultReadyAt(0)" + + // imageOutput result + << "module.resultReadyAt(1,2)" + << "module.resultReadyAt(1)" + + // exitStatusOutput result + << "module.resultReadyAt(2,3)" + << "module.resultReadyAt(2)" + + // progress value and text + << "module.progressValueChanged(1001)" + << "module.progressTextChanged(Finished successfully.)" + + // the following signal is sent at the end to report completion + << "module.progressValueChanged(1002)" + << "module.finished"; + + ctkCmdLineModuleSignalTester signalTester; + + frontend->setValue("numOutputsVar", 1); + ctkCmdLineModuleFuture future = manager.run(frontend); + signalTester.setFuture(future); + + future.waitForFinished(); + + // process pending events + QCoreApplication::processEvents(); + + QVERIFY(signalTester.checkSignals(expectedSignals)); + + QList results; + results << ctkCmdLineModuleResult("resultNumberOutput", 1); + results << ctkCmdLineModuleResult("imageOutput", "/tmp/out.nrrd"); + results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit"); + QCOMPARE(signalTester.results(), results); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::testPauseAndCancel() +{ + ctkCmdLineModuleSignalTester signalTester; + + frontend->setValue("runtimeVar", 60); + ctkCmdLineModuleFuture future = manager.run(frontend); + signalTester.setFuture(future); + + QList expectedSignals; + expectedSignals << "module.started" + << "module.progressRangeChanged(0,0)" + << "module.progressValueChanged(0)" + << "module.progressRangeChanged(0,1002)" + << "module.errorReady"; + + if (future.canPause()) + { + // Due to Qt bug 12152, these two signals are reversed + expectedSignals.push_back("module.resumed"); + expectedSignals.push_back("module.paused"); + } + + // the following two signals are send when the module reports "filter start" + expectedSignals << "module.progressValueChanged(1)" + << "module.progressTextChanged(Does nothing useful)"; + + if (future.canCancel()) + { + expectedSignals.push_back("module.canceled"); + } + expectedSignals.push_back("module.finished"); + + QTest::qWait(100); + + if (future.canPause()) + { + future.pause(); + QTest::qWait(100); + QVERIFY(future.isPaused()); + } + + QVERIFY(future.isRunning()); + + if (future.canPause()) + { + future.togglePaused(); + QTest::qWait(100); + } + + QVERIFY(!future.isPaused()); + QVERIFY(future.isRunning()); + + if (future.canCancel()) + { + // give event processing a chance before killing the process + QTest::qWait(500); + future.cancel(); + } + future.waitForFinished(); + + // process pending events + QCoreApplication::processEvents(); + + QVERIFY(future.isCanceled()); + QVERIFY(future.isFinished()); + + QVERIFY(signalTester.checkSignals(expectedSignals)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::testOutput() +{ + ctkCmdLineModuleSignalTester signalTester; + + connect(signalTester.watcher(), SIGNAL(outputDataReady()), SLOT(ouputDataReady())); + connect(signalTester.watcher(), SIGNAL(errorDataReady()), SLOT(errorDataReady())); + + this->currentWatcher = signalTester.watcher(); + + frontend->setValue("numOutputsVar", 2); + frontend->setValue("errorTextVar", "Final error msg."); + ctkCmdLineModuleFuture future = manager.run(frontend); + signalTester.setFuture(future); + + future.waitForFinished(); + + // process pending events + QCoreApplication::processEvents(); + + QVERIFY(future.isFinished()); + + QList expectedSignals; + expectedSignals << "module.started" + + // the following signals are send when connecting a QFutureWatcher to + // an already started QFuture + << "module.progressRangeChanged(0,0)" + << "module.progressValueChanged(0)" + + << "module.progressRangeChanged(0,1002)" + + // the test module always reports error data when starting + << "module.errorReady" + + // the following two signals are send when the module reports "filter start" + << "module.progressValueChanged(1)" + << "module.progressTextChanged(Does nothing useful)" + + // the output data on the standard output channel "Output 1" + << "module.outputReady" + + // this signal is send when the module reports progress for "output1" + << "module.progressValueChanged(500)" + << "module.progressTextChanged(Calculating output 2...)" + + // first resultNumberOutput result + << "module.resultReadyAt(0,1)" + << "module.resultReadyAt(0)" + + // the output data on the standard output channel "Output 2" + << "module.outputReady" + + // this signal is send when the module reports progress for "output2" + << "module.progressValueChanged(1000)" + << "module.progressTextChanged(Calculating output 3...)" + + // second resultNumberOutput result + << "module.resultReadyAt(1,2)" + << "module.resultReadyAt(1)" + + // imageOutput result + << "module.resultReadyAt(2,3)" + << "module.resultReadyAt(2)" + + // exitStatusOutput result + << "module.resultReadyAt(3,4)" + << "module.resultReadyAt(3)" + + // progress value and text + << "module.progressValueChanged(1001)" + << "module.progressTextChanged(Finished successfully.)" + + // final error message + << "module.errorReady" + + // the following signal is sent at the end to report completion + << "module.progressValueChanged(1002)" + << "module.finished"; + + QVERIFY(signalTester.checkSignals(expectedSignals)); + + const char* expectedOutput = "Output 1\nOutput 2\n"; + const char* expectedError = "A superficial error message.\nFinal error msg."; + + QCOMPARE(this->outputData.data(), expectedOutput); + QCOMPARE(this->errorData.data(), expectedError); + + QCOMPARE(future.readAllOutputData().data(), expectedOutput); + QCOMPARE(future.readAllErrorData().data(), expectedError); + + QList results; + results << ctkCmdLineModuleResult("resultNumberOutput", 1); + results << ctkCmdLineModuleResult("resultNumberOutput", 2); + results << ctkCmdLineModuleResult("errorMsgOutput", "Final error msg."); + results << ctkCmdLineModuleResult("exitStatusOutput", "Normal exit"); + QCOMPARE(signalTester.results(), results); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleFutureTester::testError() +{ + frontend->setValue("fileVar", "output1"); + frontend->setValue("exitCodeVar", 24); + frontend->setValue("errorTextVar", "Some error occured\n"); + + ctkCmdLineModuleFuture future = manager.run(frontend); + + try + { + future.waitForFinished(); + QFAIL("Expected exception not thrown."); + } + catch (const ctkCmdLineModuleRunException& e) + { + QVERIFY2(e.errorCode() == 24, "Test matching error code"); + QCOMPARE(future.readAllErrorData().data(), "A superficial error message.\nSome error occured\n"); + } +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleFutureTest) +#include "moc_ctkCmdLineModuleFutureTest.cpp" diff --git a/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleProcessXmlOutputTest.cpp b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleProcessXmlOutputTest.cpp new file mode 100644 index 0000000000..18a0345659 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleProcessXmlOutputTest.cpp @@ -0,0 +1,68 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleXmlValidator.h" + +#include "ctkTest.h" + +#include +#include +#include + + +//----------------------------------------------------------------------------- +class ctkCmdLineModuleProcessXmlOutputTester : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testValidXmlOutput(); + +}; + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleProcessXmlOutputTester::testValidXmlOutput() +{ + QString processLocation = QCoreApplication::applicationDirPath() + "/ctkCmdLineModuleTestBed"; + QProcess process; + process.start(processLocation, QStringList() << "--numOutputs" << "1" << "dummy"); + + QVERIFY(process.waitForFinished()); + QByteArray output = process.readAllStandardOutput(); + QVERIFY(!output.isEmpty()); + output.prepend(""); + output.append(""); + + QBuffer buffer(&output); + buffer.open(QIODevice::ReadOnly); + ctkCmdLineModuleXmlValidator xmlValidator(&buffer); + + QFile schema(":/ctkCmdLineModuleProcess.xsd"); + QVERIFY(schema.open(QIODevice::ReadOnly)); + xmlValidator.setInputSchema(&schema); + + QVERIFY(xmlValidator.validateInput()); +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleProcessXmlOutputTest) +#include "moc_ctkCmdLineModuleProcessXmlOutputTest.cpp" diff --git a/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleQtCustomizationTest.cpp b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleQtCustomizationTest.cpp new file mode 100644 index 0000000000..b79c38c393 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleQtCustomizationTest.cpp @@ -0,0 +1,295 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkCmdLineModuleManager.h" +#include "ctkCmdLineModuleFrontendQtGui.h" +#include "ctkCmdLineModuleBackendFunctionPointer.h" +#include "ctkCmdLineModuleParameter.h" +#include "ctkCmdLineModuleParameterGroup.h" +#include "ctkCmdLineModuleDescription.h" +#include "ctkCmdLineModuleXslTransform.h" +#include "ctkCmdLineModuleFuture.h" + +#include "ctkTest.h" + +// ---------------------------------------------------------------------------- +struct MyImageData : public ctk::CmdLineModuleBackendFunctionPointer::ImageType +{ + MyImageData(const QString& path = QString()) + : Path(path) + {} + + QString Label; + QString Path; +}; + +Q_DECLARE_METATYPE(MyImageData) +Q_DECLARE_METATYPE(const MyImageData*) + +// ---------------------------------------------------------------------------- +namespace ctk { +namespace CmdLineModuleBackendFunctionPointer { + +template<> +QString GetParameterTypeName() +{ + return "image"; +} + +}} + +// ---------------------------------------------------------------------------- +class MyImageComboBox : public QComboBox +{ + Q_OBJECT + +public: + + Q_PROPERTY(QString currentLabel READ currentLabel WRITE setCurrentLabel) + Q_PROPERTY(QString currentPath READ currentPath WRITE setCurrentPath) + Q_PROPERTY(const MyImageData* currentImage READ currentImage) + + MyImageComboBox(QWidget* parent = 0) + : QComboBox(parent) + { + imageData << MyImageData("/path/to/image1") + << MyImageData("/path/to/image2") + << MyImageData("/path/to/image3"); + + this->addItem("Image 1"); + this->addItem("Image 2"); + this->addItem("Image 3"); + } + + QString currentLabel() const + { + return this->imageData.at(this->currentIndex()).Label; + } + + void setCurrentLabel(const QString& label) + { + for(int i = 0; i < imageData.size(); ++i) + { + if (imageData[i].Label == label) + { + this->setCurrentIndex(i); + break; + } + } + } + + QString currentPath() const + { + return this->imageData.at(this->currentIndex()).Path; + } + + void setCurrentPath(const QString& path) + { + this->imageData[this->currentIndex()].Path = path; + } + + const MyImageData* currentImage() const + { + return &this->imageData.at(this->currentIndex()); + } + +private: + + QList imageData; +}; + + +// ---------------------------------------------------------------------------- +class MyQtGuiFrontend : public ctkCmdLineModuleFrontendQtGui +{ +public: + MyQtGuiFrontend(const ctkCmdLineModuleReference& moduleRef) + : ctkCmdLineModuleFrontendQtGui(moduleRef) + {} + + QUiLoader* uiLoader() const + { + struct MyUiLoader : public QUiLoader { + QStringList availableWidgets() const + { + return QUiLoader::availableWidgets() << "MyImageComboBox"; + } + QWidget* createWidget(const QString& className, QWidget* parent, const QString& name) + { + if (className == "MyImageComboBox") + { + MyImageComboBox* comboBox = new MyImageComboBox(parent); + comboBox->setObjectName(name); + comboBox->setCurrentIndex(1); + return comboBox; + } + return QUiLoader::createWidget(className, parent, name); + } + }; + static MyUiLoader myUiLoader; + return &myUiLoader; + } + + ctkCmdLineModuleXslTransform* xslTransform() const + { + static bool initialized = false; + ctkCmdLineModuleXslTransform* transform = ctkCmdLineModuleFrontendQtGui::xslTransform(); + if (!initialized) + { + transform->bindVariable("imageInputWidget", "MyImageComboBox"); + transform->bindVariable("imageValueProperty", "currentLabel"); + static QFile extraXsl(":/MyImageComboBoxTest.xsl"); + transform->setXslExtraTransformation(&extraXsl); + initialized = true; + } + return transform; + } + + QVariant value(const QString ¶meter, int role = LocalResourceRole) const + { + if (role == UserRole) + { + ctkCmdLineModuleParameter param = this->moduleReference().description().parameter(parameter); + if (param.channel() == "input" && param.tag() == "image") + { + return this->customValue(parameter, "currentImage"); + } + return QVariant(); + } + else if (role == LocalResourceRole) + { + return this->customValue(parameter, "currentPath"); + } + else + { + return ctkCmdLineModuleFrontendQtGui::value(parameter, role); + } + } + + void setValue(const QString ¶meter, const QVariant &value, int role = DisplayRole) + { + if (role == LocalResourceRole) + { + this->setCustomValue(parameter, value, "currentPath"); + } + else + { + ctkCmdLineModuleFrontendQtGui::setValue(parameter, value, role); + } + } +}; + +// ---------------------------------------------------------------------------- +class MyFpBackend : public ctkCmdLineModuleBackendFunctionPointer +{ + +protected: + + QList arguments(ctkCmdLineModuleFrontend *frontend) const + { + QList args; + foreach(ctkCmdLineModuleParameter param, frontend->parameters()) + { + QVariant arg = frontend->value(param.name(), ctkCmdLineModuleFrontend::UserRole); + if (!arg.isValid()) + { + arg = frontend->value(param.name()); + } + args << arg; + } + + return args; + } +}; + +// ---------------------------------------------------------------------------- +QString CustomImageDataPath; +void CustomImageTypeModule(const MyImageData* imageData) +{ + CustomImageDataPath = imageData->Path; +} + +// ---------------------------------------------------------------------------- +class ctkCmdLineModuleQtCustomizationTester: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testCustomization(); + +}; + +// ---------------------------------------------------------------------------- +void ctkCmdLineModuleQtCustomizationTester::testCustomization() +{ + qRegisterMetaType("const MyImageData*"); + + ctkCmdLineModuleManager moduleManager; + + MyFpBackend fpBackend; + fpBackend.registerFunctionPointer("Image Type Customization", CustomImageTypeModule); + + moduleManager.registerBackend(&fpBackend); + QUrl url = fpBackend.registeredFunctionPointers().front(); + moduleManager.registerModule(url); + + ctkCmdLineModuleReference moduleRef = moduleManager.moduleReference(url); + + ctkCmdLineModuleFrontend* fpFrontend = new MyQtGuiFrontend(moduleRef); + // force the creation of the frontend gui + fpFrontend->guiHandle(); + + QString expectedImageValue = "/path/to/image2"; + QString actualImageValue = fpFrontend->value("param0").toString(); + QCOMPARE(actualImageValue, expectedImageValue); + + // get a custom QVariant value holding the custom widget + QCOMPARE(fpFrontend->value("param0", ctkCmdLineModuleFrontend::UserRole).value()->Path, + expectedImageValue); + + // now set the property for the "LocalResourceRole" (the default property) to something else + expectedImageValue = "/tmp/path/to/image2"; + fpFrontend->setValue("param0", expectedImageValue, ctkCmdLineModuleFrontend::LocalResourceRole); + QCOMPARE(fpFrontend->value("param0").toString(), expectedImageValue); + + QVERIFY(CustomImageDataPath.isEmpty()); + + // run the module (function pointer) and check that is gets the tmp path + ctkCmdLineModuleFuture future = moduleManager.run(fpFrontend); + QTest::qSleep(500); + QApplication::processEvents(); + future.waitForFinished(); + + QCOMPARE(CustomImageDataPath, expectedImageValue); +} + + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCmdLineModuleQtCustomizationTest) +#include "moc_ctkCmdLineModuleQtCustomizationTest.cpp" diff --git a/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp new file mode 100644 index 0000000000..d542cdbf5b --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.cpp @@ -0,0 +1,187 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "ctkCmdLineModuleSignalTester.h" + +#include + +//----------------------------------------------------------------------------- +ctkCmdLineModuleSignalTester::ctkCmdLineModuleSignalTester() +{ + connect(&Watcher, SIGNAL(started()), SLOT(moduleStarted())); + connect(&Watcher, SIGNAL(canceled()), SLOT(moduleCanceled())); + connect(&Watcher, SIGNAL(finished()), SLOT(moduleFinished())); + + connect(&Watcher, SIGNAL(paused()), SLOT(modulePaused())); + connect(&Watcher, SIGNAL(resumed()), SLOT(moduleResumed())); + + connect(&Watcher, SIGNAL(resultReadyAt(int)), SLOT(resultReadyAt(int))); + connect(&Watcher, SIGNAL(resultsReadyAt(int,int)), SLOT(resultReadyAt(int,int))); + + connect(&Watcher, SIGNAL(progressRangeChanged(int,int)), SLOT(progressRangeChanged(int,int))); + connect(&Watcher, SIGNAL(progressTextChanged(QString)), SLOT(progressTextChanged(QString))); + connect(&Watcher, SIGNAL(progressValueChanged(int)), SLOT(progressValueChanged(int))); + + connect(&Watcher, SIGNAL(outputDataReady()), SLOT(outputDataReady())); + connect(&Watcher, SIGNAL(errorDataReady()), SLOT(errorDataReady())); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::setFuture(const ctkCmdLineModuleFuture &future) +{ + this->Watcher.setFuture(future); +} + +//----------------------------------------------------------------------------- +ctkCmdLineModuleFutureWatcher *ctkCmdLineModuleSignalTester::watcher() +{ + return &this->Watcher; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleStarted() +{ + Events.push_back("module.started"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleFinished() +{ + Events.push_back("module.finished"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleProgressValueChanged(int progress) +{ + Events.push_back(QString("module.progressValueChanged(%1)").arg(progress)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleProgressTextChanged(const QString& text) +{ + Events.push_back(QString("module.progressTextChanged(\"%1\")").arg(text)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::modulePaused() +{ + Events.push_back("module.paused"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleResumed() +{ + Events.push_back("module.resumed"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::moduleCanceled() +{ + Events.push_back("module.canceled"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::resultReadyAt(int resultIndex) +{ + Events.push_back(QString("module.resultReadyAt(%1)").arg(resultIndex)); + Results.push_back(Watcher.resultAt(resultIndex)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::resultReadyAt(int beginIndex, int endIndex) +{ + Events.push_back(QString("module.resultReadyAt(%1,%2)").arg(beginIndex).arg(endIndex)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::progressRangeChanged(int minimum, int maximum) +{ + Events.push_back(QString("module.progressRangeChanged(%1,%2)").arg(minimum).arg(maximum)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::progressValueChanged(int progressValue) +{ + Events.push_back(QString("module.progressValueChanged(%1)").arg(progressValue)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::progressTextChanged(const QString &progressText) +{ + Events.push_back(QString("module.progressTextChanged(%1)").arg(progressText)); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::outputDataReady() +{ + Events.push_back("module.outputReady"); +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::errorDataReady() +{ + Events.push_back("module.errorReady"); +} + +//----------------------------------------------------------------------------- +bool ctkCmdLineModuleSignalTester::checkSignals(const QList& expectedSignals) +{ + if (Events.size() != expectedSignals.size()) + { + dumpSignals(expectedSignals); + return false; + } + + for (int i=0; i < expectedSignals.size(); ++i) + { + if (expectedSignals[i] != Events[i]) + { + dumpSignals(expectedSignals); + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +void ctkCmdLineModuleSignalTester::dumpSignals(const QList& expectedSignals) +{ + int max = Events.size() > expectedSignals.size() ? Events.size() : expectedSignals.size(); + qDebug() << "Expected signal -- Actual signal"; + for (int i = 0; i < max; ++i) + { + QString sig = i < Events.size() ? Events[i] : QString(); + if (i < expectedSignals.size()) + { + qDebug() << " " << expectedSignals[i] << "--" << sig; + } + else + { + qDebug() << " " << "- NONE - " << "--" << sig; + } + } +} + +//----------------------------------------------------------------------------- +QList ctkCmdLineModuleSignalTester::results() const +{ + return Results; +} diff --git a/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.h b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.h new file mode 100644 index 0000000000..15fe43c4c1 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleSignalTester.h @@ -0,0 +1,76 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef CTKCMDLINEMODULESIGNALTESTER_H +#define CTKCMDLINEMODULESIGNALTESTER_H + +#include "ctkCmdLineModuleFutureWatcher.h" + +#include +#include + +class ctkCmdLineModuleFuture; + +class ctkCmdLineModuleSignalTester : public QObject +{ + Q_OBJECT + +public: + + ctkCmdLineModuleSignalTester(); + + void setFuture(const ctkCmdLineModuleFuture& future); + ctkCmdLineModuleFutureWatcher* watcher(); + + bool checkSignals(const QList& expectedSignals); + void dumpSignals(const QList& expectedSignals); + + QList results() const; + +public Q_SLOTS: + + virtual void moduleStarted(); + virtual void moduleFinished(); + virtual void moduleProgressValueChanged(int progress); + virtual void moduleProgressTextChanged(const QString& text); + + virtual void modulePaused(); + virtual void moduleResumed(); + virtual void moduleCanceled(); + + virtual void resultReadyAt(int resultIndex); + virtual void resultReadyAt(int beginIndex, int endIndex); + + virtual void progressRangeChanged(int minimum, int maximum); + virtual void progressValueChanged(int progressValue); + virtual void progressTextChanged(const QString &progressText); + + virtual void outputDataReady(); + virtual void errorDataReady(); + +private: + + ctkCmdLineModuleFutureWatcher Watcher; + QList Events; + QList Results; +}; + +#endif // CTKCMDLINEMODULESIGNALTESTER_H diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.qrc b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleTestResources.qrc similarity index 55% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.qrc rename to Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleTestResources.qrc index 1cee651910..71b03099a6 100644 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.qrc +++ b/Libs/CommandLineModules/Testing/Cpp/ctkCmdLineModuleTestResources.qrc @@ -1,5 +1,5 @@ - ctkCLIModuleTour.xml + MyImageComboBoxTest.xsl diff --git a/Libs/CommandLineModules/Testing/Modules/Blur2dImage/CMakeLists.txt b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/CMakeLists.txt new file mode 100644 index 0000000000..4d125c250f --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/CMakeLists.txt @@ -0,0 +1,2 @@ + +ctkFunctionCreateCmdLineModule(Blur2dImage) diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.cpp b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.cpp similarity index 87% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.cpp rename to Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.cpp index 422f0d73e6..90b9181c53 100644 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.cpp +++ b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -32,7 +33,7 @@ int main(int argc, char* argv[]) QCoreApplication app(argc, argv); // This is used by QSettings QCoreApplication::setOrganizationName("CommonTK"); - QCoreApplication::setApplicationName("CLIModuleBlur2dImage"); + QCoreApplication::setApplicationName("CmdLineModuleBlur2dImage"); ctkCommandLineParser parser; // Use Unix-style argument names @@ -50,8 +51,7 @@ int main(int argc, char* argv[]) QHash parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok); if (!ok) { - err << "Error parsing arguments: " - << parser.errorString() << "\n"; + err << "Error parsing arguments: " << parser.errorString() << "\n"; return EXIT_FAILURE; } @@ -64,7 +64,7 @@ int main(int argc, char* argv[]) if (parsedArgs.contains("xml")) { - QFile xmlDescription(":/ctkCLIModuleBlur2dImage.xml"); + QFile xmlDescription(":/ctkCmdLineModuleBlur2dImage.xml"); xmlDescription.open(QIODevice::ReadOnly); out << xmlDescription.readAll(); return EXIT_SUCCESS; @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) // Do something - //out << "Got parameter: " << QCoreApplication::arguments(); + qDebug() << "Got parameter: " << QCoreApplication::arguments(); return EXIT_SUCCESS; } diff --git a/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.qrc b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.qrc new file mode 100644 index 0000000000..7cb13800ae --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.qrc @@ -0,0 +1,5 @@ + + + ctkCmdLineModuleBlur2dImage.xml + + diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.xml b/Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.xml similarity index 100% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Blur2dImage/ctkCLIModuleBlur2dImage.xml rename to Libs/CommandLineModules/Testing/Modules/Blur2dImage/ctkCmdLineModuleBlur2dImage.xml diff --git a/Libs/CommandLineModules/Testing/Modules/CMakeLists.txt b/Libs/CommandLineModules/Testing/Modules/CMakeLists.txt new file mode 100644 index 0000000000..3167cc6493 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/CMakeLists.txt @@ -0,0 +1,25 @@ + +# This is very simple and for test purposes +# only. Relies on naming conventions and has +# no extensive error checking yet. +function(ctkFunctionCreateCmdLineModule name) + set(_src_files ${ARGN}) + list(APPEND _src_files ctkCmdLineModule${name}.cpp) + qt4_add_resources(_src_files ctkCmdLineModule${name}.qrc) + + add_executable(ctkCmdLineModule${name} ${_src_files}) + target_link_libraries(ctkCmdLineModule${name} CTKCore ${QT_LIBRARIES}) + add_dependencies(ctkCmdLineTestModules ctkCmdLineModule${name}) +endfunction() + +set(_cmdline_modules + Blur2dImage + TestBed + Tour +) + +add_custom_target(ctkCmdLineTestModules) + +foreach(_cmdline_module ${_cmdline_modules}) + add_subdirectory(${_cmdline_module}) +endforeach() diff --git a/Libs/CommandLineModules/Testing/Modules/TestBed/CMakeLists.txt b/Libs/CommandLineModules/Testing/Modules/TestBed/CMakeLists.txt new file mode 100644 index 0000000000..1d619eaa59 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/TestBed/CMakeLists.txt @@ -0,0 +1,2 @@ + +ctkFunctionCreateCmdLineModule(TestBed) diff --git a/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.cpp b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.cpp new file mode 100644 index 0000000000..875949307e --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.cpp @@ -0,0 +1,211 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +void sleep_ms(int ms) +{ +#ifdef Q_OS_WIN + Sleep(ms); +#else + struct timespec nanostep; + nanostep.tv_sec = static_cast(ms / 1000); + nanostep.tv_nsec = ((ms % 1000) * 1000.0 * 1000.0); + nanosleep(&nanostep, NULL); +#endif +} + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + // This is used by QSettings + QCoreApplication::setOrganizationName("CommonTK"); + QCoreApplication::setApplicationName("CmdLineModuleTestBed"); + + ctkCommandLineParser parser; + // Use Unix-style argument names + parser.setArgumentPrefix("--", "-"); + + // Add command line argument names + parser.addArgument("help", "h", QVariant::Bool, "Show this help text"); + parser.addArgument("xml", "", QVariant::Bool, "Print a XML description of this modules command line interface"); + parser.addArgument("runtime", "", QVariant::Int, "Runtime in seconds", 1); + parser.addArgument("numOutputs", "", QVariant::Int, "Number of outpusts", 0); + parser.addArgument("exitCode", "", QVariant::Int, "Exit code", 0); + parser.addArgument("exitCrash", "", QVariant::Bool, "Force crash", false); + parser.addArgument("exitTime", "", QVariant::Int, "Exit time", 0); + parser.addArgument("errorText", "", QVariant::String, "Error text printed at the end"); + + QTextStream out(stdout, QIODevice::WriteOnly | QIODevice::Text); + QTextStream err(stderr, QIODevice::WriteOnly | QIODevice::Text); + + // Parse the command line arguments + bool ok = false; + QHash parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok); + if (!ok) + { + err << "Error parsing arguments:" << parser.errorString() << endl; + return EXIT_FAILURE; + } + + // Show a help message + if (parsedArgs.contains("help") || parsedArgs.contains("h")) + { + out << parser.helpText(); + out.setFieldWidth(parser.fieldWidth()); + out.setFieldAlignment(QTextStream::AlignLeft); + out << " " << "Path to the output image" << endl; + return EXIT_SUCCESS; + } + + if (parsedArgs.contains("xml")) + { + QFile xmlDescription(":/ctkCmdLineModuleTestBed.xml"); + xmlDescription.open(QIODevice::ReadOnly); + out << xmlDescription.readAll(); + return EXIT_SUCCESS; + } + + if (parser.unparsedArguments().isEmpty()) + { + err << "Error parsing arguments: argument missing" << endl; + return EXIT_FAILURE; + } + + // Do something + + float runtime = parsedArgs["runtime"].toFloat(); + int numOutputs = parsedArgs["numOutputs"].toInt(); + float exitTime = parsedArgs["exitTime"].toFloat(); + int exitTimeMillis = static_cast(exitTime/2.0 * 1000.0); + int exitCode = parsedArgs["exitCode"].toInt(); + bool exitCrash = parsedArgs["exitCrash"].toBool(); + QString errorText = parsedArgs["errorText"].toString(); + + QString imageOutput = parser.unparsedArguments().at(0); + + err << "A superficial error message." << endl; + + // sleep 500ms to give the "errorReady" signal a chance + sleep_ms(500); + + QStringList outputs; + for (int i = 0; i < numOutputs; ++i) + { + outputs << "Output " + QString::number(i+1); + } + + float stepTime = outputs.size() ? runtime / static_cast(outputs.size()) : runtime; + + QTime time; + time.start(); + + out << "\n"; + out << "Test Filter\n"; + out << "Does nothing useful\n"; + out << "" << endl; + + if (outputs.empty()) + { + outputs.push_back("dummy"); + } + + float progressStep = 1.0f / static_cast(outputs.size()); + for(int i = 0; i < outputs.size(); ++i) + { + QString output = outputs[i]; + + if (exitTimeMillis != 0 && exitTimeMillis < time.elapsed()) + { + if (exitCrash) + { + int* crash = 0; + *crash = 5; + } + if (exitCode != 0 && !errorText.isEmpty()) + { + err << errorText; + } + return exitCode; + } + + // simulate some work + sleep_ms(stepTime*1000); + + // print the first output + if (output != "dummy") + { + out << output << endl; + + // report progress + out << QString("").arg((i+1)*progressStep) + << "Calculating output " << (i+2) << "..." << endl; + // report the current output number as a result + out << "" << (i+1) << "" << endl; + } + } + + // sleep 500ms to avoid squashing the last progress event with the finished event + sleep_ms(500); + + if (!errorText.isEmpty()) + { + err << errorText; + out << "" << errorText << "" << endl; + } + else + { + out << "" << imageOutput << "" << endl; + } + + out << ""; + if (exitCrash) + { + out << "Crashed" << endl; + int* crash = 0; + *crash = 5; + } + else + { + out << "Normal exit" << endl; + sleep_ms(100); + out << "1" << endl; + sleep_ms(100); + out << "Finished successfully." << endl; + sleep_ms(100); + } + + return exitCode; +} diff --git a/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.qrc b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.qrc new file mode 100644 index 0000000000..2203813d6b --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.qrc @@ -0,0 +1,5 @@ + + + ctkCmdLineModuleTestBed.xml + + diff --git a/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.xml b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.xml new file mode 100644 index 0000000000..2d9b421bab --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/TestBed/ctkCmdLineModuleTestBed.xml @@ -0,0 +1,102 @@ + + + Testing + Test Bed + +Configurable behaviour for testing purposes. + + 1.0 + + + Sascha Zelzer + + + + Configures the runtime behaviour of this module. + + runtimeVar + runtime + An integer with constraints + + 1 + + 0 + 60 + 1 + + + + numOutputsVar + numOutputs + Number of outputs which will be reported as the progress text via a QFutureWatcher. + + 0 + + + exitTimeVar + exitTime + The exit time of the module (premature finish). + + 0 + + + exitCodeVar + exitCode + The exit code of the module. + + 0 + + + exitCrashVar + exitCrash + Exit by crashing. + + false + + + errorTextVar + errorText + Final error message at the end. + + + + + + + Output parameters for testing purposes. + + resultNumberOutput + 1000 + The number of results reported by this module. + + 0 + output + + + errorMsgOutput + 1000 + Exit error message. + + output + + + exitStatusOutput + 1000 + Exit status (crashed or normal exit) + + output + Normal exit + Crashed + + + imageOutput + 0 + Image output path. + + /tmp/out.nrrd + output + + + + + diff --git a/Libs/CommandLineModules/Testing/Modules/Tour/CMakeLists.txt b/Libs/CommandLineModules/Testing/Modules/Tour/CMakeLists.txt new file mode 100644 index 0000000000..018fe81f07 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/Tour/CMakeLists.txt @@ -0,0 +1,2 @@ + +ctkFunctionCreateCmdLineModule(Tour) diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.cpp b/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.cpp similarity index 95% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.cpp rename to Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.cpp index 50ca4221db..29434ae854 100644 --- a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.cpp +++ b/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.cpp @@ -34,8 +34,8 @@ int main(int argc, char* argv[]) // QPixmap (used below) requires QApplication (instead of QCoreApplication) QApplication app(argc, argv); // This is used by QSettings - QApplication::setOrganizationName("CommonTK"); - QApplication::setApplicationName("CLIModuleTour"); + QCoreApplication::setOrganizationName("CommonTK"); + QCoreApplication::setApplicationName("CmdLineModuleTour"); ctkCommandLineParser parser; // Use Unix-style argument names @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) if (parsedArgs.contains("xml")) { - QFile xmlDescription(":/ctkCLIModuleTour.xml"); + QFile xmlDescription(":/ctkCmdLineModuleTour.xml"); xmlDescription.open(QIODevice::ReadOnly); QTextStream(stdout, QIODevice::WriteOnly) << xmlDescription.readAll(); } diff --git a/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.qrc b/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.qrc new file mode 100644 index 0000000000..8c10521d47 --- /dev/null +++ b/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.qrc @@ -0,0 +1,5 @@ + + + ctkCmdLineModuleTour.xml + + diff --git a/Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.xml b/Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.xml similarity index 100% rename from Libs/CommandLineModules/Core/Testing/CLIModules/Tour/ctkCLIModuleTour.xml rename to Libs/CommandLineModules/Testing/Modules/Tour/ctkCmdLineModuleTour.xml diff --git a/Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp b/Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp index b2b0e63163..d4173509a0 100644 --- a/Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp +++ b/Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp @@ -597,7 +597,9 @@ int ctkCommandLineParserTest1(int, char*[]) // Check that QSettings worked if(settings.status() != QSettings::NoError) { - qCritical() << "QSettings tests setup - QSettings::status() returned " << settings.status() << "."; + qCritical() << "QSettings tests setup - QSettings::status() returned " + << static_cast(settings.status()) << "."; + return EXIT_FAILURE; return EXIT_FAILURE; } diff --git a/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.cpp b/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.cpp index 8d8e354885..c9f321d4c8 100644 --- a/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.cpp +++ b/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.cpp @@ -50,6 +50,11 @@ ctkSingletonTestHelper::ctkSingletonTestHelper() : d_ptr(new ctkSingletonTestHel { } +//----------------------------------------------------------------------------- +ctkSingletonTestHelper::~ctkSingletonTestHelper() +{ +} + //----------------------------------------------------------------------------- ctkSingletonTestHelper* ctkSingletonTestHelper::instance() { diff --git a/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.h b/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.h index 601d305aa8..b9fe9b34df 100644 --- a/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.h +++ b/Libs/Core/Testing/Cpp/ctkSingletonTestHelper.h @@ -33,8 +33,6 @@ class ctkSingletonTestHelperPrivate; class ctkSingletonTestHelper { public: - ctkSingletonTestHelper(); - static ctkSingletonTestHelper* instance(); // Add singleton method here ... @@ -42,6 +40,9 @@ class ctkSingletonTestHelper int northFaceCount()const; protected: + ctkSingletonTestHelper(); + virtual ~ctkSingletonTestHelper(); + CTK_SINGLETON_DECLARE(ctkSingletonTestHelper); QScopedPointer d_ptr; diff --git a/Libs/Core/ctkAbstractFactory.h b/Libs/Core/ctkAbstractFactory.h index f3d0ba647e..f7a1c5670f 100644 --- a/Libs/Core/ctkAbstractFactory.h +++ b/Libs/Core/ctkAbstractFactory.h @@ -44,6 +44,7 @@ class ctkAbstractFactoryItem public: //explicit ctkAbstractFactoryItem(); ctkAbstractFactoryItem(); + virtual ~ctkAbstractFactoryItem(); virtual bool load() = 0; diff --git a/Libs/Core/ctkAbstractFactory.tpp b/Libs/Core/ctkAbstractFactory.tpp index c4552a92a1..56f01c1b7e 100644 --- a/Libs/Core/ctkAbstractFactory.tpp +++ b/Libs/Core/ctkAbstractFactory.tpp @@ -38,6 +38,12 @@ ctkAbstractFactoryItem::ctkAbstractFactoryItem() this->Verbose = false; } +//---------------------------------------------------------------------------- +template +ctkAbstractFactoryItem::~ctkAbstractFactoryItem() +{ +} + //---------------------------------------------------------------------------- template QStringList ctkAbstractFactoryItem::instantiateErrorStrings()const diff --git a/Libs/Core/ctkAbstractLibraryFactory.h b/Libs/Core/ctkAbstractLibraryFactory.h index f8e6c617c6..b09a712882 100644 --- a/Libs/Core/ctkAbstractLibraryFactory.h +++ b/Libs/Core/ctkAbstractLibraryFactory.h @@ -50,7 +50,7 @@ class ctkFactoryLibraryItem : public ctkAbstractFactoryFileBasedItem /// \brief Register an object in the factory /// The parameter \a key passed by reference will be updated with the - /// associated object name obtained using ::objectNameToKey() + /// associated object name obtained using objectNameToKey(const QString&) template bool registerQObject(QString& key); diff --git a/Libs/Core/ctkBackTrace.h b/Libs/Core/ctkBackTrace.h index bc79b492e7..1239e23245 100644 --- a/Libs/Core/ctkBackTrace.h +++ b/Libs/Core/ctkBackTrace.h @@ -73,7 +73,7 @@ class CTK_CORE_EXPORT ctkBackTrace /** * @brief Get a textual representation for a given stack frame. * @param frameNumber The stack frame number. - * @return A string describing the stack frame with number frameNumber + * @return A string describing the stack frame with number frameNumber * or a null QString if there is no corresponding stack frame. */ QString stackFrame(unsigned frameNumber) const; diff --git a/Libs/Core/ctkException.cpp b/Libs/Core/ctkException.cpp index 30dfb91ea7..4b4a12749e 100644 --- a/Libs/Core/ctkException.cpp +++ b/Libs/Core/ctkException.cpp @@ -105,14 +105,16 @@ const char* ctkException::className() const throw() // -------------------------------------------------------------------------- const char* ctkException::what() const throw() { - static std::string txt; - txt = std::string(name()); - if (!Msg.isEmpty()) + if (WhatMsg.empty()) { - txt += ": "; - txt += Msg.toStdString(); + WhatMsg = std::string(name()); + if (!Msg.isEmpty()) + { + WhatMsg += ": "; + WhatMsg += Msg.toStdString(); + } } - return txt.c_str(); + return WhatMsg.c_str(); } // -------------------------------------------------------------------------- diff --git a/Libs/Core/ctkException.h b/Libs/Core/ctkException.h index 0838bf9b01..8eb49a60fd 100644 --- a/Libs/Core/ctkException.h +++ b/Libs/Core/ctkException.h @@ -163,6 +163,7 @@ class CTK_CORE_EXPORT ctkException : public std::exception, public ctkBackTrace private: QString Msg; + mutable std::string WhatMsg; ctkException* NestedException; void printEnclosedStackTrace(QDebug dbg, const QList& enclosingTrace, diff --git a/Libs/Core/ctkTransferFunctionRepresentation.cpp b/Libs/Core/ctkTransferFunctionRepresentation.cpp index 2fee8b98bb..45f78b6269 100644 --- a/Libs/Core/ctkTransferFunctionRepresentation.cpp +++ b/Libs/Core/ctkTransferFunctionRepresentation.cpp @@ -248,7 +248,6 @@ void ctkTransferFunctionRepresentation::computeCurve() else //dynamic_cast(startCP)) { QList points = this->bezierParams(startCP, nextCP); - QList::iterator it = points.begin(); QList bezierPoints; foreach(const ctkPoint& p, points) { @@ -331,7 +330,6 @@ void ctkTransferFunctionRepresentation::computeGradient() else //dynamic_cast(startCP)) { // TODO handle bezier points with color QList points = this->bezierParams(startCP, nextCP); - QList::iterator it = points.begin(); QList bezierPoints; foreach(const ctkPoint& p, points) { diff --git a/Libs/DICOM/Core/CMakeLists.txt b/Libs/DICOM/Core/CMakeLists.txt index bceaa3a9d9..cfd5c92d48 100644 --- a/Libs/DICOM/Core/CMakeLists.txt +++ b/Libs/DICOM/Core/CMakeLists.txt @@ -19,7 +19,7 @@ set(KIT_SRCS ctkDICOMFilterProxyModel.h ctkDICOMIndexer.cpp ctkDICOMIndexer.h - ctkDICOMIndexerPrivate.h + ctkDICOMIndexer_p.h ctkDICOMModel.cpp ctkDICOMModel.h ctkDICOMPersonName.cpp @@ -43,7 +43,7 @@ set(KIT_MOC_SRCS ctkDICOMAbstractThumbnailGenerator.h ctkDICOMDatabase.h ctkDICOMIndexer.h - ctkDICOMIndexerPrivate.h + ctkDICOMIndexer_p.h ctkDICOMFilterProxyModel.h ctkDICOMModel.h ctkDICOMQuery.h diff --git a/Libs/DICOM/Core/Resources/dicom-sample-unversioned-schema.sql b/Libs/DICOM/Core/Resources/dicom-sample-unversioned-schema.sql new file mode 100644 index 0000000000..31190d7d13 --- /dev/null +++ b/Libs/DICOM/Core/Resources/dicom-sample-unversioned-schema.sql @@ -0,0 +1,2276 @@ +-- +-- initialize a small sample database from an empty database. +-- For the corresponding DICOM files and more information see +-- http://www.slicer.org/slicerWiki/index.php/DICOM:Database +-- +-- Note: the semicolon at the end is necessary for the simple parser to separate +-- the statements since the SQlite driver does not handle multiple +-- commands per QSqlQuery::exec call! +-- ; + +BEGIN TRANSACTION; +DROP TABLE IF EXISTS 'Images' ; +DROP TABLE IF EXISTS 'Patients' ; +DROP TABLE IF EXISTS 'Series' ; +DROP TABLE IF EXISTS 'Studies' ; +DROP TABLE IF EXISTS 'Directories' ; +CREATE TABLE 'Images' ( 'Filename' VARCHAR(1024) NOT NULL , 'SeriesInstanceUID' VARCHAR(255) NOT NULL , PRIMARY KEY ('Filename') ); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead24.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead8.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead51.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead87.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead4.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead92.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead79.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead56.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead62.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead16.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead53.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead89.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead17.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead35.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead93.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead85.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead39.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead90.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead65.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead9.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead36.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead76.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead78.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead73.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead14.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead75.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead37.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead86.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead12.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead55.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead31.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead28.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead84.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead34.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead26.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead54.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead30.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead67.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead50.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead22.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead6.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead32.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead68.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead38.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead61.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead43.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead40.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead45.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead21.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead29.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead3.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead15.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead66.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead48.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead23.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead47.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead81.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead72.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead44.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead70.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead82.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead59.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead88.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead1.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead10.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead58.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead46.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead33.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead7.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead42.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead27.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead49.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead13.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead41.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead71.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead77.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead25.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead64.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead52.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead11.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead2.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead57.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead69.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead60.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead20.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead74.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead5.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead80.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead19.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead91.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead83.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead18.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CTHeadAxialDicom/CTHead63.dcm','1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118383','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119059','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117047','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118425','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116697','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119927','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119535','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119437','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117543','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105475','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117921','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116179','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118509','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119913','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117389','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118103','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117893','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105783','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105147','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105503','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119871','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116151','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119269','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118755','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117585','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116879','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117865','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115899','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115913','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105797','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105909','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118905','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116599','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119521','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118467','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105161','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115319','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118710','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105531','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116011','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119815','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117215','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105307','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117501','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116235','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116767','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116851','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116865','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115885','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116081','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117711','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118439','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116039','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115941','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119731','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116669','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105377','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116515','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118565','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117837','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105559','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118313','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116109','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115459','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117291','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119703','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118854','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105063','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116305','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116137','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118299','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117089','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115431','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118075','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116025','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118215','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115655','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119675','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119633','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119003','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118701','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105881','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118481','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118495','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115795','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118649','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105021','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116781','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118145','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118131','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116263','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105741','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119199','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118061','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119605','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105895','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115955','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115529','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117683','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115473','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116487','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116991','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118369','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117375','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105489','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117347','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117277','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117851','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117627','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118947','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119283','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116655','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105433','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116165','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118728','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119941','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115641','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116403','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118397','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117145','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119689','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118355','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118047','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117459','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117655','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118607','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115403','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118271','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118635','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105251','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119787','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116711','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115697','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119241','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118791','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105447','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116375','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115809','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115487','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118523','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105951','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117879','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117403','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119983','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119465','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119395','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115389','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119899','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116123','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105671','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116795','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116221','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105811','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119843','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115969','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115669','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116837','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119073','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119101','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105979','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117187','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119143','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105713','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117557','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115263','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117319','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115501','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119381','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119661','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117949','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115997','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118809','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116557','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117669','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115683','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118257','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117977','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117739','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116949','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119647','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115871','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118327','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118692','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116347','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116753','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117529','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119717','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105293','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116585','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118719','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117571','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119451','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105657','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117305','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119969','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117725','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105349','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116935','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115781','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118746','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119213','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118663','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105119','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117641','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105035','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117697','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118800','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115375','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118818','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105839','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115739','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117159','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117201','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117613','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117417','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105517','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117963','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118285','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105049','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119773','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119297','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119563','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116921','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116725','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116501','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119857','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119577','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105755','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118593','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115613','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116473','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118579','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105685','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116445','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117361','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105867','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105175','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116809','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115543','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115857','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116823','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118229','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105105','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117033','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117991','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105923','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117599','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115753','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116683','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117935','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119423','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118863','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116291','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119493','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105405','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118537','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118453','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116319','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116207','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105265','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105091','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119227','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115823','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116641','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116977','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116627','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118827','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118961','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105601','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105335','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119157','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116361','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118005','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115711','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118933','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119017','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115445','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118877','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119829','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118919','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118989','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117795','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119955','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119409','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105545','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116249','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118019','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115571','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115767','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117243','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119325','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115291','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115725','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116907','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105461','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115585','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117473','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105279','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105363','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105993','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117173','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116529','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105209','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118891','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115333','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105133','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116739','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105321','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116333','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105965','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116067','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105615','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105643','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117767','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117333','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105573','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117753','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119549','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119353','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105699','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17106007','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119185','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105769','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118411','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119507','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117131','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117781','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119367','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118845','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119885','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115599','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116431','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118621','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118089','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117823','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117061','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118243','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116277','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118551','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119087','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117445','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119479','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118737','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116389','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119311','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116963','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116543','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105937','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119801','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118773','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117229','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119339','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117487','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105077','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115361','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118159','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115927','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119619','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116193','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119171','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117019','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118117','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118201','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119591','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118187','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118173','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118764','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117515','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116053','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117075','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116571','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115627','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117005','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116417','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116613','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119031','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118341','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119255','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119129','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105391','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105419','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119745','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117103','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115983','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115347','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119045','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115277','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117809','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118975','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119759','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17106021','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105727','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105629','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118836','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115557','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115515','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117907','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116459','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117431','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105587','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105223','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105237','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17117117','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115417','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118782','1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105853','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116095','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17105825','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17115305','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17118033','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17116893','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD2 (Dyna CT)/DICOM/09112417/40300000/17119115','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594908','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592174','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589989','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585730','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595969','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592302','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597907','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593923','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598435','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588016','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590613','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596804','1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597236','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592483','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591758','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595020','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584322','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594940','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594668','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589349','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586343','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587552','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598883','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591870','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587079','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600547','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586887','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584242','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586167','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600499','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588000','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592350','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600083','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592771','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597539','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595921','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587968','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588480','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588400','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590229','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599207','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585250','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591982','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589301','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594092','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594124','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598099','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598563','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584530','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594620','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584466','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598947','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589733','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587920','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598355','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588176','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593987','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590117','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592286','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595404','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587904','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589381','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593235','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592675','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587175','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586775','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590133','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588752','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600451','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31582235','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589573','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587383','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595436','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594572','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596273','1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597763','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590437','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591374','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599779','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596996','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597156','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597092','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589008','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590389','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587191','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586631','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598691','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590309','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587632','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591406','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589493','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590798','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587047','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588192','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593651','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594892','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594524','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587255','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597859','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600099','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592451','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586071','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599587','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600643','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588672','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596385','1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598147','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600659','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589461','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590910','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598723','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590373','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587568','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589621','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585314','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590501','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584786','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593203','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594972','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593555','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593411','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597140','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595308','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600595','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596649','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591966','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600467','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586711','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597012','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584722','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589072','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595953','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591230','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588912','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588256','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586967','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592803','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593107','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591358','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594748','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596740','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593347','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586951','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593011','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589024','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596701','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587680','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599795','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599194','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596714','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597204','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594268','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588784','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593395','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592110','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599475','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587143','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598387','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599875','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592963','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593795','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598195','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588368','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594716','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593315','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595548','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596241','1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585698','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31582251','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595937','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589333','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598307','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595244','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593059','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593875','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595036','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587063','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598931','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588352','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588112','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591518','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590709','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587712','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597619','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592531','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590597','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593971','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591006','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589525','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587159','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599324','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591678','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592547','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595100','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586535','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600579','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584626','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594380','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599907','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599411','1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595340','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588448','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588960','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600403','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586199','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600707','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586647','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586663','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595484','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600371','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588416','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595212','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586423','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597108','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591182','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598803','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590565','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598163','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589253','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595761','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590677','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584834','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594604','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598979','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593587','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597523','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592467','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593603','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596820','1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596257','1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593859','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593939','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592094','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598483','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585959','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599427','1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597891','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588544','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596662','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599747','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591134','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584594','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587399','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591566','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591326','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595905','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585170','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587872','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598403','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587936','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595228','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592611','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592062','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593043','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587536','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597747','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586743','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585682','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587271','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596193','1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588160','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599827','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596209','1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586455','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598531','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599683','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585538','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588240','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597715','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584226','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587776','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591710','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588992','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599523','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590878','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591726','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598787','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593331','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585218','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598067','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588592','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587504','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591934','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593763','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599955','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590629','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591070','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591838','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594156','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592883','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586007','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588800','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590293','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584386','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599011','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586375','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590261','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589056','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589829','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596597','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589125','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589845','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588864','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588816','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594332','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588048','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597507','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591550','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588096','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588880','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595388','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600115','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586359','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585634','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585090','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591486','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592435','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598419','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587223','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584562','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585490','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592739','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587584','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588080','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591294','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591166','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590325','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596623','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587888','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592851','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593779','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600723','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592979','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595596','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597987','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585714','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597124','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589269','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595889','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598323','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587616','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585618','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593891','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598707','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591038','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588144','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598291','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586871','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594988','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588208','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598227','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589605','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594204','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598371','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595068','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586183','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598995','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596129','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586311','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595084','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597044','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589413','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585943','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593027','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587696','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600211','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592915','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599259','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584658','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586983','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591694','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589957','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585410','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585442','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594924','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588432','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585927','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599539','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584818','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586327','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587207','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593219','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598579','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591886','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587792','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596097','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594956','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592691','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585074','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592899','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597252','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590197','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589941','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594172','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593139','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595532','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597939','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596675','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590942','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584306','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586055','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600179','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596417','1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591422','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598547','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591278','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594284','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598627','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590862','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585746','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595500','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595260','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592254','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591118','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599987','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585234','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597635','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593491','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600307','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599043','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593667','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594108','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586791','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593187','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597923','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594812','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600163','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599027','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595052','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590245','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589749','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600323','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591614','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590517','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586503','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584850','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586487','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584418','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596636','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600419','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592787','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586679','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586823','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591742','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585586','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597683','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598035','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590782','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591086','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597172','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585847','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599220','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584754','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585186','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590149','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590469','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600227','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589701','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596980','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591662','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595873','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599971','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585831','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591918','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587031','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596852','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589205','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596481','1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585602','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592014','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585554','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588688','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588304','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584610','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591806','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590830','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597651','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599285','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587015','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595164','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588560','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597188','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584210','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592206','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597667','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598867','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586263','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588496','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585298','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596433','1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598739','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599350','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587239','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589893','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595713','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589397','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596900','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591102','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600387','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589221','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589557','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584578','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592238','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595292','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585042','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587856','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585522','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596932','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599075','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596353','1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596916','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598915','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586599','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592995','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591262','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600339','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600675','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597076','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592627','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599507','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586135','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595148','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584514','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593283','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587351','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590037','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599395','1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589765','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594348','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595132','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598243','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585863','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596497','1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599571','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585202','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592707','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593843','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594876','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600131','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586919','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594700','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585138','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584642','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594444','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598835','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584194','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595985','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594764','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593539','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591454','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584258','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594220','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593123','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587127','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592835','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598451','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594780','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592595','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595825','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597060','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589189','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590485','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591854','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598819','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590005','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595420','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593475','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600611','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591630','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592723','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585911','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586583','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597811','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590926','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598339','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599491','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590974','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590277','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588128','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596465','1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594252','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588944','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594476','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594140','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599298','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588928','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586039','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589445','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598515','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596401','1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592947','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593715','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599603','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585394','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596001','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585570','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588608','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591646','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589781','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595516','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593811','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600259','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593251','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588528','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599233','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598851','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596610','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594300','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584866','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589317','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598899','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597827','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599859','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599891','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585106','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592563','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591502','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588320','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591598','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593507','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587664','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593683','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585666','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596145','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594860','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591246','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597571','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594364','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597955','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588384','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595452','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589653','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588512','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594556','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599311','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584402','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595628','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590846','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586567','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584882','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591998','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595276','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593955','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588720','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599555','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589157','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597555','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589237','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584914','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596289','1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598115','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587984','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595468','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591950','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590085','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586935','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584770','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588288','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595564','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592366','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586279','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590069','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593635','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585895','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590181','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586615','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593459','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589541','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594844','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593267','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586407','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597971','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594412','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586727','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599619','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594828','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586231','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599923','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590421','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599443','1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590213','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594492','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588656','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591054','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588576','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595660','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586023','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594316','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596727','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587367','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593299','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592126','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591198','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595841','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598659','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593379','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584738','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589877','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592318','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587303','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596836','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591390','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585426','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588976','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599459','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586855','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587287','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595004','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589909','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584674','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591822','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587488','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590958','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591342','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596369','1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590357','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596964','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584930','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592419','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585506','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584290','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584898','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599843','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598003','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600243','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586391','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594732','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591150','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595777','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586807','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586759','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586087','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594540','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598675','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585010','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596321','1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595356','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596337','1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600483','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591470','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587335','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596065','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599246','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590405','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590165','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585026','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594636','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590549','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585282','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588896','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589173','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590021','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592867','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599715','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595180','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596081','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596571','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596584','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598755','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595324','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594076','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597699','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594188','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587095','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597220','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586151','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587648','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589589','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592190','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595809','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587808','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593827','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589925','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594508','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592499','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586903','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597587','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592046','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584978','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600003','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599272','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591438','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585458','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584994','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588768','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589797','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598051','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590661','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589141','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592931','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597779','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585650','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584482','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598179','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600563','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587111','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595612','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600627','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594396','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598643','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598131','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593363','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590053','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596449','1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587520','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596756','1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597795','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584690','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586551','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600291','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594003','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598499','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597603','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595196','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599811','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588736','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596113','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584706','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600019','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596017','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599337','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599699','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589685','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585122','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586439','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584450','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591310','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598611','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599363','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599091','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590894','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595793','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593443','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590101','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586519','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592755','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585378','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592078','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584434','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597731','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585362','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588464','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584546','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593747','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591022','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588224','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598275','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591582','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588624','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598467','1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584802','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592819','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596788','1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589040','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586103','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593171','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585346','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585474','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592222','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593091','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599379','1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599651','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598595','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591790','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589509','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585991','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596688','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589365','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600147','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595745','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592659','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600067','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586215','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592158','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596513','1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591774','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594684','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595729','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599181','1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594428','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591902','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600035','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591214','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587415','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589429','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599059','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590341','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589861','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598259','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598771','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585154','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593075','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596161','1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599667','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597843','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599635','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597028','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588064','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596868','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592030','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593571','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593907','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592270','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600691','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587840','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587824','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586695','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585058','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594236','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588832','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593619','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584370','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587600','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589717','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596948','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598019','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592515','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598963','1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600531','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592142','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594460','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590990','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594588','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592334','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588272','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600515','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586471','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596884','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587952','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598083','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584946','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585266','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596529','1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584354','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587760','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593731','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600195','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596305','1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600275','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590581','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593523','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596033','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600435','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584274','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584498','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587319','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31597875','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590645','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31591534','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600355','1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595116','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584962','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599763','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588336','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585975','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586247','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596558','1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588032','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589973','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586295','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589285','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595372','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594652','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590814','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588704','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595580','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592579','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596177','1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586839','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599939','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589669','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585879','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593155','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31592643','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595857','1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588640','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589477','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31598211','1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590533','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31599731','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596545','1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31595644','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589637','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586999','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31594796','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593427','1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31588848','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31585330','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31589813','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587728','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31600051','1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590453','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596225','1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596772','1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31584338','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31590693','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31596049','1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31593699','1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31586119','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390000/31587744','1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568347','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570842','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569445','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569393','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577590','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571076','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582539','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569003','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579462','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568477','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582897','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579332','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574305','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578890','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572997','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569055','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570738','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572373','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572191','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567385','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568815','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578760','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580939','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579202','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576099','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571362','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566631','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569549','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582795','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577467','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570868','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577668','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575631','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571232','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570530','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572711','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568737','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580627','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583563','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576973','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566709','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582963','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576359','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574643','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569939','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583589','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572815','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577077','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577441','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574539','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579124','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569237','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583095','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566527','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576768','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583199','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573907','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580835','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573829','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579020','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575553','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31584031','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567177','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573725','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575657','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569731','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571310','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582985','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575761','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574747','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574825','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578552','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582587','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547879','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583329','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574513','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581043','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580653','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570400','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576203','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575397','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572763','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580523','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571801','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574773','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568217','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580315','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547905','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568633','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569133','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569653','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567827','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566657','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569159','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583007','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578214','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571697','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583381','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567463','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570426','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571102','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569289','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577025','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547594','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570166','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570244','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577207','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575839','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575111','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577103','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575995','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580783','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569705','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583719','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571050','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574851','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566943','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581719','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581563','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573413','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581771','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574227','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582699','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575813','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580029','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570946','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569887','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582667','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582331','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583953','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569861','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31585762','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575787','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572347','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567775','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578136','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579540','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568035','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581511','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575267','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568087','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570296','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583771','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567931','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576151','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568243','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581069','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583225','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569029','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573985','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582031','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583407','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575033','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582941','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574565','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579743','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578968','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576456','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574617','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580445','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569263','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567645','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569497','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31584109','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575319','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581589','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581693','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574903','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576177','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581277','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577389','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578344','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578162','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547931','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570218','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576073','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575293','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582603','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573283','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580861','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583018','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568529','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568977','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575527','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568503','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567671','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576820','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579951','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567229','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575423','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583615','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547646','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570660','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578682','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566969','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568789','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567307','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581667','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582919','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579384','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568685','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568113','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573517','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574799','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567957','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572867','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578396','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580549','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576921','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575501','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576534','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582315','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568951','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569835','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578578','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567697','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577564','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570608','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574167','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566475','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577824','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573127','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572633','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578318','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581381','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571336','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578032','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583797','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572659','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575969','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569679','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566995','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583485','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571388','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576716','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570374','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567515','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576846','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578916','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577415','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567255','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579821','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569991','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582187','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580913','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570069','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580393','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582057','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573075','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573387','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575865','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575735','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583901','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572503','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582135','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583355','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578006','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575449','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578058','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567489','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575683','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574409','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569367','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583693','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570920','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578370','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573933','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571258','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572451','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580003','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577928','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579436','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576612','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582763','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569341','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575215','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579072','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581355','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578942','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581225','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547983','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582827','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582459','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567073','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580471','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577233','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578812','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573439','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567437','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577285','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572061','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579639','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572113','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570816','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577798','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576947','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582395','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573621','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571853','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582974','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572035','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547957','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582635','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579150','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574669','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571671','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571827','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573673','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578448','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577051','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579613','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577772','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580705','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582411','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583029','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567723','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582811','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570894','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583062','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579665','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573543','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567411','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573023','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572321','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580757','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575085','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581017','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575345','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566501','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575709','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579488','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579769','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547672','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568321','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583823','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582555','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582886','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582715','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572581','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547698','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574877','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581927','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580081','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583667','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567099','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568925','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580237','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574011','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573309','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577902','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582267','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574435','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578188','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571593','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575189','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31584083','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582779','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568295','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569523','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574695','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567619','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568191','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575891','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579795','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578838','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577954','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582083','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583641','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582651','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567749','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580055','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569471','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577616','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566865','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566891','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571645','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579691','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572945','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578084','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569107','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573699','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574461','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566449','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579925','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577538','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581537','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579514','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567203','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567879','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580809','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570270','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573101','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567593','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572139','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566735','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580601','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576385','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574487','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583040','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574981','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570556','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574253','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583147','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576229','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568555','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578474','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570504','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581823','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573335','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582299','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570140','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580419','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575475','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31585778','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582747','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571492','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579228','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581953','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571466','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581459','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568399','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573881','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576333','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567281','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571983','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580497','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579358','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570192','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583251','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567567','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571128','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573959','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547568','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567333','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581095','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572685','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583927','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567853','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569913','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574279','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576586','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576047','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568165','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572529','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572971','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575371','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581147','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580159','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571518','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571619','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570764','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579717','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573361','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578292','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581251','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583979','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583849','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583084','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571440','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569211','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567047','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573205','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575059','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582908','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574929','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568139','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577876','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573855','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578110','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577642','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573257','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570712','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582161','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571024','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577746','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575007','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582475','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569185','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581745','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575163','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575579','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566605','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583173','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578266','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583073','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570582','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580133','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576690','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572607','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569575','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582283','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568061','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580965','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580679','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582843','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582930','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566553','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581199','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569081','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583875','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567359','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577694','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547853','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569315','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576021','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567021','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574357','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567151','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571775','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566917','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583277','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574115','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577311','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579098','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582363','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568451','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576281','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571154','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547542','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576638','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582507','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578240','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578994','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573803','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571957','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579847','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580185','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568659','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577337','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578526','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568425','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572269','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576742','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568269','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574591','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566813','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573491','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568763','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547620','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574063','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573465','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577980','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576125','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580263','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580991','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568711','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576794','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573569','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566761','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576255','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566579','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574331','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581407','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570452','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582571','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580575','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583433','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576482','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582683','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581615','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579254','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568009','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570790','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583537','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575137','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581979','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568899','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572477','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568607','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574141','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582109','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572399','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570972','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572087','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579306','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578708','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575241','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582005','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574089','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581485','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569809','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579280','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547769','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578422','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578630','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572243','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582731','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569783','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570686','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567801','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572555','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577850','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571414','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578604','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580211','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579410','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576664','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572919','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578500','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582491','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574955','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579046','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547516','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581303','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576307','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573153','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579873','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583303','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572217','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570998','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566839','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580887','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570017','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572789','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583511','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31547827','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571180','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571749','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572893','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567983','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579176','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581329','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577363','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579977','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581173','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571284','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574383','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570043','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575605','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577259','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567905','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577129','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569965','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582875','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571931','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581875','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573647','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574037','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568373','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581641','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575917','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576999','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570634','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582859','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580367','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581901','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577155','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581121','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31574721','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570322','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580107','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582952','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568841','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582347','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31579899','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582427','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569757','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567125','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570348','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566683','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573751','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573231','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31575943','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583121','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569627','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571206','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569419','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576508','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572295','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573049','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578734','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572841','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581433','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572425','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581797','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31581849','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31566787','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573595','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31568581','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572737','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582379','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580341','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31584005','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578656','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582619','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583745','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582523','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31570478','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573777','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31567541','1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578864','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580731','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31569601','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572009','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31578786','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577720','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571905','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31572165','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31576560','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31584057','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583459','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582996','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31573179','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571879','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31577181','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31582443','1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31571723','1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31580289','1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080'); +INSERT INTO "Images" VALUES('CD1/DICOM/09112417/37390001/31583051','1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147'); +CREATE TABLE 'Patients' ( 'UID' INTEGER PRIMARY KEY AUTOINCREMENT, 'PatientsName' VARCHAR(255) NULL , 'PatientID' VARCHAR(255) NULL , 'PatientsBirthDate' DATE NULL , 'PatientsBirthTime' TIME NULL , 'PatientsSex' varchar(1) NULL , 'PatientsComments' VARCHAR(255) NULL, 'PatientsAge' varchar(10) NULL ); +INSERT INTO "Patients" VALUES(14,'Austrialian','8775070',20060101,10100,'M','A volunteer','35'); +INSERT INTO "Patients" VALUES(15,'09.11.24-14:36:24-STD','09.11.24-14:36:24-STD-4.0.4094797',18581118,'','O','','78'); +INSERT INTO "Patients" VALUES(16,'MROVERLAY-13','09.11.24-14:37:10-STD-1.3.12.2.1107.5.2.31.30287',19560304,'','O','','102'); +CREATE TABLE 'Series' ( 'SeriesInstanceUID' VARCHAR(255) NOT NULL , 'StudyInstanceUID' VARCHAR(45) NOT NULL , 'SeriesNumber' INT NULL , 'SeriesDate' DATE NULL , 'SeriesTime' VARCHAR(20) NULL , 'SeriesDescription' VARCHAR(255) NULL , 'BodyPartExamined' VARCHAR(255) NULL , 'FrameOfReferenceUID' VARCHAR(255) NULL , 'AcquisitionNumber' INT NULL , 'ContrastAgent' VARCHAR(255) NULL , 'ScanningSequence' VARCHAR(45) NULL , 'EchoNumber' INT NULL , 'TemporalPosition' INT NULL , PRIMARY KEY ('SeriesInstanceUID') ); +INSERT INTO "Series" VALUES('1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058','1.2.826.0.1.3680043.2.1125.1.73379483469717886505187028001198162',123456,'','','None','','',0,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173529.684000','DynaCT Nat Fill HU Normal [InSpace3D]','','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000394',1,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173453.699000','Images for VOI selection','','',1,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',17,'2009-11-24','160901.687000','t1_spc_WIP537_cor_body','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',18,'2009-11-24','161741.952000','t1_spc_WIP537_cor_surface','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',20,'2009-11-24','171701.952000','t1_spc_WIP537_cor_surface_PreScanNormalize_Needle','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',9,'2009-11-24','151607.280000','t1_se_ax_OFF_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',5,'2009-11-24','150154.798000','t2_spc_ns_sag_p2_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',6,'2009-11-24','150756.655000','t1_se_ax_OFF_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',19,'2009-11-24','163552.202000','t1_spc_WIP537_cor_surface_PreScanNormalize','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',16,'2009-11-24','154932.124000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',7,'2009-11-24','151226.577000','VIBE_1x1x1_body','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',4,'2009-11-24','145754.627000','VIBE_1x1x1_body','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',3,'2009-11-24','145630.752000','VIBE_1x1x1','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',8,'2009-11-24','151416.108000','t1_se_ax_OFF_iso_body','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',1,'2009-11-24','144410.799000','SCOUT','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',11,'2009-11-24','152020.233000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',12,'2009-11-24','152211.187000','t1_se_ax_OFF_2mm','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',15,'2009-11-24','154334.780000','SCOUT','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',2,'2009-11-24','144806.174000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',10,'2009-11-24','151758.015000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',13,'2009-11-24','152401.983000','t1_se_ax_OFF_2mm_body','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',14,'2009-11-24','152554.999000','t1_se_ax_OFF_2mm','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',103,'2009-11-24','171811.576000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',102,'2009-11-24','163724.171000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',101,'2009-11-24','162255.999000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',99,'2009-11-24','170811.702000','PhoenixZIPReport','','',0,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',100,'2009-11-24','162146.108000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +CREATE TABLE 'Studies' ( 'StudyInstanceUID' VARCHAR(255) NOT NULL , 'PatientsUID' INT NOT NULL , 'StudyID' VARCHAR(255) NULL , 'StudyDate' DATE NULL , 'StudyTime' VARCHAR(20) NULL , 'AccessionNumber' VARCHAR(255) NULL , 'ModalitiesInStudy' VARCHAR(255) NULL , 'InstitutionName' VARCHAR(255) NULL , 'ReferringPhysician' VARCHAR(255) NULL , 'PerformingPhysiciansName' VARCHAR(255) NULL , 'StudyDescription' VARCHAR(255) NULL , PRIMARY KEY ('StudyInstanceUID') ); +INSERT INTO "Studies" VALUES('1.2.826.0.1.3680043.2.1125.1.73379483469717886505187028001198162',14,'123456','2009-01-02','010100.000000','1','','Hospital YYY', 'Unknown','me','None'); +INSERT INTO "Studies" VALUES('1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',15,'Default StudyID','2009-11-24','143624.000000','','','Hospital XXX', '','you',''); +INSERT INTO "Studies" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',16,'2','2009-11-24','143836.908000','','','Hospital XXX', 'IORDACHITA','her','Carrino^MR_Overlay'); +DELETE FROM sqlite_sequence; +INSERT INTO "sqlite_sequence" VALUES('Patients',16); + +CREATE TABLE 'Directories' ( + 'Dirname' VARCHAR(1024) , + PRIMARY KEY ('Dirname') ); + +COMMIT; diff --git a/Libs/DICOM/Core/Resources/dicom-sample.sql b/Libs/DICOM/Core/Resources/dicom-sample.sql index 31190d7d13..ed3140ab9e 100644 --- a/Libs/DICOM/Core/Resources/dicom-sample.sql +++ b/Libs/DICOM/Core/Resources/dicom-sample.sql @@ -2233,35 +2233,35 @@ CREATE TABLE 'Patients' ( 'UID' INTEGER PRIMARY KEY AUTOINCREMENT, 'Patients INSERT INTO "Patients" VALUES(14,'Austrialian','8775070',20060101,10100,'M','A volunteer','35'); INSERT INTO "Patients" VALUES(15,'09.11.24-14:36:24-STD','09.11.24-14:36:24-STD-4.0.4094797',18581118,'','O','','78'); INSERT INTO "Patients" VALUES(16,'MROVERLAY-13','09.11.24-14:37:10-STD-1.3.12.2.1107.5.2.31.30287',19560304,'','O','','102'); -CREATE TABLE 'Series' ( 'SeriesInstanceUID' VARCHAR(255) NOT NULL , 'StudyInstanceUID' VARCHAR(45) NOT NULL , 'SeriesNumber' INT NULL , 'SeriesDate' DATE NULL , 'SeriesTime' VARCHAR(20) NULL , 'SeriesDescription' VARCHAR(255) NULL , 'BodyPartExamined' VARCHAR(255) NULL , 'FrameOfReferenceUID' VARCHAR(255) NULL , 'AcquisitionNumber' INT NULL , 'ContrastAgent' VARCHAR(255) NULL , 'ScanningSequence' VARCHAR(45) NULL , 'EchoNumber' INT NULL , 'TemporalPosition' INT NULL , PRIMARY KEY ('SeriesInstanceUID') ); -INSERT INTO "Series" VALUES('1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058','1.2.826.0.1.3680043.2.1125.1.73379483469717886505187028001198162',123456,'','','None','','',0,'','',0,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173529.684000','DynaCT Nat Fill HU Normal [InSpace3D]','','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000394',1,'','',0,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173453.699000','Images for VOI selection','','',1,'','',0,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',17,'2009-11-24','160901.687000','t1_spc_WIP537_cor_body','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',18,'2009-11-24','161741.952000','t1_spc_WIP537_cor_surface','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',20,'2009-11-24','171701.952000','t1_spc_WIP537_cor_surface_PreScanNormalize_Needle','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',9,'2009-11-24','151607.280000','t1_se_ax_OFF_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',5,'2009-11-24','150154.798000','t2_spc_ns_sag_p2_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',6,'2009-11-24','150756.655000','t1_se_ax_OFF_iso','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',19,'2009-11-24','163552.202000','t1_spc_WIP537_cor_surface_PreScanNormalize','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',16,'2009-11-24','154932.124000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',7,'2009-11-24','151226.577000','VIBE_1x1x1_body','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',4,'2009-11-24','145754.627000','VIBE_1x1x1_body','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',3,'2009-11-24','145630.752000','VIBE_1x1x1','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',8,'2009-11-24','151416.108000','t1_se_ax_OFF_iso_body','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',1,'2009-11-24','144410.799000','SCOUT','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',11,'2009-11-24','152020.233000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',12,'2009-11-24','152211.187000','t1_se_ax_OFF_2mm','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',15,'2009-11-24','154334.780000','SCOUT','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','GR',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',2,'2009-11-24','144806.174000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',10,'2009-11-24','151758.015000','t1_se_ax_OFF','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',13,'2009-11-24','152401.983000','t1_se_ax_OFF_2mm_body','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',14,'2009-11-24','152554.999000','t1_se_ax_OFF_2mm','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',103,'2009-11-24','171811.576000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',102,'2009-11-24','163724.171000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',101,'2009-11-24','162255.999000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',99,'2009-11-24','170811.702000','PhoenixZIPReport','','',0,'','',0,0); -INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',100,'2009-11-24','162146.108000','','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +CREATE TABLE 'Series' ( 'SeriesInstanceUID' VARCHAR(255) NOT NULL , 'StudyInstanceUID' VARCHAR(45) NOT NULL , 'SeriesNumber' INT NULL , 'SeriesDate' DATE NULL , 'SeriesTime' VARCHAR(20) NULL , 'SeriesDescription' VARCHAR(255) NULL , 'Modality' VARCHAR(20) NULL , 'BodyPartExamined' VARCHAR(255) NULL , 'FrameOfReferenceUID' VARCHAR(255) NULL , 'AcquisitionNumber' INT NULL , 'ContrastAgent' VARCHAR(255) NULL , 'ScanningSequence' VARCHAR(45) NULL , 'EchoNumber' INT NULL , 'TemporalPosition' INT NULL , PRIMARY KEY ('SeriesInstanceUID') ); +INSERT INTO "Series" VALUES('1.2.826.0.1.3680043.2.1125.1.65375240934815452318141136507497058','1.2.826.0.1.3680043.2.1125.1.73379483469717886505187028001198162',123456,'','','None','','','',0,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420245387500000395','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173529.684000','DynaCT Nat Fill HU Normal [InSpace3D]','CT','','1.3.12.2.1107.5.4.7.6975.30000009112420245387500000394',1,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.4.7.6975.30000009112420241301500000022','1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',1,'2009-11-24','173453.699000','Images for VOI selection','CT','','',1,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416085385445425206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',17,'2009-11-24','160901.687000','t1_spc_WIP537_cor_body','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416173969227026001.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',18,'2009-11-24','161741.952000','t1_spc_WIP537_cor_surface','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112417165423402928463.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',20,'2009-11-24','171701.952000','t1_spc_WIP537_cor_surface_PreScanNormalize_Needle','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415160557860924767.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',9,'2009-11-24','151607.280000','t1_se_ax_OFF_iso','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415015166452324206.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',5,'2009-11-24','150154.798000','t2_spc_ns_sag_p2_iso','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415075486124424445.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',6,'2009-11-24','150756.655000','t1_se_ax_OFF_iso','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112416354054128327388.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',19,'2009-11-24','163552.202000','t1_spc_WIP537_cor_surface_PreScanNormalize','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415493132049125056.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',16,'2009-11-24','154932.124000','t1_se_ax_OFF','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415122398728824565.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',7,'2009-11-24','151226.577000','VIBE_1x1x1_body','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414575190620223971.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',4,'2009-11-24','145754.627000','VIBE_1x1x1_body','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414562789689323766.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',3,'2009-11-24','145630.752000','VIBE_1x1x1','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415141429610124694.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',8,'2009-11-24','151416.108000','t1_se_ax_OFF_iso_body','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414440663007822537.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',1,'2009-11-24','144410.799000','SCOUT','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124143837049.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415201967481224864.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',11,'2009-11-24','152020.233000','t1_se_ax_OFF','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415221047088724889.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',12,'2009-11-24','152211.187000','t1_se_ax_OFF_2mm','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415433060459424973.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',15,'2009-11-24','154334.780000','SCOUT','MR','','1.3.12.2.1107.5.2.31.30287.2.20091124154153702.0.0.0',1,'','GR',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112414480531183523134.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',2,'2009-11-24','144806.174000','t1_se_ax_OFF','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415175721344424833.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',10,'2009-11-24','151758.015000','t1_se_ax_OFF','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415240124326224925.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',13,'2009-11-24','152401.983000','t1_se_ax_OFF_2mm_body','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.2009112415255430595124964.0.0.0','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',14,'2009-11-24','152554.999000','t1_se_ax_OFF_2mm','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124143837049.0.0.4950',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000003549','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',103,'2009-11-24','171811.576000','','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000002726','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',102,'2009-11-24','163724.171000','','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001903','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',101,'2009-11-24','162255.999000','','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112412545103000000147','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',99,'2009-11-24','170811.702000','PhoenixZIPReport','MR','','',0,'','',0,0); +INSERT INTO "Series" VALUES('1.3.12.2.1107.5.2.31.30287.30000009112415082031000001080','1.3.12.2.1107.5.2.31.30287.30000009112412540589000000011',100,'2009-11-24','162146.108000','','MR','','1.3.12.2.1107.5.2.31.30287.1.20091124154153702.0.0.5014',1,'','SE',1,0); CREATE TABLE 'Studies' ( 'StudyInstanceUID' VARCHAR(255) NOT NULL , 'PatientsUID' INT NOT NULL , 'StudyID' VARCHAR(255) NULL , 'StudyDate' DATE NULL , 'StudyTime' VARCHAR(20) NULL , 'AccessionNumber' VARCHAR(255) NULL , 'ModalitiesInStudy' VARCHAR(255) NULL , 'InstitutionName' VARCHAR(255) NULL , 'ReferringPhysician' VARCHAR(255) NULL , 'PerformingPhysiciansName' VARCHAR(255) NULL , 'StudyDescription' VARCHAR(255) NULL , PRIMARY KEY ('StudyInstanceUID') ); INSERT INTO "Studies" VALUES('1.2.826.0.1.3680043.2.1125.1.73379483469717886505187028001198162',14,'123456','2009-01-02','010100.000000','1','','Hospital YYY', 'Unknown','me','None'); INSERT INTO "Studies" VALUES('1.3.12.2.1107.5.4.5.50096.30000009112417295950800000029',15,'Default StudyID','2009-11-24','143624.000000','','','Hospital XXX', '','you',''); diff --git a/Libs/DICOM/Core/Resources/dicom-schema.sql b/Libs/DICOM/Core/Resources/dicom-schema.sql index 72a0d4f0e6..6faa7b53ce 100644 --- a/Libs/DICOM/Core/Resources/dicom-schema.sql +++ b/Libs/DICOM/Core/Resources/dicom-schema.sql @@ -4,14 +4,25 @@ -- Note: the semicolon at the end is necessary for the simple parser to separate -- the statements since the SQlite driver does not handle multiple -- commands per QSqlQuery::exec call! +-- Note: be sure to update ctkDICOMDatabase and SchemaInfo Version +-- whenever you make a change to this schema -- ; +DROP TABLE IF EXISTS 'SchemaInfo' ; DROP TABLE IF EXISTS 'Images' ; DROP TABLE IF EXISTS 'Patients' ; DROP TABLE IF EXISTS 'Series' ; DROP TABLE IF EXISTS 'Studies' ; DROP TABLE IF EXISTS 'Directories' ; +DROP INDEX IF EXISTS 'ImagesFilenameIndex' ; +DROP INDEX IF EXISTS 'ImagesSeriesIndex' ; +DROP INDEX IF EXISTS 'SeriesStudyIndex' ; +DROP INDEX IF EXISTS 'StudiesPatientIndex' ; + +CREATE TABLE 'SchemaInfo' ( 'Version' VARCHAR(1024) NOT NULL ); +INSERT INTO 'SchemaInfo' VALUES('0.5.3'); + CREATE TABLE 'Images' ( 'SOPInstanceUID' VARCHAR(64) NOT NULL, 'Filename' VARCHAR(1024) NOT NULL , @@ -34,6 +45,7 @@ CREATE TABLE 'Series' ( 'SeriesDate' DATE NULL , 'SeriesTime' VARCHAR(20) NULL , 'SeriesDescription' VARCHAR(255) NULL , + 'Modality' VARCHAR(20) NULL , 'BodyPartExamined' VARCHAR(255) NULL , 'FrameOfReferenceUID' VARCHAR(64) NULL , 'AcquisitionNumber' INT NULL , @@ -56,6 +68,11 @@ CREATE TABLE 'Studies' ( 'StudyDescription' VARCHAR(255) NULL , PRIMARY KEY ('StudyInstanceUID') ); +CREATE UNIQUE INDEX IF NOT EXISTS 'ImagesFilenameIndex' ON 'Images' ('Filename'); +CREATE INDEX IF NOT EXISTS 'ImagesSeriesIndex' ON 'Images' ('SeriesInstanceUID'); +CREATE INDEX IF NOT EXISTS 'SeriesStudyIndex' ON 'Series' ('StudyInstanceUID'); +CREATE INDEX IF NOT EXISTS 'StudiesPatientIndex' ON 'Studies' ('PatientsUID'); + CREATE TABLE 'Directories' ( 'Dirname' VARCHAR(1024) , PRIMARY KEY ('Dirname') ); diff --git a/Libs/DICOM/Core/Resources/dicom-unversioned-schema.sql b/Libs/DICOM/Core/Resources/dicom-unversioned-schema.sql new file mode 100644 index 0000000000..72a0d4f0e6 --- /dev/null +++ b/Libs/DICOM/Core/Resources/dicom-unversioned-schema.sql @@ -0,0 +1,61 @@ +-- +-- A simple SQLITE3 database schema for modelling locally stored DICOM files +-- +-- Note: the semicolon at the end is necessary for the simple parser to separate +-- the statements since the SQlite driver does not handle multiple +-- commands per QSqlQuery::exec call! +-- ; + +DROP TABLE IF EXISTS 'Images' ; +DROP TABLE IF EXISTS 'Patients' ; +DROP TABLE IF EXISTS 'Series' ; +DROP TABLE IF EXISTS 'Studies' ; +DROP TABLE IF EXISTS 'Directories' ; + +CREATE TABLE 'Images' ( + 'SOPInstanceUID' VARCHAR(64) NOT NULL, + 'Filename' VARCHAR(1024) NOT NULL , + 'SeriesInstanceUID' VARCHAR(64) NOT NULL , + 'InsertTimestamp' VARCHAR(20) NOT NULL , + PRIMARY KEY ('SOPInstanceUID') ); +CREATE TABLE 'Patients' ( + 'UID' INTEGER PRIMARY KEY AUTOINCREMENT, + 'PatientsName' VARCHAR(255) NULL , + 'PatientID' VARCHAR(255) NULL , + 'PatientsBirthDate' DATE NULL , + 'PatientsBirthTime' TIME NULL , + 'PatientsSex' varchar(1) NULL , + 'PatientsAge' varchar(10) NULL , + 'PatientsComments' VARCHAR(255) NULL ); +CREATE TABLE 'Series' ( + 'SeriesInstanceUID' VARCHAR(64) NOT NULL , + 'StudyInstanceUID' VARCHAR(64) NOT NULL , + 'SeriesNumber' INT NULL , + 'SeriesDate' DATE NULL , + 'SeriesTime' VARCHAR(20) NULL , + 'SeriesDescription' VARCHAR(255) NULL , + 'BodyPartExamined' VARCHAR(255) NULL , + 'FrameOfReferenceUID' VARCHAR(64) NULL , + 'AcquisitionNumber' INT NULL , + 'ContrastAgent' VARCHAR(255) NULL , + 'ScanningSequence' VARCHAR(45) NULL , + 'EchoNumber' INT NULL , + 'TemporalPosition' INT NULL , + PRIMARY KEY ('SeriesInstanceUID') ); +CREATE TABLE 'Studies' ( + 'StudyInstanceUID' VARCHAR(64) NOT NULL , + 'PatientsUID' INT NOT NULL , + 'StudyID' VARCHAR(255) NULL , + 'StudyDate' DATE NULL , + 'StudyTime' VARCHAR(20) NULL , + 'AccessionNumber' VARCHAR(255) NULL , + 'ModalitiesInStudy' VARCHAR(255) NULL , + 'InstitutionName' VARCHAR(255) NULL , + 'ReferringPhysician' VARCHAR(255) NULL , + 'PerformingPhysiciansName' VARCHAR(255) NULL , + 'StudyDescription' VARCHAR(255) NULL , + PRIMARY KEY ('StudyInstanceUID') ); + +CREATE TABLE 'Directories' ( + 'Dirname' VARCHAR(1024) , + PRIMARY KEY ('Dirname') ); diff --git a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt index 23a6a39ab7..c2bdba55ac 100644 --- a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt +++ b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt @@ -4,6 +4,8 @@ create_test_sourcelist(Tests ${KIT}CppTests.cpp ctkDICOMCoreTest1.cpp ctkDICOMDatabaseTest1.cpp ctkDICOMDatabaseTest2.cpp + ctkDICOMDatabaseTest3.cpp + ctkDICOMDatabaseTest4.cpp ctkDICOMDatasetTest1.cpp ctkDICOMIndexerTest1.cpp ctkDICOMModelTest1.cpp @@ -31,6 +33,10 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME}) # ctkDICOMDatabase SIMPLE_TEST(ctkDICOMDatabaseTest1) SIMPLE_TEST(ctkDICOMDatabaseTest2 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA) +SIMPLE_TEST(ctkDICOMDatabaseTest3 + ${CMAKE_CURRENT_SOURCE_DIR}/../../Resources/dicom-unversioned-schema.sql + ) +SIMPLE_TEST(ctkDICOMDatabaseTest4 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA) SIMPLE_TEST(ctkDICOMDatasetTest1) SIMPLE_TEST(ctkDICOMIndexerTest1 ) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp index ab75c6d3f2..7cc0c08514 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp @@ -46,6 +46,9 @@ int ctkDICOMDatabaseTest2( int argc, char * argv [] ) ctkDICOMDatabase database; QDir databaseDirectory = QDir::temp(); + databaseDirectory.remove("ctkDICOMDatabase.sql"); + databaseDirectory.remove("ctkDICOMTagCache.sql"); + QFileInfo databaseFile(databaseDirectory, QString("database.test")); database.openDatabase(databaseFile.absoluteFilePath()); @@ -67,11 +70,11 @@ int ctkDICOMDatabaseTest2( int argc, char * argv [] ) { std::cerr << "ctkDICOMDatabase::openDatabase() failed: " << "database should not be in memory" << std::endl; - return EXIT_FAILURE; + return EXIT_FAILURE; } bool res = database.initializeDatabase(); - + if (!res) { std::cerr << "ctkDICOMDatabase::initializeDatabase() failed." << std::endl; @@ -97,6 +100,13 @@ int ctkDICOMDatabaseTest2( int argc, char * argv [] ) return EXIT_FAILURE; } + if ( database.groupElementToTag(group, element) != tag ) + { + std::cerr << "ctkDICOMDatabase: could not convert a uints to tag string" << std::endl; + return EXIT_FAILURE; + } + + // // Basic test: // - insert the file specified on the command line @@ -112,12 +122,82 @@ int ctkDICOMDatabaseTest2( int argc, char * argv [] ) std::cerr << "ctkDICOMDatabase: didn't get back the original file path" << std::endl; return EXIT_FAILURE; } - + + QString foundInstance = database.instanceForFile(dicomFilePath); + + if (foundInstance != instanceUID) + { + std::cerr << "ctkDICOMDatabase: didn't get back the original instance uid" << std::endl; + return EXIT_FAILURE; + } + + + // + // Test the tag cache + // + + if (database.tagCacheExists()) + { + std::cerr << "ctkDICOMDatabase: tag cache should not exist in fresh database" << std::endl; + return EXIT_FAILURE; + } + + if (!database.initializeTagCache()) + { + std::cerr << "ctkDICOMDatabase: could not initialize tag cache" << std::endl; + return EXIT_FAILURE; + } + + if (!database.tagCacheExists()) + { + std::cerr << "ctkDICOMDatabase: tag cache should exist but is not detected" << std::endl; + return EXIT_FAILURE; + } + + + if (database.cachedTag(instanceUID, tag) != QString("")) + { + std::cerr << "ctkDICOMDatabase: tag cache should return empty string for unknown instance tag" << std::endl; + return EXIT_FAILURE; + } QString knownSeriesDescription("3D Cor T1 FAST IR-prepped GRE"); + if (!database.cacheTag(instanceUID, tag, knownSeriesDescription)) + { + std::cerr << "ctkDICOMDatabase: could not insert instance tag" << std::endl; + return EXIT_FAILURE; + } + + if (database.cachedTag(instanceUID, tag) != knownSeriesDescription) + { + std::cerr << "ctkDICOMDatabase: could not retrieve cached tag" << std::endl; + return EXIT_FAILURE; + } + + QString foundSeriesDescription = database.instanceValue(instanceUID, tag); + if (foundSeriesDescription != knownSeriesDescription) + { + std::cerr << "ctkDICOMDatabase: invalid element value returned" << std::endl; + return EXIT_FAILURE; + } + + // now update the database + database.updateSchema(); + + // and repeat the above checks + foundFile = database.fileForInstance(instanceUID); + + if (foundFile != dicomFilePath) + { + std::cerr << "ctkDICOMDatabase: didn't get back the original file path" << std::endl; + return EXIT_FAILURE; + } + + foundSeriesDescription = database.instanceValue(instanceUID, tag); + if (foundSeriesDescription != knownSeriesDescription) { std::cerr << "ctkDICOMDatabase: invalid element value returned" << std::endl; diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest3.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest3.cpp new file mode 100644 index 0000000000..b23fb232d8 --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest3.cpp @@ -0,0 +1,114 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" + +// STD includes +#include +#include + + +int ctkDICOMDatabaseTest3( int argc, char * argv [] ) +{ + QCoreApplication app(argc, argv); + + if (argc <= 1) + { + std::cerr << "Warning, no sql file given. Test stops" << std::endl; + std::cerr << "Usage: ctkDICOMDatabaseTest3 " << std::endl; + return EXIT_FAILURE; + } + + QDir databaseDirectory = QDir::temp(); + databaseDirectory.remove("ctkDICOMDatabase.sql"); + + QFileInfo databaseFile(databaseDirectory, QString("database.test")); + QString databaseFileName(databaseFile.absoluteFilePath()); + + std::cerr << "Populating database " << databaseFileName.toStdString() << "\n"; + + // first, create a database and initialize it with the old schema + try + { + ctkDICOMDatabase myCTK( databaseFileName ); + + if (!myCTK.initializeDatabase(argv[1])) + { + std::cerr << "Error when initializing the data base with: " << argv[1] + << " error: " << myCTK.lastError().toStdString(); + return EXIT_FAILURE; + } + + if ( myCTK.schemaVersionLoaded() != QString("") ) + { + std::cerr << "Schema tag should be empty in old schema\n"; + std::cerr << "Instead we got: (" << myCTK.schemaVersionLoaded().toStdString() << ")\n"; + return EXIT_FAILURE; + } + + myCTK.closeDatabase(); + } + catch (std::exception e) + { + std::cerr << "Error when opening the data base file: " << databaseFileName.toStdString() + << " error: " << e.what(); + return EXIT_FAILURE; + } + + // now try opening it and updating the schema + try + { + ctkDICOMDatabase myCTK( databaseFileName ); + + if ( myCTK.schemaVersionLoaded() == myCTK.schemaVersion() ) + { + std::cerr << "Schema version should Not match\n"; + return EXIT_FAILURE; + } + + if ( !myCTK.updateSchema() ) + { + std::cerr << "Could not update schema\n"; + return EXIT_FAILURE; + } + + if ( myCTK.schemaVersionLoaded() != myCTK.schemaVersion() ) + { + std::cerr << "Schema version should match\n"; + return EXIT_FAILURE; + } + + myCTK.closeDatabase(); + } + catch (std::exception e) + { + std::cerr << "Error when re-opening the data base file: " << databaseFileName.toStdString() + << " error: " << e.what(); + return EXIT_FAILURE; + } + + + return EXIT_SUCCESS; +} diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest4.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest4.cpp new file mode 100644 index 0000000000..20e1ac3e00 --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest4.cpp @@ -0,0 +1,150 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" + +// STD includes +#include +#include + + +int ctkDICOMDatabaseTest4( int argc, char * argv [] ) +{ + QCoreApplication app(argc, argv); + + if (argc < 2) + { + std::cerr << "ctkDICOMDatabaseTest2: missing dicom filePath argument"; + std::cerr << std::endl; + return EXIT_FAILURE; + } + + QString dicomFilePath(argv[1]); + + ctkDICOMDatabase database; + QDir databaseDirectory = QDir::temp(); + databaseDirectory.remove("ctkDICOMDatabase.sql"); + databaseDirectory.remove("ctkDICOMTagCache.sql"); + + QFileInfo databaseFile(databaseDirectory, QString("database.test")); + database.openDatabase(databaseFile.absoluteFilePath()); + + bool res = database.initializeDatabase(); + + if (!res) + { + std::cerr << "ctkDICOMDatabase::initializeDatabase() failed." << std::endl; + return EXIT_FAILURE; + } + + // + // Basic test: + // - insert the file specified on the command line + // - ask for tag values and compare to known results + // + QString instanceUID("1.2.840.113619.2.135.3596.6358736.4843.1115808177.83"); + QString tag("0008,103e"); + QString badTag("9999,9999"); + + // + // Test the precache feature of the database + // + + if (database.cachedTag(instanceUID, tag) != QString("")) + { + std::cerr << "ctkDICOMDatabase: tag cache should return empty string for unknown instance tag" << std::endl; + return EXIT_FAILURE; + } + + if (database.cachedTag(instanceUID, badTag) != QString("")) + { + std::cerr << "ctkDICOMDatabase: bad tag cache should return empty string for unknown instance tag" << std::endl; + return EXIT_FAILURE; + } + + QStringList tagsToPrecache; + tagsToPrecache << tag; + database.setTagsToPrecache(tagsToPrecache); + + if (database.tagsToPrecache() != tagsToPrecache) + { + std::cerr << "ctkDICOMDatabase: tags to precache not correct" << std::endl; + return EXIT_FAILURE; + } + + // check the insert timestamp + QDateTime beforeInsert = QDateTime::currentDateTime(); + std::cerr << "Current dateTime " << beforeInsert.toString().toStdString() << std::endl; + + database.insert(dicomFilePath, false, false); + + QDateTime insertTimeStamp = database.insertDateTimeForInstance(instanceUID); + std::cerr << "Instance inserted " << insertTimeStamp.toString().toStdString() << std::endl; + + QString filePath = database.fileForInstance(instanceUID); + std::cerr << "Instance file " << filePath.toStdString() << std::endl; + + int elapsed = beforeInsert.secsTo(insertTimeStamp); + if (elapsed > 1) + { + std::cerr << "ctkDICOMDatabase: Took more than a second to insert the file." << std::endl; + return EXIT_FAILURE; + } + + // check for series description in tag cache + QString knownSeriesDescription("3D Cor T1 FAST IR-prepped GRE"); + + QString cachedTag = database.cachedTag(instanceUID, tag); + + if (cachedTag != knownSeriesDescription) + { + std::cerr << "ctkDICOMDatabase: tag cache should return known value for instance" << std::endl; + return EXIT_FAILURE; + } + + if (database.instanceValue(instanceUID, tag) != knownSeriesDescription) + { + std::cerr << "ctkDICOMDatabase: database should return known value for instance" << std::endl; + return EXIT_FAILURE; + } + + if (database.instanceValue(instanceUID, badTag) != QString("")) + { + std::cerr << "ctkDICOMDatabase: bad tag should have empty value" << std::endl; + return EXIT_FAILURE; + } + + if (database.cachedTag(instanceUID, badTag) != QString("__TAG_NOT_IN_INSTANCE__")) + { + std::cerr << "ctkDICOMDatabase: bad tag should have sentinal value in cache" << std::endl; + return EXIT_FAILURE; + } + + database.closeDatabase(); + + std::cerr << "Database is in " << databaseDirectory.path().toStdString() << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index 12c4664ce2..5f4218d546 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -21,19 +21,19 @@ #include // Qt includes -#include -#include -#include -#include #include -#include -#include -#include +#include #include +#include #include -#include #include #include +#include +#include +#include +#include +#include +#include // ctkDICOM includes #include "ctkDICOMDatabase.h" @@ -62,6 +62,10 @@ static ctkLogger logger("org.commontk.dicom.DICOMDatabase" ); //------------------------------------------------------------------------------ +// Flag for tag cache to avoid repeated serarches for +// tags that do no exist. +static QString TagNotInInstance("__TAG_NOT_IN_INSTANCE__"); + //------------------------------------------------------------------------------ class ctkDICOMDatabasePrivate { @@ -80,11 +84,26 @@ class ctkDICOMDatabasePrivate /// bool loggedExec(QSqlQuery& query); bool loggedExec(QSqlQuery& query, const QString& queryString); + bool LoggedExecVerbose; // dataset must be set always // filePath has to be set if this is an import of an actual file void insert ( const ctkDICOMDataset& ctkDataset, const QString& filePath, bool storeFile = true, bool generateThumbnail = true); + /// + /// copy the complete list of files to an extra table + /// + void createBackupFileList(); + + /// + /// remove the extra table containing the backup + /// + void removeBackupFileList(); + + + /// + /// get all Filename values from table + QStringList filenames(QString table); /// Name of the database file (i.e. for SQLITE the sqlite file) QString DatabaseFileName; @@ -103,9 +122,22 @@ class ctkDICOMDatabasePrivate QString LastSeriesInstanceUID; int LastPatientUID; + /// resets the variables to new inserts won't be fooled by leftover values + void resetLastInsertedValues(); + /// parallel inserts are not allowed (yet) QMutex insertMutex; + /// tagCache table has been checked to exist + bool TagCacheVerified; + /// tag cache has independent database to avoid locking issue + /// with other access to the database which need to be + /// reading while the tag cache is writing + QSqlDatabase TagCacheDatabase; + QString TagCacheDatabaseFilename; + QStringList TagsToPrecache; + void precacheTags( const QString sopInstanceUID ); + int insertPatient(const ctkDICOMDataset& ctkDataset); void insertStudy(const ctkDICOMDataset& ctkDataset, int dbPatientID); void insertSeries( const ctkDICOMDataset& ctkDataset, QString studyInstanceUID); @@ -118,6 +150,19 @@ class ctkDICOMDatabasePrivate ctkDICOMDatabasePrivate::ctkDICOMDatabasePrivate(ctkDICOMDatabase& o): q_ptr(&o) { this->thumbnailGenerator = NULL; + this->LoggedExecVerbose = false; + this->TagCacheVerified = false; + this->resetLastInsertedValues(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabasePrivate::resetLastInsertedValues() +{ + this->LastPatientID = QString(""); + this->LastPatientsName = QString(""); + this->LastPatientsBirthDate = QString(""); + this->LastStudyInstanceUID = QString(""); + this->LastSeriesInstanceUID = QString(""); this->LastPatientUID = -1; } @@ -127,6 +172,7 @@ void ctkDICOMDatabasePrivate::init(QString databaseFilename) Q_Q(ctkDICOMDatabase); q->openDatabase(databaseFilename); + } //------------------------------------------------------------------------------ @@ -175,11 +221,31 @@ bool ctkDICOMDatabasePrivate::loggedExec(QSqlQuery& query, const QString& queryS } else { + if (LoggedExecVerbose) + { logger.debug( "SQL worked!\n SQL: " + query.lastQuery()); + } } return (success); } +//------------------------------------------------------------------------------ +void ctkDICOMDatabasePrivate::createBackupFileList() +{ + QSqlQuery query(this->Database); + loggedExec(query, "CREATE TABLE IF NOT EXISTS main.Filenames_backup (Filename TEXT PRIMARY KEY NOT NULL )" ); + loggedExec(query, "INSERT INTO Filenames_backup SELECT Filename FROM Images;" ); +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabasePrivate::removeBackupFileList() +{ + QSqlQuery query(this->Database); + loggedExec(query, "DROP TABLE main.Filenames_backup; " ); +} + + + //------------------------------------------------------------------------------ void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& connectionName ) { @@ -200,11 +266,23 @@ void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& c return; } } + d->resetLastInsertedValues(); + if (!isInMemory()) { QFileSystemWatcher* watcher = new QFileSystemWatcher(QStringList(databaseFile),this); connect(watcher, SIGNAL(fileChanged(QString)),this, SIGNAL (databaseChanged()) ); } + + //Disable synchronous writing to make modifications faster + QSqlQuery pragmaSyncQuery(d->Database); + pragmaSyncQuery.exec("PRAGMA synchronous = OFF"); + pragmaSyncQuery.finish(); + + // set up the tag cache for use later + QFileInfo fileInfo(d->DatabaseFileName); + d->TagCacheDatabaseFilename = QString( fileInfo.dir().path() + "/ctkDICOMTagCache.sql" ); + d->TagCacheVerified = false; } @@ -312,20 +390,129 @@ bool ctkDICOMDatabasePrivate::executeScript(const QString script) { return true; } +//------------------------------------------------------------------------------ +QStringList ctkDICOMDatabasePrivate::filenames(QString table) +{ + /// get all filenames from the database + QSqlQuery allFilesQuery(this->Database); + QStringList allFileNames; + loggedExec(allFilesQuery,QString("SELECT Filename from %1 ;").arg(table) ); + + while (allFilesQuery.next()) + { + allFileNames << allFilesQuery.value(0).toString(); + } + return allFileNames; +} + //------------------------------------------------------------------------------ bool ctkDICOMDatabase::initializeDatabase(const char* sqlFileName) { Q_D(ctkDICOMDatabase); + + d->resetLastInsertedValues(); + + // remove any existing schema info - this handles the case where an + // old schema should be loaded for testing. + QSqlQuery dropSchemaInfo(d->Database); + d->loggedExec( dropSchemaInfo, QString("DROP TABLE IF EXISTS 'SchemaInfo';") ); return d->executeScript(sqlFileName); } +//------------------------------------------------------------------------------ +QString ctkDICOMDatabase::schemaVersionLoaded() +{ + Q_D(ctkDICOMDatabase); + /// look for the version info in the database + QSqlQuery versionQuery(d->Database); + if ( !d->loggedExec( versionQuery, QString("SELECT Version from SchemaInfo;") ) ) + { + return QString(""); + } + + if (versionQuery.next()) + { + return versionQuery.value(0).toString(); + } + + return QString(""); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMDatabase::schemaVersion() +{ + // When changing schema version: + // * make sure this matches the Version value in the + // SchemaInfo table defined in Resources/dicom-schema.sql + // * make sure the 'Images' contains a 'Filename' column + // so that the ctkDICOMDatabasePrivate::filenames method + // still works. + // + return QString("0.5.3"); +}; + +//------------------------------------------------------------------------------ +bool ctkDICOMDatabase::updateSchemaIfNeeded(const char* schemaFile) +{ + if ( schemaVersionLoaded() != schemaVersion() ) + { + return this->updateSchema(schemaFile); + } + else + { + emit schemaUpdateStarted(0); + emit schemaUpdated(); + return false; + } +} + +//------------------------------------------------------------------------------ +bool ctkDICOMDatabase::updateSchema(const char* schemaFile) +{ + // backup filelist + // reinit with the new schema + // reinsert everything + + Q_D(ctkDICOMDatabase); + d->createBackupFileList(); + + d->resetLastInsertedValues(); + this->initializeDatabase(schemaFile); + + QStringList allFiles = d->filenames("Filenames_backup"); + emit schemaUpdateStarted(allFiles.length()); + + int progressValue = 0; + foreach(QString file, allFiles) + { + emit schemaUpdateProgress(progressValue); + emit schemaUpdateProgress(file); + + // TODO: use QFuture + this->insert(file,false,false,true); + + progressValue++; + } + // TODO: check better that everything is ok + d->removeBackupFileList(); + emit schemaUpdated(); + return true; + +} + + //------------------------------------------------------------------------------ void ctkDICOMDatabase::closeDatabase() { Q_D(ctkDICOMDatabase); d->Database.close(); + d->TagCacheDatabase.close(); } +// +// Patient/study/series convenience methods +// + //------------------------------------------------------------------------------ QStringList ctkDICOMDatabase::patients() { @@ -398,13 +585,57 @@ QString ctkDICOMDatabase::fileForInstance(QString sopInstanceUID) query.bindValue ( 0, sopInstanceUID ); query.exec(); QString result; - if (query.next()) + if (query.next()) { result = query.value(0).toString(); } return( result ); } +//------------------------------------------------------------------------------ +QString ctkDICOMDatabase::instanceForFile(QString fileName) +{ + Q_D(ctkDICOMDatabase); + QSqlQuery query(d->Database); + query.prepare ( "SELECT SOPInstanceUID FROM Images WHERE Filename=?"); + query.bindValue ( 0, fileName ); + query.exec(); + QString result; + if (query.next()) + { + result = query.value(0).toString(); + } + return( result ); +} + +//------------------------------------------------------------------------------ +QDateTime ctkDICOMDatabase::insertDateTimeForInstance(QString sopInstanceUID) +{ + Q_D(ctkDICOMDatabase); + QSqlQuery query(d->Database); + query.prepare ( "SELECT InsertTimestamp FROM Images WHERE SOPInstanceUID=?"); + query.bindValue ( 0, sopInstanceUID ); + query.exec(); + QDateTime result; + if (query.next()) + { + result = QDateTime::fromString(query.value(0).toString(), Qt::ISODate); + } + return( result ); +} + + +// +// instance header methods +// + +//------------------------------------------------------------------------------ +QStringList ctkDICOMDatabase::allFiles() +{ + Q_D(ctkDICOMDatabase); + return d->filenames("Images"); +} + //------------------------------------------------------------------------------ void ctkDICOMDatabase::loadInstanceHeader (QString sopInstanceUID) { @@ -435,12 +666,15 @@ void ctkDICOMDatabase::loadFileHeader (QString fileName) while (dataset->nextObject(stack, true) == EC_Normal) { DcmObject *dO = stack.top(); - QString tag = QString("%1,%2").arg( - dO->getGTag(),4,16,QLatin1Char('0')).arg( - dO->getETag(),4,16,QLatin1Char('0')); - std::ostringstream s; - dO->print(s); - d->LoadedHeader[tag] = QString(s.str().c_str()); + if (dO) + { + QString tag = QString("%1,%2").arg( + dO->getGTag(),4,16,QLatin1Char('0')).arg( + dO->getETag(),4,16,QLatin1Char('0')); + std::ostringstream s; + dO->print(s); + d->LoadedHeader[tag] = QString(s.str().c_str()); + } } } return; @@ -460,9 +694,22 @@ QString ctkDICOMDatabase::headerValue (QString key) return (d->LoadedHeader[key]); } +// +// instanceValue and fileValue methods +// + //------------------------------------------------------------------------------ QString ctkDICOMDatabase::instanceValue(QString sopInstanceUID, QString tag) { + QString value = this->cachedTag(sopInstanceUID, tag); + if (value == TagNotInInstance) + { + return ""; + } + if (value != "") + { + return value; + } unsigned short group, element; this->tagToGroupElement(tag, group, element); return( this->instanceValue(sopInstanceUID, group, element) ); @@ -471,10 +718,21 @@ QString ctkDICOMDatabase::instanceValue(QString sopInstanceUID, QString tag) //------------------------------------------------------------------------------ QString ctkDICOMDatabase::instanceValue(const QString sopInstanceUID, const unsigned short group, const unsigned short element) { + QString tag = this->groupElementToTag(group,element); + QString value = this->cachedTag(sopInstanceUID, tag); + if (value == TagNotInInstance) + { + return ""; + } + if (value != "") + { + return value; + } QString filePath = this->fileForInstance(sopInstanceUID); if (filePath != "" ) { - return( this->fileValue(filePath, group, element) ); + value = this->fileValue(filePath, group, element); + return( value ); } else { @@ -488,6 +746,16 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag) { unsigned short group, element; this->tagToGroupElement(tag, group, element); + QString sopInstanceUID = this->instanceForFile(fileName); + QString value = this->cachedTag(sopInstanceUID, tag); + if (value == TagNotInInstance) + { + return ""; + } + if (value != "") + { + return value; + } return( this->fileValue(fileName, group, element) ); } @@ -495,6 +763,8 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag) QString ctkDICOMDatabase::fileValue(const QString fileName, const unsigned short group, const unsigned short element) { // here is where the real lookup happens + // - first we check the tagCache to see if the value exists for this instance tag + // If not, // - for now we create a ctkDICOMDataset and extract the value from there // - then we convert to the appropriate type of string // @@ -505,12 +775,26 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, const unsigned short // -- if so, keep looking for the requested group/element // -- if not, start again from the begining + QString tag = this->groupElementToTag(group, element); + QString sopInstanceUID = this->instanceForFile(fileName); + QString value = this->cachedTag(sopInstanceUID, tag); + if (value == TagNotInInstance) + { + return ""; + } + if (value != "") + { + return value; + } + ctkDICOMDataset dataset; dataset.InitializeFromFile(fileName); - + DcmTagKey tagKey(group, element); - return( dataset.GetAllElementValuesAsString(tagKey) ); + value = dataset.GetAllElementValuesAsString(tagKey); + this->cacheTag(sopInstanceUID, tag, value); + return( value ); } //------------------------------------------------------------------------------ @@ -518,12 +802,26 @@ bool ctkDICOMDatabase::tagToGroupElement(const QString tag, unsigned short& grou { QStringList groupElement = tag.split(","); bool groupOK, elementOK; + if (groupElement.length() != 2) + { + return false; + } group = groupElement[0].toUInt(&groupOK, 16); element = groupElement[1].toUInt(&elementOK, 16); return( groupOK && elementOK ); } +//------------------------------------------------------------------------------ +QString ctkDICOMDatabase::groupElementToTag(const unsigned short& group, const unsigned short& element) +{ + return QString("%1,%2").arg(group,4,16,QLatin1Char('0')).arg(element,4,16,QLatin1Char('0')); +} + +// +// methods related to insert +// + //------------------------------------------------------------------------------ void ctkDICOMDatabase::insert( DcmDataset *dataset, bool storeFile, bool generateThumbnail) { @@ -610,7 +908,7 @@ int ctkDICOMDatabasePrivate::insertPatient(const ctkDICOMDataset& ctkDataset) insertPatientStatement.prepare ( "INSERT INTO Patients ('UID', 'PatientsName', 'PatientID', 'PatientsBirthDate', 'PatientsBirthTime', 'PatientsSex', 'PatientsAge', 'PatientsComments' ) values ( NULL, ?, ?, ?, ?, ?, ?, ? )" ); insertPatientStatement.bindValue ( 0, patientsName ); insertPatientStatement.bindValue ( 1, patientID ); - insertPatientStatement.bindValue ( 2, patientsBirthDate ); + insertPatientStatement.bindValue ( 2, QDate::fromString ( patientsBirthDate, "yyyyMMdd" ) ); insertPatientStatement.bindValue ( 3, patientsBirthTime ); insertPatientStatement.bindValue ( 4, patientsSex ); // TODO: shift patient's age to study, @@ -625,6 +923,7 @@ int ctkDICOMDatabasePrivate::insertPatient(const ctkDICOMDataset& ctkDataset) return dbPatientID; } +//------------------------------------------------------------------------------ void ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMDataset& ctkDataset, int dbPatientID) { QString studyInstanceUID(ctkDataset.GetElementAsString(DCM_StudyInstanceUID) ); @@ -634,6 +933,7 @@ void ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMDataset& ctkDataset, int checkStudyExistsQuery.exec(); if(!checkStudyExistsQuery.next()) { + qDebug() << "Need to insert new study: " << studyInstanceUID; QString studyID(ctkDataset.GetElementAsString(DCM_StudyID) ); QString studyDate(ctkDataset.GetElementAsString(DCM_StudyDate) ); @@ -666,10 +966,14 @@ void ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMDataset& ctkDataset, int { LastStudyInstanceUID = studyInstanceUID; } - + } + else + { + qDebug() << "Used existing study: " << studyInstanceUID; } } +//------------------------------------------------------------------------------ void ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMDataset& ctkDataset, QString studyInstanceUID) { QString seriesInstanceUID(ctkDataset.GetElementAsString(DCM_SeriesInstanceUID) ); @@ -680,9 +984,12 @@ void ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMDataset& ctkDataset, QS loggedExec(checkSeriesExistsQuery); if(!checkSeriesExistsQuery.next()) { + qDebug() << "Need to insert new series: " << seriesInstanceUID; + QString seriesDate(ctkDataset.GetElementAsString(DCM_SeriesDate) ); QString seriesTime(ctkDataset.GetElementAsString(DCM_SeriesTime) ); QString seriesDescription(ctkDataset.GetElementAsString(DCM_SeriesDescription) ); + QString modality(ctkDataset.GetElementAsString(DCM_Modality) ); QString bodyPartExamined(ctkDataset.GetElementAsString(DCM_BodyPartExamined) ); QString frameOfReferenceUID(ctkDataset.GetElementAsString(DCM_FrameOfReferenceUID) ); QString contrastAgent(ctkDataset.GetElementAsString(DCM_ContrastBolusAgent) ); @@ -693,20 +1000,21 @@ void ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMDataset& ctkDataset, QS long temporalPosition(ctkDataset.GetElementAsInteger(DCM_TemporalPositionIdentifier) ); QSqlQuery insertSeriesStatement ( Database ); - insertSeriesStatement.prepare ( "INSERT INTO Series ( 'SeriesInstanceUID', 'StudyInstanceUID', 'SeriesNumber', 'SeriesDate', 'SeriesTime', 'SeriesDescription', 'BodyPartExamined', 'FrameOfReferenceUID', 'AcquisitionNumber', 'ContrastAgent', 'ScanningSequence', 'EchoNumber', 'TemporalPosition' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" ); + insertSeriesStatement.prepare ( "INSERT INTO Series ( 'SeriesInstanceUID', 'StudyInstanceUID', 'SeriesNumber', 'SeriesDate', 'SeriesTime', 'SeriesDescription', 'Modality', 'BodyPartExamined', 'FrameOfReferenceUID', 'AcquisitionNumber', 'ContrastAgent', 'ScanningSequence', 'EchoNumber', 'TemporalPosition' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" ); insertSeriesStatement.bindValue ( 0, seriesInstanceUID ); insertSeriesStatement.bindValue ( 1, studyInstanceUID ); insertSeriesStatement.bindValue ( 2, static_cast(seriesNumber) ); - insertSeriesStatement.bindValue ( 3, seriesDate ); - insertSeriesStatement.bindValue ( 4, QDate::fromString ( seriesTime, "yyyyMMdd" ) ); + insertSeriesStatement.bindValue ( 3, QDate::fromString ( seriesDate, "yyyyMMdd" ) ); + insertSeriesStatement.bindValue ( 4, seriesTime ); insertSeriesStatement.bindValue ( 5, seriesDescription ); - insertSeriesStatement.bindValue ( 6, bodyPartExamined ); - insertSeriesStatement.bindValue ( 7, frameOfReferenceUID ); - insertSeriesStatement.bindValue ( 8, static_cast(acquisitionNumber) ); - insertSeriesStatement.bindValue ( 9, contrastAgent ); - insertSeriesStatement.bindValue ( 10, scanningSequence ); - insertSeriesStatement.bindValue ( 11, static_cast(echoNumber) ); - insertSeriesStatement.bindValue ( 12, static_cast(temporalPosition) ); + insertSeriesStatement.bindValue ( 6, modality ); + insertSeriesStatement.bindValue ( 7, bodyPartExamined ); + insertSeriesStatement.bindValue ( 8, frameOfReferenceUID ); + insertSeriesStatement.bindValue ( 9, static_cast(acquisitionNumber) ); + insertSeriesStatement.bindValue ( 10, contrastAgent ); + insertSeriesStatement.bindValue ( 11, scanningSequence ); + insertSeriesStatement.bindValue ( 12, static_cast(echoNumber) ); + insertSeriesStatement.bindValue ( 13, static_cast(temporalPosition) ); if ( !insertSeriesStatement.exec() ) { logger.error ( "Error executing statament: " @@ -719,12 +1027,53 @@ void ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMDataset& ctkDataset, QS LastSeriesInstanceUID = seriesInstanceUID; } } + else + { + qDebug() << "Used existing series: " << seriesInstanceUID; + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabase::setTagsToPrecache( const QStringList tags) +{ + Q_D(ctkDICOMDatabase); + d->TagsToPrecache = tags; +} + +//------------------------------------------------------------------------------ +const QStringList ctkDICOMDatabase::tagsToPrecache() +{ + Q_D(ctkDICOMDatabase); + return d->TagsToPrecache; } +//------------------------------------------------------------------------------ +void ctkDICOMDatabasePrivate::precacheTags( const QString sopInstanceUID ) +{ + Q_Q(ctkDICOMDatabase); + + ctkDICOMDataset dataset; + QString fileName = q->fileForInstance(sopInstanceUID); + dataset.InitializeFromFile(fileName); + + foreach (const QString &tag, this->TagsToPrecache) + { + unsigned short group, element; + q->tagToGroupElement(tag, group, element); + DcmTagKey tagKey(group, element); + QString value = dataset.GetAllElementValuesAsString(tagKey); + q->cacheTag(sopInstanceUID, tag, value); + } +} + +//------------------------------------------------------------------------------ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const QString& filePath, bool storeFile, bool generateThumbnail) { Q_Q(ctkDICOMDatabase); + // this is the method that all other insert signatures end up calling + // after they have pre-parsed their arguments + QMutexLocker lock(&insertMutex); // Check to see if the file has already been loaded @@ -771,6 +1120,12 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q QString studyInstanceUID(ctkDataset.GetElementAsString(DCM_StudyInstanceUID) ); QString seriesInstanceUID(ctkDataset.GetElementAsString(DCM_SeriesInstanceUID) ); QString patientID(ctkDataset.GetElementAsString(DCM_PatientID) ); + if ( patientID.isEmpty() && !studyInstanceUID.isEmpty() ) + { // Use study instance uid as patient id if patient id is empty - can happen on anonymized datasets + // see: http://www.na-mic.org/Bug/view.php?id=2040 + logger.warn("Patient ID is empty, using studyInstanceUID as patient ID"); + patientID = studyInstanceUID; + } if ( patientsName.isEmpty() && !patientID.isEmpty() ) { // Use patient id as name if name is empty - can happen on anonymized datasets // see: http://www.na-mic.org/Bug/view.php?id=1643 @@ -778,16 +1133,10 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q } if ( patientsName.isEmpty() || studyInstanceUID.isEmpty() || patientID.isEmpty() ) { - logger.error("Dataset is missing necessary information!"); + logger.error("Dataset is missing necessary information (patient name, study instance UID, or patient ID)!"); return; } - - - - - - // store the file if the database is not in memomry // TODO: if we are called from insert(file) we // have to do something else @@ -885,6 +1234,9 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q insertImageStatement.bindValue ( 2, seriesInstanceUID ); insertImageStatement.bindValue ( 3, QDateTime::currentDateTime() ); insertImageStatement.exec(); + + // insert was needed, so cache any application-requested tags + this->precacheTags(sopInstanceUID); } } @@ -910,8 +1262,13 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q emit q->databaseChanged(); } } + else + { + qDebug() << "No patient name or no patient id - not inserting!"; + } } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::fileExistsAndUpToDate(const QString& filePath) { Q_D(ctkDICOMDatabase); @@ -933,18 +1290,21 @@ bool ctkDICOMDatabase::fileExistsAndUpToDate(const QString& filePath) } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::isOpen() const { Q_D(const ctkDICOMDatabase); return d->Database.isOpen(); } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::isInMemory() const { Q_D(const ctkDICOMDatabase); return d->DatabaseFileName == ":memory:"; } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID) { Q_D(ctkDICOMDatabase); @@ -1016,11 +1376,12 @@ bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID) this->cleanup(); - d->LastSeriesInstanceUID = ""; + d->resetLastInsertedValues(); return true; } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::cleanup() { Q_D(ctkDICOMDatabase); @@ -1031,6 +1392,7 @@ bool ctkDICOMDatabase::cleanup() return true; } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID) { Q_D(ctkDICOMDatabase); @@ -1053,10 +1415,11 @@ bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID) result = false; } } - d->LastStudyInstanceUID = ""; + d->resetLastInsertedValues(); return result; } +//------------------------------------------------------------------------------ bool ctkDICOMDatabase::removePatient(const QString& patientID) { Q_D(ctkDICOMDatabase); @@ -1079,9 +1442,130 @@ bool ctkDICOMDatabase::removePatient(const QString& patientID) result = false; } } - d->LastPatientID = ""; - d->LastPatientsName = ""; - d->LastPatientsBirthDate = ""; - d->LastPatientUID = -1; + d->resetLastInsertedValues(); return result; } + +/// +/// Code related to the tagCache +/// + +//------------------------------------------------------------------------------ +bool ctkDICOMDatabase::tagCacheExists() +{ + Q_D(ctkDICOMDatabase); + + if (d->TagCacheVerified) + { + return true; + } + + // try to open the database if it's not already open + if ( !(d->TagCacheDatabase.isOpen()) ) + { + qDebug() << "TagCacheDatabase not open\n"; + d->TagCacheDatabase = QSqlDatabase::addDatabase("QSQLITE", "TagCache"); + d->TagCacheDatabase.setDatabaseName(d->TagCacheDatabaseFilename); + if ( !(d->TagCacheDatabase.open()) ) + { + qDebug() << "TagCacheDatabase would not open!\n"; + qDebug() << "TagCacheDatabaseFilename is: " << d->TagCacheDatabaseFilename << "\n"; + return false; + } + + //Disable synchronous writing to make modifications faster + QSqlQuery pragmaSyncQuery(d->TagCacheDatabase); + pragmaSyncQuery.exec("PRAGMA synchronous = OFF"); + pragmaSyncQuery.finish(); + + } + + // check that the table exists + QSqlQuery cacheExists( d->TagCacheDatabase ); + cacheExists.prepare("SELECT * FROM TagCache LIMIT 1"); + bool success = d->loggedExec(cacheExists); + if (success) + { + qDebug() << "TagCacheDatabase verified!\n"; + d->TagCacheVerified = true; + return true; + } + qDebug() << "TagCacheDatabase NOT verified based on table check!\n"; + return false; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMDatabase::initializeTagCache() +{ + Q_D(ctkDICOMDatabase); + + // First, drop any existing table + if ( this->tagCacheExists() ) + { + qDebug() << "TagCacheDatabase drop existing table\n"; + QSqlQuery dropCacheTable( d->TagCacheDatabase ); + dropCacheTable.prepare( "DROP TABLE TagCache" ); + d->loggedExec(dropCacheTable); + } + + // now create a table + qDebug() << "TagCacheDatabase adding table\n"; + QSqlQuery createCacheTable( d->TagCacheDatabase ); + createCacheTable.prepare( + "CREATE TABLE TagCache (SOPInstanceUID, Tag, Value, PRIMARY KEY (SOPInstanceUID, Tag))" ); + bool success = d->loggedExec(createCacheTable); + if (success) + { + d->TagCacheVerified = true; + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMDatabase::cachedTag(const QString sopInstanceUID, const QString tag) +{ + Q_D(ctkDICOMDatabase); + if ( !this->tagCacheExists() ) + { + if ( !this->initializeTagCache() ) + { + return( "" ); + } + } + QSqlQuery selectValue( d->TagCacheDatabase ); + selectValue.prepare( "SELECT Value FROM TagCache WHERE SOPInstanceUID = :sopInstanceUID AND Tag = :tag" ); + selectValue.bindValue(":sopInstanceUID",sopInstanceUID); + selectValue.bindValue(":tag",tag); + d->loggedExec(selectValue); + QString result(""); + if (selectValue.next()) + { + result = selectValue.value(0).toString(); + } + return( result ); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMDatabase::cacheTag(const QString sopInstanceUID, const QString tag, const QString value) +{ + Q_D(ctkDICOMDatabase); + if ( !this->tagCacheExists() ) + { + if ( !this->initializeTagCache() ) + { + return false; + } + } + QString valueToInsert(value); + if (valueToInsert == "") + { + valueToInsert = TagNotInInstance; + } + QSqlQuery insertTag( d->TagCacheDatabase ); + insertTag.prepare( "INSERT OR REPLACE INTO TagCache VALUES(:sopInstanceUID, :tag, :value)" ); + insertTag.bindValue(":sopInstanceUID",sopInstanceUID); + insertTag.bindValue(":tag",tag); + insertTag.bindValue(":value",valueToInsert); + return d->loggedExec(insertTag); +} diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.h b/Libs/DICOM/Core/ctkDICOMDatabase.h index 299f47c107..bd07983f19 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.h +++ b/Libs/DICOM/Core/ctkDICOMDatabase.h @@ -29,6 +29,7 @@ #include "ctkDICOMDataset.h" #include "ctkDICOMCoreExport.h" +class QDateTime; class ctkDICOMDatabasePrivate; class DcmDataset; class ctkDICOMAbstractThumbnailGenerator; @@ -55,6 +56,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject Q_PROPERTY(bool isOpen READ isOpen) Q_PROPERTY(QString lastError READ lastError) Q_PROPERTY(QString databaseFilename READ databaseFilename) + Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache) public: explicit ctkDICOMDatabase(QObject *parent = 0); @@ -100,16 +102,31 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// written to disk at all but instead only kept in memory (and /// thus expires after destruction of this object). /// @param connectionName The database connection name. + /// @param update the schema if it is found to be out of date Q_INVOKABLE virtual void openDatabase(const QString databaseFile, - const QString& connectionName = "DICOM-DB" ); + const QString& connectionName = "DICOM-DB"); /// /// close the database. It must not be used afterwards. Q_INVOKABLE void closeDatabase(); /// - /// delete all data and reinitialize the database. + /// delete all data and (re-)initialize the database. Q_INVOKABLE bool initializeDatabase(const char* schemaFile = ":/dicom/dicom-schema.sql"); + /// updates the database schema and reinserts all existing files + Q_INVOKABLE bool updateSchema(const char* schemaFile = ":/dicom/dicom-schema.sql"); + + /// updates the database schema only if the versions don't match + /// Returns true if schema was updated + Q_INVOKABLE bool updateSchemaIfNeeded(const char* schemaFile = ":/dicom/dicom-schema.sql"); + + /// returns the schema version needed by the current version of this code + Q_INVOKABLE QString schemaVersion(); + + /// returns the schema version for the currently open database + /// in order to support schema updating + Q_INVOKABLE QString schemaVersionLoaded(); + /// /// \brief database accessors Q_INVOKABLE QStringList patients (); @@ -117,7 +134,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject Q_INVOKABLE QStringList seriesForStudy (const QString studyUID); Q_INVOKABLE QStringList filesForSeries (const QString seriesUID); Q_INVOKABLE QString fileForInstance (const QString sopInstanceUID); + Q_INVOKABLE QString instanceForFile (const QString fileName); + Q_INVOKABLE QDateTime insertDateTimeForInstance (const QString fileName); + Q_INVOKABLE QStringList allFiles (); /// /// \brief load the header from a file and allow access to elements /// @param sopInstanceUID A string with the uid for a given instance @@ -129,6 +149,19 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject Q_INVOKABLE QStringList headerKeys (); Q_INVOKABLE QString headerValue (const QString key); + /// + /// \brief application-defined tags of interest + /// This list of tags is added to the internal tag cache during import + /// operations. The list should be prepared by the application as + /// a hint to the database that these tags are likely to be accessed + /// later. Internally, the database will cache the values of these + /// tags so that subsequent calls to fileValue or instanceValue will + /// be able to use the cache rather than re-reading the file. + /// @param tags should be a list of ascii hex group/element tags + /// like "0008,0008" as in the instanceValue and fileValue calls + void setTagsToPrecache(const QStringList tags); + const QStringList tagsToPrecache(); + /// Insert into the database if not already exsting. /// @param dataset The dataset to store into the database. Usually, this is /// is a complete DICOM object, like a complete image. However @@ -144,10 +177,15 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// does only make sense if a full object is received. /// @param @generateThumbnail If true, a thumbnail is generated. /// - Q_INVOKABLE void insert( const ctkDICOMDataset& ctkDataset, bool storeFile, bool generateThumbnail); - void insert ( DcmDataset *dataset, bool storeFile = true, bool generateThumbnail = true); - Q_INVOKABLE void insert ( const QString& filePath, bool storeFile = true, bool generateThumbnail = true, bool createHierarchy = true, const QString& destinationDirectoryName = QString() ); - + Q_INVOKABLE void insert( const ctkDICOMDataset& ctkDataset, + bool storeFile, bool generateThumbnail); + void insert ( DcmDataset *dataset, + bool storeFile = true, bool generateThumbnail = true); + Q_INVOKABLE void insert ( const QString& filePath, + bool storeFile = true, bool generateThumbnail = true, + bool createHierarchy = true, + const QString& destinationDirectoryName = QString() ); + /// Check if file is already in database and up-to-date bool fileExistsAndUpToDate(const QString& filePath); @@ -166,16 +204,43 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// @param key A group,element tag in zero-filled hex /// @param group The group portion of the tag as an integer /// @param element The element portion of the tag as an integer - /// @Returns empty string is element is missing + /// @Returns empty string if element is missing Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const QString tag); Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const unsigned short group, const unsigned short element); Q_INVOKABLE QString fileValue (const QString fileName, const QString tag); Q_INVOKABLE QString fileValue (const QString fileName, const unsigned short group, const unsigned short element); - bool tagToGroupElement (const QString tag, unsigned short& group, unsigned short& element); + Q_INVOKABLE bool tagToGroupElement (const QString tag, unsigned short& group, unsigned short& element); + Q_INVOKABLE QString groupElementToTag (const unsigned short& group, const unsigned short& element); + + /// + /// \brief store values of previously requested instance elements + /// These are meant to be internal methods used by the instanceValue and fileValue + /// methods, but they can be used by calling classes to populate or access + /// instance tag values as needed. + /// @param sopInstanceUID A string with the uid for a given instance + /// (corresponding file will be found via database) + /// @param key A group,element tag in zero-filled hex + /// @Returns empty string if element for uid is missing from cache + /// + /// Lightweight check of tag cache existence (once db check per runtime) + Q_INVOKABLE bool tagCacheExists (); + /// Create a tagCache in the current database. Delete the existing one if it exists. + Q_INVOKABLE bool initializeTagCache (); + /// Return the value of a cached tag + Q_INVOKABLE QString cachedTag (const QString sopInstanceUID, const QString tag); + /// Insert an instance tag's value into to the cache + Q_INVOKABLE bool cacheTag (const QString sopInstanceUID, const QString tag, const QString value); Q_SIGNALS: void databaseChanged(); + /// Indicates that the schema is about to be updated and how many files will be processed + void schemaUpdateStarted(int); + /// Indicates progress in updating schema (int is file number, string is file name) + void schemaUpdateProgress(int); + void schemaUpdateProgress(QString); + /// Indicates schema update finished + void schemaUpdated(); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMDataset.cpp b/Libs/DICOM/Core/ctkDICOMDataset.cpp index 81136f55a3..6a243fa4dc 100644 --- a/Libs/DICOM/Core/ctkDICOMDataset.cpp +++ b/Libs/DICOM/Core/ctkDICOMDataset.cpp @@ -112,11 +112,10 @@ void ctkDICOMDataset::InitializeFromFile(const QString& filename, const Uint32 maxReadLength, const E_FileReadMode readMode) { - Q_UNUSED(maxReadLength); DcmDataset *dataset; DcmFileFormat fileformat; - OFCondition status = fileformat.loadFile(filename.toAscii().data(), readXfer, groupLength, readMode); + OFCondition status = fileformat.loadFile(filename.toAscii().data(), readXfer, groupLength, maxReadLength, readMode); dataset = fileformat.getAndRemoveDataset(); if (!status.good()) diff --git a/Libs/DICOM/Core/ctkDICOMIndexer.cpp b/Libs/DICOM/Core/ctkDICOMIndexer.cpp index 84cea37c51..4c9ad34375 100644 --- a/Libs/DICOM/Core/ctkDICOMIndexer.cpp +++ b/Libs/DICOM/Core/ctkDICOMIndexer.cpp @@ -36,7 +36,7 @@ // ctkDICOM includes #include "ctkLogger.h" #include "ctkDICOMIndexer.h" -#include "ctkDICOMIndexerPrivate.h" +#include "ctkDICOMIndexer_p.h" #include "ctkDICOMDatabase.h" // DCMTK includes @@ -96,7 +96,7 @@ ctkDICOMIndexerPrivate::~ctkDICOMIndexerPrivate() } -void ctkDICOMIndexerPrivate::OnProgress(int progress) +void ctkDICOMIndexerPrivate::OnProgress(int) { Q_Q(ctkDICOMIndexer); @@ -148,56 +148,111 @@ void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& ctkDICOMDatabase, { Q_D(ctkDICOMIndexer); - // currently it is not supported to have multiple - // parallel directory imports so the second call blocks - // - d->DirectoryImportWatcher.waitForFinished(); + QStringList listOfFiles; + QDir directory(directoryName); - const std::string src_directory(directoryName.toStdString()); - - OFList originalDcmtkFileNames; - OFList dcmtkFileNames; - OFStandard::searchDirectoryRecursively( QDir::toNativeSeparators(src_directory.c_str()).toAscii().data(), originalDcmtkFileNames, "", ""); - - int totalNumberOfFiles = originalDcmtkFileNames.size(); + if(directory.exists("DICOMDIR")) + { + addDicomdir(ctkDICOMDatabase,directoryName,destinationDirectoryName); + } + else + { + QDirIterator it(directoryName,QDir::Files,QDirIterator::Subdirectories); + while(it.hasNext()) + { + listOfFiles << it.next(); + } + emit foundFilesToIndex(listOfFiles.count()); + addListOfFiles(ctkDICOMDatabase,listOfFiles,destinationDirectoryName); + } +} - // hack to reverse list of filenames (not neccessary when image loading works correctly) - for ( OFListIterator(OFString) iter = originalDcmtkFileNames.begin(); iter != originalDcmtkFileNames.end(); ++iter ) +//------------------------------------------------------------------------------ +void ctkDICOMIndexer::addListOfFiles(ctkDICOMDatabase& ctkDICOMDatabase, + const QStringList& listOfFiles, + const QString& destinationDirectoryName) +{ + Q_D(ctkDICOMIndexer); + if(!listOfFiles.isEmpty()) { - dcmtkFileNames.push_front( *iter ); + if(d->DirectoryImportWatcher.isRunning()) + { + d->DirectoryImportWatcher.cancel(); + d->DirectoryImportWatcher.waitForFinished(); + } + d->FilesToIndex.append(listOfFiles); + d->DirectoryImportFuture = QtConcurrent::filter(d->FilesToIndex,AddFileFunctor(this,ctkDICOMDatabase,destinationDirectoryName)); + d->DirectoryImportWatcher.setFuture(d->DirectoryImportFuture); } +} - OFListIterator(OFString) iter = dcmtkFileNames.begin(); +//------------------------------------------------------------------------------ +void ctkDICOMIndexer::addDicomdir(ctkDICOMDatabase& ctkDICOMDatabase, + const QString& directoryName, + const QString& destinationDirectoryName + ) +{ + Q_D(ctkDICOMIndexer); + + //Initialize dicomdir with directory path + QString dcmFilePath = directoryName; + dcmFilePath.append("/DICOMDIR"); + DcmDicomDir* dicomDir = new DcmDicomDir(dcmFilePath.toStdString().c_str()); - OFListIterator(OFString) last = dcmtkFileNames.end(); + //Values to store records data at the moment only uid needed + OFString patientsName, studyInstanceUID, seriesInstanceUID, sopInstanceUID, referencedFileName ; - if(iter == last) return; + //Variables for progress operations + QString instanceFilePath; + QStringList listOfInstances; - emit foundFilesToIndex(totalNumberOfFiles); + DcmDirectoryRecord* rootRecord = &(dicomDir->getRootRecord()); + DcmDirectoryRecord* patientRecord = NULL; + DcmDirectoryRecord* studyRecord = NULL; + DcmDirectoryRecord* seriesRecord = NULL; + DcmDirectoryRecord* fileRecord = NULL; - /* iterate over all input filenames */ - int fileNumber = 0; - int currentProgress = -1; - d->Canceled = false; - while (iter != last) + /*Iterate over all records in dicomdir and setup path to the dataset of the filerecord + then insert. the filerecord into the database. + If any UID is missing the record and all of it's subelements won't be added to the database*/ + if(rootRecord != NULL) { - if (d->Canceled) + while (((patientRecord = rootRecord->nextSub(patientRecord)) != NULL) + &&(patientRecord->findAndGetOFString(DCM_PatientName, patientsName).good())) + { + logger.debug( "Reading new Patients:" ); + logger.debug( "Patient's Name: " + QString(patientsName.c_str()) ); + + while (((studyRecord = patientRecord->nextSub(studyRecord)) != NULL) + && (studyRecord->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())) { - break; + logger.debug( "Reading new Studys:" ); + logger.debug( "Studies Name: " + QString(studyInstanceUID.c_str()) ); + + while (((seriesRecord = studyRecord->nextSub(seriesRecord)) != NULL) + &&(seriesRecord->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())) + { + logger.debug( "Reading new Series:" ); + logger.debug( "Series Instance Name: " + QString(seriesInstanceUID.c_str()) ); + + while (((fileRecord = seriesRecord->nextSub(fileRecord)) != NULL) + &&(fileRecord->findAndGetOFStringArray(DCM_ReferencedSOPInstanceUIDInFile, sopInstanceUID).good()) + &&(fileRecord->findAndGetOFStringArray(DCM_ReferencedFileID,referencedFileName).good())) + { + + //Get the filepath of the instance and insert it into a list + instanceFilePath = directoryName; + instanceFilePath.append("/"); + instanceFilePath.append(QString( referencedFileName.c_str() )); + instanceFilePath.replace("\\","/"); + listOfInstances << instanceFilePath; + } + } } - emit indexingFileNumber(++fileNumber); - int newProgress = ( fileNumber * 100 ) / totalNumberOfFiles; - if (newProgress != currentProgress) - { - currentProgress = newProgress; - emit progress( currentProgress ); } - QString filePath((*iter).c_str()); - d->FilesToIndex << filePath; - ++iter; + emit foundFilesToIndex(listOfInstances.count()); + addListOfFiles(ctkDICOMDatabase,listOfInstances,destinationDirectoryName); } - d->DirectoryImportFuture = QtConcurrent::filter(d->FilesToIndex,AddFileFunctor(this,ctkDICOMDatabase,destinationDirectoryName)); - d->DirectoryImportWatcher.setFuture(d->DirectoryImportFuture); } //------------------------------------------------------------------------------ @@ -209,7 +264,6 @@ void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& dicomDatabase, const QSt * Probably this should go to the database class as well * Or we have to extend the interface to make possible what we do here * without using SQL directly - /// get all filenames from the database QSqlQuery allFilesQuery(dicomDatabase.database()); diff --git a/Libs/DICOM/Core/ctkDICOMIndexer.h b/Libs/DICOM/Core/ctkDICOMIndexer.h index f1a41973fb..59306bec85 100644 --- a/Libs/DICOM/Core/ctkDICOMIndexer.h +++ b/Libs/DICOM/Core/ctkDICOMIndexer.h @@ -51,6 +51,25 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMIndexer : public QObject Q_INVOKABLE void addDirectory(ctkDICOMDatabase& database, const QString& directoryName, const QString& destinationDirectoryName = ""); + /// + /// \brief Adds directory to database by using DICOMDIR and optionally copies files to + /// destinationDirectory. + /// Scan the directory using Dcmtk and populate the database with all the + /// DICOM images accordingly. + /// + Q_INVOKABLE void addDicomdir(ctkDICOMDatabase& database, const QString& directoryName, + const QString& destinationDirectoryName = ""); + + /// + /// \brief Adds a QStringList containing the file path to database and optionally copies files to + /// destinationDirectory. + /// + /// Scan the directory using Dcmtk and populate the database with all the + /// DICOM images accordingly. + /// + Q_INVOKABLE void addListOfFiles(ctkDICOMDatabase& database, const QStringList& listOfFiles, + const QString& destinationDirectoryName = ""); + /// /// \brief Adds a file to database and optionally copies the file to /// destinationDirectory. diff --git a/Libs/DICOM/Core/ctkDICOMIndexerPrivate.h b/Libs/DICOM/Core/ctkDICOMIndexer_p.h similarity index 100% rename from Libs/DICOM/Core/ctkDICOMIndexerPrivate.h rename to Libs/DICOM/Core/ctkDICOMIndexer_p.h diff --git a/Libs/DICOM/Core/ctkDICOMModel.cpp b/Libs/DICOM/Core/ctkDICOMModel.cpp index bf8e0661a3..728d205bd4 100644 --- a/Libs/DICOM/Core/ctkDICOMModel.cpp +++ b/Libs/DICOM/Core/ctkDICOMModel.cpp @@ -335,7 +335,7 @@ void ctkDICOMModelPrivate::updateQueries(Node* node)const condition.append(" ( StudyDate BETWEEN \'" + QDate::fromString(this->SearchParameters["StartDate"].toString(), "yyyyMMdd").toString("yyyy-MM-dd") + "\' AND \'" + QDate::fromString(this->SearchParameters["EndDate"].toString(), "yyyyMMdd").toString("yyyy-MM-dd") + "\' ) AND "); } - query = this->generateQuery("StudyInstanceUID as UID, StudyDescription as Name, ModalitiesInStudy as Scan, StudyDate as Date, AccessionNumber as Number, ReferringPhysician as Institution, ReferringPhysician as Referrer, PerformingPhysiciansName as Performer", "Studies", condition + QString("PatientsUID='%1'").arg(node->UID)); + query = this->generateQuery("StudyInstanceUID as UID, StudyDescription as Name, ModalitiesInStudy as Scan, StudyDate as Date, AccessionNumber as Number, InstitutionName as Institution, ReferringPhysician as Referrer, PerformingPhysiciansName as Performer", "Studies", condition + QString("PatientsUID='%1'").arg(node->UID)); logger.debug ( "ctkDICOMModelPrivate::updateQueries for Patient: query is: " + query ); break; case ctkDICOMModel::StudyType: @@ -344,7 +344,7 @@ void ctkDICOMModelPrivate::updateQueries(Node* node)const { condition.append("SeriesDescription LIKE \"%" + this->SearchParameters["Series"].toString() + "%\"" + " AND "); } - query = this->generateQuery("SeriesInstanceUID as UID, SeriesDescription as Name, BodyPartExamined as Scan, SeriesDate as Date, AcquisitionNumber as Number","Series",condition + QString("StudyInstanceUID='%1'").arg(node->UID)); + query = this->generateQuery("SeriesInstanceUID as UID, SeriesDescription as Name, Modality as Age, SeriesNumber as Scan, BodyPartExamined as \"Subject ID\", SeriesDate as Date, AcquisitionNumber as Number","Series",condition + QString("StudyInstanceUID='%1'").arg(node->UID)); logger.debug ( "ctkDICOMModelPrivate::updateQueries for Study: query is: " + query ); break; case ctkDICOMModel::SeriesType: @@ -497,7 +497,16 @@ QVariant ctkDICOMModel::data ( const QModelIndex & dataIndex, int role ) const // invalid). return QString(); } - return d->value(parentIndex, dataIndex.row(), field); + + QVariant dataValue=d->value(parentIndex, dataIndex.row(), field); + if (dataValue.isNull()) + { + if (columnName.compare("Name")==0) + { + return QString("No description"); + } + } + return dataValue; } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index 088a4787d8..a0a57252fa 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -109,7 +109,7 @@ ctkDICOMQueryPrivate::ctkDICOMQueryPrivate() this->Query = new DcmDataset(); this->Port = 0; this->Canceled = false; - this->PreferCGET = true; + this->PreferCGET = false; } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index d67cc258b1..fc182cd1fd 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -219,11 +219,6 @@ bool ctkDICOMRetrievePrivate::initializeSCU( const QString& studyInstanceUID, const RetrieveType retrieveType, DcmDataset *retrieveParameters) { - if ( !this->Database ) - { - logger.error ( "No Database for retrieve transaction" ); - return false; - } // If we like to query another server than before, be sure to disconnect first if (this->SCU.isConnected() && this->ConnectionParamsChanged) diff --git a/Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp index 4e3efdc9e0..45951f2fbb 100644 --- a/Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp @@ -71,6 +71,7 @@ class ctkDICOMAppWidgetPrivate: public Ui_ctkDICOMAppWidget Q_DECLARE_PUBLIC(ctkDICOMAppWidget); ctkDICOMAppWidgetPrivate(ctkDICOMAppWidget* ); + ~ctkDICOMAppWidgetPrivate(); ctkFileDialog* ImportDialog; ctkDICOMQueryRetrieveWidget* QueryRetrieveWidget; @@ -80,6 +81,11 @@ class ctkDICOMAppWidgetPrivate: public Ui_ctkDICOMAppWidget ctkDICOMModel DICOMModel; ctkDICOMFilterProxyModel DICOMProxyModel; QSharedPointer DICOMIndexer; + QProgressDialog *IndexerProgress; + QProgressDialog *UpdateSchemaProgress; + + void showIndexerDialog(); + void showUpdateSchemaDialog(); // used when suspending the ctkDICOMModel QSqlDatabase EmptyDatabase; @@ -97,16 +103,131 @@ ctkDICOMAppWidgetPrivate::ctkDICOMAppWidgetPrivate(ctkDICOMAppWidget* parent): q ThumbnailGenerator = QSharedPointer (new ctkDICOMThumbnailGenerator); DICOMDatabase->setThumbnailGenerator(ThumbnailGenerator.data()); DICOMIndexer = QSharedPointer (new ctkDICOMIndexer); + IndexerProgress = 0; + UpdateSchemaProgress = 0; +} + +ctkDICOMAppWidgetPrivate::~ctkDICOMAppWidgetPrivate() +{ + if ( IndexerProgress ) + { + delete IndexerProgress; + } + if ( UpdateSchemaProgress ) + { + delete UpdateSchemaProgress; + } +} + +void ctkDICOMAppWidgetPrivate::showUpdateSchemaDialog() +{ + Q_Q(ctkDICOMAppWidget); + if (UpdateSchemaProgress == 0) + { + // + // Set up the Update Schema Progress Dialog + // + UpdateSchemaProgress = new QProgressDialog( + q->tr("DICOM Schema Update"), "Cancel", 0, 100, q, + Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + + // We don't want the progress dialog to resize itself, so we bypass the label + // by creating our own + QLabel* progressLabel = new QLabel(q->tr("Initialization...")); + UpdateSchemaProgress->setLabel(progressLabel); +#ifdef Q_WS_MAC + // BUG: avoid deadlock of dialogs on mac + UpdateSchemaProgress->setWindowModality(Qt::NonModal); +#else + UpdateSchemaProgress->setWindowModality(Qt::ApplicationModal); +#endif + UpdateSchemaProgress->setMinimumDuration(0); + UpdateSchemaProgress->setValue(0); + + //q->connect(UpdateSchemaProgress, SIGNAL(canceled()), + // DICOMIndexer.data(), SLOT(cancel())); + + q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateStarted(int)), + UpdateSchemaProgress, SLOT(setMaximum(int))); + q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateProgress(int)), + UpdateSchemaProgress, SLOT(setValue(int))); + q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateProgress(QString)), + progressLabel, SLOT(setText(QString))); + + // close the dialog + q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdated()), + UpdateSchemaProgress, SLOT(close())); + // reset the database to show new data + q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdated()), + &DICOMModel, SLOT(reset())); + // reset the database if canceled + q->connect(UpdateSchemaProgress, SIGNAL(canceled()), + &DICOMModel, SLOT(reset())); + } + UpdateSchemaProgress->show(); +} + +void ctkDICOMAppWidgetPrivate::showIndexerDialog() +{ + Q_Q(ctkDICOMAppWidget); + if (IndexerProgress == 0) + { + // + // Set up the Indexer Progress Dialog + // + IndexerProgress = new QProgressDialog( q->tr("DICOM Import"), "Cancel", 0, 100, q, + Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + + // We don't want the progress dialog to resize itself, so we bypass the label + // by creating our own + QLabel* progressLabel = new QLabel(q->tr("Initialization...")); + IndexerProgress->setLabel(progressLabel); +#ifdef Q_WS_MAC + // BUG: avoid deadlock of dialogs on mac + IndexerProgress->setWindowModality(Qt::NonModal); +#else + IndexerProgress->setWindowModality(Qt::ApplicationModal); +#endif + IndexerProgress->setMinimumDuration(0); + IndexerProgress->setValue(0); + + q->connect(IndexerProgress, SIGNAL(canceled()), + DICOMIndexer.data(), SLOT(cancel())); + + q->connect(DICOMIndexer.data(), SIGNAL(progress(int)), + IndexerProgress, SLOT(setValue(int))); + q->connect(DICOMIndexer.data(), SIGNAL(indexingFilePath(QString)), + progressLabel, SLOT(setText(QString))); + + // close the dialog + q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()), + IndexerProgress, SLOT(close())); + // reset the database to show new data + q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()), + &DICOMModel, SLOT(reset())); + // stop indexing and reset the database if canceled + q->connect(IndexerProgress, SIGNAL(canceled()), + DICOMIndexer.data(), SLOT(cancel())); + q->connect(IndexerProgress, SIGNAL(canceled()), + &DICOMModel, SLOT(reset())); + + // allow users of this widget to know that the process has finished + q->connect(IndexerProgress, SIGNAL(canceled()), + q, SIGNAL(directoryImported())); + q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()), + q, SIGNAL(directoryImported())); + } + IndexerProgress->show(); } //---------------------------------------------------------------------------- // ctkDICOMAppWidget methods //---------------------------------------------------------------------------- -ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent), +ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent), d_ptr(new ctkDICOMAppWidgetPrivate(this)) { - Q_D(ctkDICOMAppWidget); + Q_D(ctkDICOMAppWidget); d->setupUi(this); @@ -186,16 +307,26 @@ ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent), //---------------------------------------------------------------------------- ctkDICOMAppWidget::~ctkDICOMAppWidget() { - Q_D(ctkDICOMAppWidget); + Q_D(ctkDICOMAppWidget); d->QueryRetrieveWidget->deleteLater(); d->ImportDialog->deleteLater(); } +//---------------------------------------------------------------------------- +void ctkDICOMAppWidget::updateDatabaseSchemaIfNeeded() +{ + + Q_D(ctkDICOMAppWidget); + + d->showUpdateSchemaDialog(); + d->DICOMDatabase->updateSchemaIfNeeded(); +} + //---------------------------------------------------------------------------- void ctkDICOMAppWidget::setDatabaseDirectory(const QString& directory) { - Q_D(ctkDICOMAppWidget); + Q_D(ctkDICOMAppWidget); QSettings settings; settings.setValue("DatabaseDirectory", directory); @@ -203,7 +334,7 @@ void ctkDICOMAppWidget::setDatabaseDirectory(const QString& directory) //close the active DICOM database d->DICOMDatabase->closeDatabase(); - + //open DICOM database on the directory QString databaseFileName = directory + QString("/ctkDICOM.sql"); try @@ -216,7 +347,10 @@ void ctkDICOMAppWidget::setDatabaseDirectory(const QString& directory) d->DICOMDatabase->closeDatabase(); return; } - + + // update the database schema if needed and provide progress + this->updateDatabaseSchemaIfNeeded(); + d->DICOMModel.setDatabase(d->DICOMDatabase->database()); d->DICOMModel.setEndLevel(ctkDICOMModel::SeriesType); d->TreeView->resizeColumnToContents(0); @@ -245,6 +379,22 @@ bool ctkDICOMAppWidget::searchWidgetPopUpMode(){ return d->IsSearchWidgetPopUpMode; } +//------------------------------------------------------------------------------ +void ctkDICOMAppWidget::setTagsToPrecache( const QStringList tags) +{ + Q_D(ctkDICOMAppWidget); + d->DICOMDatabase->setTagsToPrecache(tags); +} + +//------------------------------------------------------------------------------ +const QStringList ctkDICOMAppWidget::tagsToPrecache() +{ + Q_D(ctkDICOMAppWidget); + return d->DICOMDatabase->tagsToPrecache(); +} + + + //---------------------------------------------------------------------------- ctkDICOMDatabase* ctkDICOMAppWidget::database(){ Q_D(ctkDICOMAppWidget); @@ -289,7 +439,7 @@ void ctkDICOMAppWidget::onAddToDatabase() void ctkDICOMAppWidget::openImportDialog() { Q_D(ctkDICOMAppWidget); - + d->ImportDialog->show(); d->ImportDialog->raise(); } @@ -336,7 +486,7 @@ void ctkDICOMAppWidget::onRemoveAction() { QString seriesUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString(); d->DICOMDatabase->removeSeries(seriesUID); - } + } else if ( d->DICOMModel.data(index0,ctkDICOMModel::TypeRole) == static_cast(ctkDICOMModel::StudyType)) { QString studyUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString(); @@ -375,13 +525,6 @@ void ctkDICOMAppWidget::resetModel() d->DICOMModel.reset(); } -//---------------------------------------------------------------------------- -void ctkDICOMAppWidget::onProgress(int progress) -{ - Q_UNUSED(progress); - QApplication::processEvents(); -} - //---------------------------------------------------------------------------- void ctkDICOMAppWidget::onThumbnailSelected(const ctkThumbnailLabel& widget) { @@ -430,36 +573,8 @@ void ctkDICOMAppWidget::onImportDirectory(QString directory) { targetDirectory = d->DICOMDatabase->databaseDirectory(); } - QProgressDialog* progress = new QProgressDialog("DICOM Import", "Cancel", 0, 100, this, - Qt::WindowTitleHint | Qt::WindowSystemMenuHint); - // We don't want the progress dialog to resize itself, so we bypass the label - // by creating our own - QLabel* progressLabel = new QLabel(tr("Initialization...")); - progress->setLabel(progressLabel); -#ifdef Q_WS_MAC - // BUG: avoid deadlock of dialogs on mac - progress->setWindowModality(Qt::NonModal); -#else - progress->setWindowModality(Qt::ApplicationModal); -#endif - progress->setMinimumDuration(0); - progress->setValue(0); - progress->show(); - - connect(progress, SIGNAL(canceled()), d->DICOMIndexer.data(), SLOT(cancel())); - connect(d->DICOMIndexer.data(), SIGNAL(indexingFilePath(QString)), - progressLabel, SLOT(setText(QString))); - connect(d->DICOMIndexer.data(), SIGNAL(progress(int)), - progress, SLOT(setValue(int))); - connect(d->DICOMIndexer.data(), SIGNAL(progress(int)), - this, SLOT(onProgress(int))); - - connect(d->DICOMIndexer.data(), SIGNAL(indexingComplete()), - progress, SLOT(close())); - + d->showIndexerDialog(); d->DICOMIndexer->addDirectory(*d->DICOMDatabase,directory,targetDirectory); - - d->DICOMModel.reset(); } } diff --git a/Libs/DICOM/Widgets/ctkDICOMAppWidget.h b/Libs/DICOM/Widgets/ctkDICOMAppWidget.h index e434a9ceac..a918d6575f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMAppWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMAppWidget.h @@ -38,14 +38,28 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMAppWidget : public QWidget Q_PROPERTY(ctkDICOMDatabase* database READ database) Q_PROPERTY(QString databaseDirectory READ databaseDirectory WRITE setDatabaseDirectory) Q_PROPERTY(bool searchWidgetPopUpMode READ searchWidgetPopUpMode WRITE setSearchWidgetPopUpMode) + Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache) public: typedef QWidget Superclass; explicit ctkDICOMAppWidget(QWidget* parent=0); virtual ~ctkDICOMAppWidget(); + /// Directory being used to store the dicom database QString databaseDirectory() const; + /// See ctkDICOMDatabase for description - these accessors + /// delegate to the corresponding routines of the internal + /// instance of the database. + /// @see ctkDICOMDatabase + void setTagsToPrecache(const QStringList tags); + const QStringList tagsToPrecache(); + + /// Updates schema of loaded database to match the one + /// coded by the current version of ctkDICOMDatabase. + /// Also provides a dialog box for progress + void updateDatabaseSchemaIfNeeded(); + /// Setting search widget pop-up mode /// Default value is false. Setting it to true will make /// search widget to be displayed as pop-up widget @@ -66,13 +80,13 @@ public Q_SLOTS: void resumeModel(); void resetModel(); - void onProgress(int); - Q_SIGNALS: /// Emited when directory is changed void databaseDirectoryChanged(const QString&); /// Emited when query/retrieve operation has happened void queryRetrieveFinished(); + /// Emited when the directory import operation has completed + void directoryImported(); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp index ec277b7cdd..f7d98c8cc7 100644 --- a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp @@ -68,6 +68,7 @@ class ctkDICOMQueryRetrieveWidgetPrivate: public Ui_ctkDICOMQueryRetrieveWidget QProgressDialog* ProgressDialog; QString CurrentServer; + bool UseProgressDialog; }; //---------------------------------------------------------------------------- @@ -106,7 +107,7 @@ void ctkDICOMQueryRetrieveWidgetPrivate::init() QObject::connect(this->CancelButton, SIGNAL(clicked()), q, SLOT(cancel())); this->results->setModel(&this->Model); - this->results->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->results->setSelectionMode(QAbstractItemView::NoSelection); this->results->setSelectionBehavior(QAbstractItemView::SelectRows); QObject::connect(this->results->selectionModel(), @@ -146,6 +147,12 @@ QSharedPointer ctkDICOMQueryRetrieveWidget::retrieveDatabase() return d->RetrieveDatabase; } +void ctkDICOMQueryRetrieveWidget::useProgressDialog(bool enable) +{ + Q_D(ctkDICOMQueryRetrieveWidget); + d->UseProgressDialog=enable; +} + //---------------------------------------------------------------------------- void ctkDICOMQueryRetrieveWidget::query() { @@ -163,7 +170,6 @@ void ctkDICOMQueryRetrieveWidget::query() } d->QueriesByStudyUID.clear(); - // for each of the selected server nodes, send the query QProgressDialog progress("Query DICOM servers", "Cancel", 0, 100, this, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); @@ -234,6 +240,7 @@ void ctkDICOMQueryRetrieveWidget::query() { d->Model.setDatabase(d->QueryResultDatabase.database()); } + d->RetrieveButton->setEnabled(d->QueriesByStudyUID.keys().size() != 0); progress.setValue(progress.maximum()); d->ProgressDialog = 0; @@ -250,20 +257,24 @@ void ctkDICOMQueryRetrieveWidget::retrieve() return; } - // for each of the selected server nodes, send the query QProgressDialog progress("Retrieve from DICOM servers", "Cancel", 0, 0, this, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); // We don't want the progress dialog to resize itself, so we bypass the label // by creating our own QLabel* progressLabel = new QLabel(tr("Initialization...")); - progress.setLabel(progressLabel); - d->ProgressDialog = &progress; - progress.setWindowModality(Qt::ApplicationModal); - progress.setMinimumDuration(0); - progress.setValue(0); - progress.setMaximum(0); - progress.setAutoClose(false); - progress.show(); + + // for each of the selected server nodes, send the query + if(d->UseProgressDialog) + { + progress.setLabel(progressLabel); + d->ProgressDialog = &progress; + progress.setWindowModality(Qt::ApplicationModal); + progress.setMinimumDuration(0); + progress.setValue(0); + progress.setMaximum(0); + progress.setAutoClose(false); + progress.show(); + } QMap serverParameters = d->ServerNodeWidget->parameters(); ctkDICOMRetrieve *retrieve = new ctkDICOMRetrieve; @@ -273,16 +284,19 @@ void ctkDICOMQueryRetrieveWidget::retrieve() retrieve->setMoveDestinationAETitle( serverParameters["StorageAETitle"].toString() ); // do the rerieval for each selected series + // that is selected in the tree view foreach( QString studyUID, d->QueriesByStudyUID.keys() ) { - if (progress.wasCanceled()) + if(d->UseProgressDialog) { - break; + if (progress.wasCanceled()) + { + break; + } + progressLabel->setText(QString(tr("Retrieving:\n%1")).arg(studyUID)); + this->updateRetrieveProgress(0); } - progressLabel->setText(QString(tr("Retrieving:\n%1")).arg(studyUID)); - this->updateRetrieveProgress(0); - // Get information which server we want to get the study from and prepare request accordingly ctkDICOMQuery *query = d->QueriesByStudyUID[studyUID]; retrieve->setDatabase( d->RetrieveDatabase ); @@ -295,12 +309,14 @@ void ctkDICOMQueryRetrieveWidget::retrieve() logger.debug("About to retrieve " + studyUID + " from " + d->QueriesByStudyUID[studyUID]->host()); logger.info ( "Starting to retrieve" ); - connect(&progress, SIGNAL(canceled()), retrieve, SLOT(cancel())); - connect(retrieve, SIGNAL(progress(QString)), - progressLabel, SLOT(setText(QString))); - connect(retrieve, SIGNAL(progress(int)), - this, SLOT(updateRetrieveProgress(int))); - + if(d->UseProgressDialog) + { + connect(&progress, SIGNAL(canceled()), retrieve, SLOT(cancel())); + connect(retrieve, SIGNAL(progress(QString)), + progressLabel, SLOT(setText(QString))); + connect(retrieve, SIGNAL(progress(int)), + this, SLOT(updateRetrieveProgress(int))); + } try { // perform the retrieve @@ -316,40 +332,41 @@ void ctkDICOMQueryRetrieveWidget::retrieve() catch (std::exception e) { logger.error ( "Retrieve failed" ); - if ( QMessageBox::question ( this, - tr("Query Retrieve"), tr("Retrieve failed. Keep trying?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) - { - continue; - } - else + if(d->UseProgressDialog) { - break; + if ( QMessageBox::question ( this, + tr("Query Retrieve"), tr("Retrieve failed. Keep trying?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { + continue; + } + else + { + break; + } } } - disconnect(retrieve, SIGNAL(progress(QString)), - progressLabel, SLOT(setText(QString))); - disconnect(retrieve, SIGNAL(progress(int)), - this, SLOT(updateRetrieveProgress(int))); - disconnect(&progress, SIGNAL(canceled()), retrieve, SLOT(cancel())); - - // Store retrieve structure for later use. - // Comment MO: I do not think that makes much sense; you store per study one fat - // structure including an SCU. Also, I switched the code to re-use the retrieve - // SCU in order to not start/stop the association for every study. In general, - // it would make most sense in my opinion to have one SCU for each server you - // like to retrieve from. There is no good reason to have one for each study. - // d->RetrievalsByStudyUID[studyUID] = retrieve; + if(d->UseProgressDialog) + { + disconnect(retrieve, SIGNAL(progress(QString)), + progressLabel, SLOT(setText(QString))); + disconnect(retrieve, SIGNAL(progress(int)), + this, SLOT(updateRetrieveProgress(int))); + disconnect(&progress, SIGNAL(canceled()), retrieve, SLOT(cancel())); + } logger.info ( "Retrieve success" ); } - QString message(tr("Retrieve Process Finished")); - if (retrieve->wasCanceled()) + if(d->UseProgressDialog) { - message = tr("Retrieve Process Canceled"); + QString message(tr("Retrieve Process Finished")); + if (retrieve->wasCanceled()) + { + message = tr("Retrieve Process Canceled"); + } + QMessageBox::information ( this, tr("Query Retrieve"), message ); } - QMessageBox::information ( this, tr("Query Retrieve"), message ); emit studiesRetrieved(d->RetrievalsByStudyUID.keys()); delete retrieve; @@ -424,6 +441,15 @@ void ctkDICOMQueryRetrieveWidget::onSelectionChanged(const QItemSelection &selec Q_D(ctkDICOMQueryRetrieveWidget); logger.debug("Selection change"); - d->RetrieveButton->setEnabled(d->results->selectionModel()->hasSelection()); + // TODO: allow selection of individual studies to retrieve. Requires + // monitoring the selection and mapping to the study list (which is not + // straightforward because the dataroles of patient and series don't + // map directly to studies). + //d->RetrieveButton->setEnabled(d->results->selectionModel()->hasSelection()); } +QMap ctkDICOMQueryRetrieveWidget::getServerParameters() +{ + Q_D(ctkDICOMQueryRetrieveWidget); + return d->ServerNodeWidget->parameters(); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h index eefb14a83c..e585b9e4ed 100644 --- a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h @@ -26,6 +26,9 @@ // Qt includes #include #include +#include +#include +#include // CTK includes @@ -41,9 +44,13 @@ Q_OBJECT; typedef QWidget Superclass; explicit ctkDICOMQueryRetrieveWidget(QWidget* parent=0); virtual ~ctkDICOMQueryRetrieveWidget(); + QMap getServerParameters(); QSharedPointer retrieveDatabase()const; + /// enable or disable ctk progress bars + void useProgressDialog(bool enable); + public Q_SLOTS: void setRetrieveDatabase(QSharedPointer retrieveDatabase); void query(); diff --git a/Libs/PluginFramework/CMakeLists.txt b/Libs/PluginFramework/CMakeLists.txt index c2a0710d70..805a5128d8 100644 --- a/Libs/PluginFramework/CMakeLists.txt +++ b/Libs/PluginFramework/CMakeLists.txt @@ -39,15 +39,15 @@ set(KIT_SRCS ctkPluginFrameworkLauncher.cpp ctkPluginFrameworkListeners.cpp ctkPluginFrameworkListeners_p.h - ctkPluginFrameworkPrivate.cpp - ctkPluginFrameworkPrivate_p.h + ctkPluginFramework_p.cpp + ctkPluginFramework_p.h ctkPluginFrameworkUtil.cpp ctkPluginFrameworkUtil_p.h ctkPluginLocalization.cpp ctkPluginManifest.cpp ctkPluginManifest_p.h - ctkPluginPrivate.cpp - ctkPluginPrivate_p.h + ctkPlugin_p.cpp + ctkPlugin_p.h ctkPlugins.cpp ctkPlugins_p.h ctkPluginStorage_p.h @@ -55,17 +55,17 @@ set(KIT_SRCS ctkPluginStorageSQL_p.h ctkPluginTracker.h ctkPluginTracker.tpp - ctkPluginTrackerPrivate.h - ctkPluginTrackerPrivate.tpp + ctkPluginTracker_p.h + ctkPluginTracker_p.tpp ctkRequirePlugin.cpp ctkRequirePlugin_p.h ctkServiceEvent.cpp ctkServiceException.cpp ctkServiceFactory.h ctkServiceReference.cpp - ctkServiceReferencePrivate.cpp + ctkServiceReference_p.cpp ctkServiceRegistration.cpp - ctkServiceRegistrationPrivate.cpp + ctkServiceRegistration_p.cpp ctkServices.cpp ctkServices_p.h ctkServiceSlotEntry.cpp @@ -73,8 +73,8 @@ set(KIT_SRCS ctkServiceTracker.h ctkServiceTracker.tpp ctkServiceTrackerCustomizer.h - ctkServiceTrackerPrivate.h - ctkServiceTrackerPrivate.tpp + ctkServiceTracker_p.h + ctkServiceTracker_p.tpp ctkTrackedPlugin_p.h ctkTrackedPlugin.tpp ctkTrackedPluginListener_p.h diff --git a/Libs/PluginFramework/ctkLDAPSearchFilter.cpp b/Libs/PluginFramework/ctkLDAPSearchFilter.cpp index d3e549f779..5bd68f6cfc 100644 --- a/Libs/PluginFramework/ctkLDAPSearchFilter.cpp +++ b/Libs/PluginFramework/ctkLDAPSearchFilter.cpp @@ -22,7 +22,7 @@ #include "ctkLDAPSearchFilter.h" #include "ctkLDAPExpr_p.h" -#include "ctkServiceReferencePrivate.h" +#include "ctkServiceReference_p.h" //---------------------------------------------------------------------------- class ctkLDAPSearchFilterData : public QSharedData diff --git a/Libs/PluginFramework/ctkPlugin.cpp b/Libs/PluginFramework/ctkPlugin.cpp index 00c4d39592..51aed709e8 100644 --- a/Libs/PluginFramework/ctkPlugin.cpp +++ b/Libs/PluginFramework/ctkPlugin.cpp @@ -19,19 +19,18 @@ =============================================================================*/ -#include "ctkPlugin.h" +#include +#include "ctkPlugin.h" +#include "ctkPlugin_p.h" #include "ctkPluginContext.h" #include "ctkPluginContext_p.h" #include "ctkPluginFrameworkUtil_p.h" -#include "ctkPluginPrivate_p.h" #include "ctkPluginArchive_p.h" #include "ctkPluginFrameworkContext_p.h" #include "ctkServices_p.h" #include "ctkUtils.h" -#include - //---------------------------------------------------------------------------- ctkPlugin::ctkPlugin() : d_ptr(0) diff --git a/Libs/PluginFramework/ctkPlugin.h b/Libs/PluginFramework/ctkPlugin.h index 860e6a0900..b2dae53ead 100644 --- a/Libs/PluginFramework/ctkPlugin.h +++ b/Libs/PluginFramework/ctkPlugin.h @@ -478,7 +478,7 @@ class CTK_PLUGINFW_EXPORT ctkPlugin { *
  • If this plugin's state was originally ACTIVE, the * updated plugin is started as described in the ctkPlugin::start() * method. If ctkPlugin::start() throws an exception, a Framework - * event of type {@link ctkPluginFrameworkEvent#ERROR} is fired containing the + * event of type {@link ctkPluginFrameworkEvent#PLUGIN_ERROR} is fired containing the * exception. * * @@ -500,7 +500,7 @@ class CTK_PLUGINFW_EXPORT ctkPlugin { *
  • Original plugin is still used; no update occurred. * * - * @param input The QUrl from which to read the new + * @param updateLocation The QUrl from which to read the new * plugin or null to indicate the Framework must create * the URL from this plugin's * {@link ctkPluginConstants#PLUGIN_UPDATELOCATION Plugin-UpdateLocation} diff --git a/Libs/PluginFramework/ctkPluginContext.cpp b/Libs/PluginFramework/ctkPluginContext.cpp index 629bfd997d..abe2219626 100644 --- a/Libs/PluginFramework/ctkPluginContext.cpp +++ b/Libs/PluginFramework/ctkPluginContext.cpp @@ -19,15 +19,15 @@ =============================================================================*/ +#include "ctkPlugin_p.h" #include "ctkPluginContext.h" #include "ctkPluginContext_p.h" - -#include "ctkPluginPrivate_p.h" #include "ctkPluginFrameworkContext_p.h" + #include "ctkServices_p.h" #include "ctkServiceRegistration.h" #include "ctkServiceReference.h" -#include "ctkServiceReferencePrivate.h" +#include "ctkServiceReference_p.h" #include diff --git a/Libs/PluginFramework/ctkPluginContext.h b/Libs/PluginFramework/ctkPluginContext.h index a828d438a2..593be471d2 100644 --- a/Libs/PluginFramework/ctkPluginContext.h +++ b/Libs/PluginFramework/ctkPluginContext.h @@ -646,7 +646,7 @@ class CTK_PLUGINFW_EXPORT ctkPluginContext bool connectFrameworkListener(const QObject* receiver, const char* slot, Qt::ConnectionType type = Qt::QueuedConnection); /** - * Disconnects the specified slot from the context + * Disconnects the specified slot from the context * plugin. * *

    diff --git a/Libs/PluginFramework/ctkPluginFramework.cpp b/Libs/PluginFramework/ctkPluginFramework.cpp index 070feaaca7..de030e0de5 100644 --- a/Libs/PluginFramework/ctkPluginFramework.cpp +++ b/Libs/PluginFramework/ctkPluginFramework.cpp @@ -19,13 +19,12 @@ =============================================================================*/ +#include "ctkPlugin_p.h" +#include "ctkPluginArchive_p.h" +#include "ctkPluginConstants.h" #include "ctkPluginFramework.h" - -#include "ctkPluginFrameworkPrivate_p.h" -#include "ctkPluginPrivate_p.h" +#include "ctkPluginFramework_p.h" #include "ctkPluginFrameworkContext_p.h" -#include "ctkPluginConstants.h" -#include "ctkPluginArchive_p.h" #include "service/event/ctkEvent.h" diff --git a/Libs/PluginFramework/ctkPluginFramework.h b/Libs/PluginFramework/ctkPluginFramework.h index 1d250b365d..221b1ab822 100644 --- a/Libs/PluginFramework/ctkPluginFramework.h +++ b/Libs/PluginFramework/ctkPluginFramework.h @@ -92,18 +92,18 @@ class CTK_PLUGINFW_EXPORT ctkPluginFramework : public ctkPlugin * following ctkPluginFrameworkEvent types may be returned by * this method. *

      - *
    • {@link ctkPluginFrameworkEvent#STOPPED STOPPED} - This Framework has + *
    • {@link ctkPluginFrameworkEvent#FRAMEWORK_STOPPED STOPPED} - This Framework has * been stopped.
    • * - *
    • {@link ctkPluginFrameworkEvent#STOPPED_UPDATE STOPPED_UPDATE} - This + *
    • {@link ctkPluginFrameworkEvent#FRAMEWORK_STOPPED_UPDATE STOPPED_UPDATE} - This * Framework has been updated which has shutdown and will now * restart.
    • * - *
    • {@link ctkPluginFrameworkEvent#ERROR ERROR} - The Framework + *
    • {@link ctkPluginFrameworkEvent#PLUGIN_ERROR ERROR} - The Framework * encountered an error while shutting down or an error has occurred * which forced the framework to shutdown.
    • * - *
    • {@link ctkPluginFrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This + *
    • {@link ctkPluginFrameworkEvent#FRAMEWORK_WAIT_TIMEDOUT WAIT_TIMEDOUT} - This * method has timed out and returned before this Framework has * stopped.
    • *
    @@ -148,13 +148,13 @@ class CTK_PLUGINFW_EXPORT ctkPluginFramework : public ctkPlugin * of this Framework is moved to start level zero (0), as described in the * Start Level Service Specification. Any exceptions that occur * during plugin stopping must be wrapped in a {@link ctkPluginException} and - * then published as a framework event of type {@link ctkPluginFrameworkEvent#ERROR}
  • + * then published as a framework event of type {@link ctkPluginFrameworkEvent#PLUGIN_ERROR} *
  • Unregister all services registered by this Framework.
  • *
  • Event handling is disabled.
  • *
  • This Framework's state is set to {@link #RESOLVED}.
  • *
  • All resources held by this Framework are released. This includes * threads, loaded libraries, open files, etc.
  • - *
  • Notify all threads that are waiting at {@link #waitForStop(long) + *
  • Notify all threads that are waiting at {@link #waitForStop(unsigned long) * waitForStop} that the stop operation has completed.
  • * *

    diff --git a/Libs/PluginFramework/ctkPluginFrameworkContext.cpp b/Libs/PluginFramework/ctkPluginFrameworkContext.cpp index 9c9ce1dcb1..0afbe3766d 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkContext.cpp +++ b/Libs/PluginFramework/ctkPluginFrameworkContext.cpp @@ -20,9 +20,8 @@ =============================================================================*/ #include "ctkPluginFrameworkContext_p.h" - #include "ctkPluginFrameworkUtil_p.h" -#include "ctkPluginFrameworkPrivate_p.h" +#include "ctkPluginFramework_p.h" #include "ctkPluginArchive_p.h" #include "ctkPluginStorageSQL_p.h" #include "ctkPluginConstants.h" @@ -103,12 +102,31 @@ void ctkPluginFrameworkContext::init() foreach(QString preloadLib, preloadLibs) { - QLibrary lib(preloadLib); + QLibrary lib; + QStringList nameAndVersion = preloadLib.split(":"); + + QString libraryName; + if (nameAndVersion.count() == 1) + { + libraryName = nameAndVersion.front(); + lib.setFileName(nameAndVersion.front()); + } + else if (nameAndVersion.count() == 2) + { + libraryName = nameAndVersion.front() + "." + nameAndVersion.back(); + lib.setFileNameAndVersion(nameAndVersion.front(), nameAndVersion.back()); + } + else + { + qWarning() << "Wrong syntax in" << preloadLib << ". Use [:version]. Skipping."; + continue; + } + lib.setLoadHints(loadHints); - log() << "Pre-loading library" << preloadLib << "with hints [" << static_cast(loadHints) << "]"; + log() << "Pre-loading library" << libraryName << "with hints [" << static_cast(loadHints) << "]"; if (!lib.load()) { - qWarning() << "Pre-loading library" << preloadLib << "failed"; + qWarning() << "Pre-loading library" << libraryName << "failed. Check your library search paths."; } } } diff --git a/Libs/PluginFramework/ctkPluginFrameworkEvent.h b/Libs/PluginFramework/ctkPluginFrameworkEvent.h index 99de94d5f7..6e52a37bd4 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkEvent.h +++ b/Libs/PluginFramework/ctkPluginFrameworkEvent.h @@ -177,13 +177,13 @@ class CTK_PLUGINFW_EXPORT ctkPluginFrameworkEvent *

    * The type values are: *

      - *
    • {@link #STARTED} - *
    • {@link #ERROR} - *
    • {@link #WARNING} - *
    • {@link #INFO} - *
    • {@link #STOPPED} - *
    • {@link #STOPPED_UPDATE} - *
    • {@link #WAIT_TIMEDOUT} + *
    • {@link #FRAMEWORK_STARTED} + *
    • {@link #PLUGIN_ERROR} + *
    • {@link #PLUGIN_WARNING} + *
    • {@link #PLUGIN_INFO} + *
    • {@link #FRAMEWORK_STOPPED} + *
    • {@link #FRAMEWORK_STOPPED_UPDATE} + *
    • {@link #FRAMEWORK_WAIT_TIMEDOUT} *
    * * @return The type of state change. diff --git a/Libs/PluginFramework/ctkPluginFrameworkLauncher.h b/Libs/PluginFramework/ctkPluginFrameworkLauncher.h index 3f5bb824f8..da88eaa92e 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkLauncher.h +++ b/Libs/PluginFramework/ctkPluginFrameworkLauncher.h @@ -50,7 +50,7 @@ class CTK_PLUGINFW_EXPORT ctkPluginFrameworkLauncher * *

    * If the framework has already been initialized by a call - * to #install(const QString&) or #start, the + * to #install or #start, the * new properties do not have any effect until the framework * is restarted. * @@ -157,7 +157,7 @@ class CTK_PLUGINFW_EXPORT ctkPluginFrameworkLauncher /** * Add a path to the list of search paths for plugins. * - * When calling #install(const QString&), #start, or + * When calling #install(const QString&, ctkPluginContext*), #start, or * #getPluginPath(const QString&), the plugin is searched in the * paths given as arguments to this method. The least recently added * path is searched first. diff --git a/Libs/PluginFramework/ctkPluginFrameworkListeners.cpp b/Libs/PluginFramework/ctkPluginFrameworkListeners.cpp index 9d15e679d8..c20133297a 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkListeners.cpp +++ b/Libs/PluginFramework/ctkPluginFrameworkListeners.cpp @@ -25,7 +25,7 @@ #include "ctkPluginFrameworkContext_p.h" #include "ctkPluginConstants.h" #include "ctkLDAPExpr_p.h" -#include "ctkServiceReferencePrivate.h" +#include "ctkServiceReference_p.h" #include #include diff --git a/Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp b/Libs/PluginFramework/ctkPluginFramework_p.cpp similarity index 99% rename from Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp rename to Libs/PluginFramework/ctkPluginFramework_p.cpp index f3db994ffa..6b8e084597 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp +++ b/Libs/PluginFramework/ctkPluginFramework_p.cpp @@ -19,12 +19,11 @@ =============================================================================*/ -#include "ctkPluginFrameworkPrivate_p.h" - -#include "ctkPluginFramework.h" #include "ctkPluginConstants.h" #include "ctkPluginContext.h" #include "ctkPluginContext_p.h" +#include "ctkPluginFramework.h" +#include "ctkPluginFramework_p.h" #include "ctkPluginFrameworkContext_p.h" #include "ctkPluginFrameworkUtil_p.h" diff --git a/Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h b/Libs/PluginFramework/ctkPluginFramework_p.h similarity index 99% rename from Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h rename to Libs/PluginFramework/ctkPluginFramework_p.h index fe38473a7e..148f0d65e7 100644 --- a/Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h +++ b/Libs/PluginFramework/ctkPluginFramework_p.h @@ -22,11 +22,11 @@ #ifndef CTKPLUGINFRAMEWORKPRIVATE_P_H #define CTKPLUGINFRAMEWORKPRIVATE_P_H -#include "ctkPluginPrivate_p.h" -#include "ctkPluginFramework.h" - #include +#include "ctkPlugin_p.h" +#include "ctkPluginFramework.h" + class ctkPluginFrameworkContext; /** diff --git a/Libs/PluginFramework/ctkPluginTracker.tpp b/Libs/PluginFramework/ctkPluginTracker.tpp index 24225ca87d..e760da5fa0 100644 --- a/Libs/PluginFramework/ctkPluginTracker.tpp +++ b/Libs/PluginFramework/ctkPluginTracker.tpp @@ -21,7 +21,7 @@ #include "ctkPluginContext.h" -#include "ctkPluginTrackerPrivate.h" +#include "ctkPluginTracker_p.h" #include "ctkTrackedPlugin_p.h" #include diff --git a/Libs/PluginFramework/ctkPluginTrackerPrivate.h b/Libs/PluginFramework/ctkPluginTracker_p.h similarity index 98% rename from Libs/PluginFramework/ctkPluginTrackerPrivate.h rename to Libs/PluginFramework/ctkPluginTracker_p.h index 0be3193233..44a092d31a 100644 --- a/Libs/PluginFramework/ctkPluginTrackerPrivate.h +++ b/Libs/PluginFramework/ctkPluginTracker_p.h @@ -94,6 +94,6 @@ class ctkPluginTrackerPrivate ctkPluginTracker * const q_ptr; }; -#include "ctkPluginTrackerPrivate.tpp" +#include "ctkPluginTracker_p.tpp" #endif // CTKPLUGINTRACKERPRIVATE_H diff --git a/Libs/PluginFramework/ctkPluginTrackerPrivate.tpp b/Libs/PluginFramework/ctkPluginTracker_p.tpp similarity index 100% rename from Libs/PluginFramework/ctkPluginTrackerPrivate.tpp rename to Libs/PluginFramework/ctkPluginTracker_p.tpp diff --git a/Libs/PluginFramework/ctkPluginPrivate.cpp b/Libs/PluginFramework/ctkPlugin_p.cpp similarity index 99% rename from Libs/PluginFramework/ctkPluginPrivate.cpp rename to Libs/PluginFramework/ctkPlugin_p.cpp index acfa29f120..df2345f75e 100644 --- a/Libs/PluginFramework/ctkPluginPrivate.cpp +++ b/Libs/PluginFramework/ctkPlugin_p.cpp @@ -19,8 +19,7 @@ =============================================================================*/ -#include "ctkPluginPrivate_p.h" - +#include "ctkPlugin_p.h" #include "ctkPluginConstants.h" #include "ctkPluginDatabaseException.h" #include "ctkPluginArchive_p.h" @@ -29,9 +28,9 @@ #include "ctkPluginActivator.h" #include "ctkPluginContext_p.h" -#include "ctkServices_p.h" -#include "ctkServiceReferencePrivate.h" +#include "ctkServiceReference_p.h" #include "ctkServiceRegistration.h" +#include "ctkServices_p.h" // for ctk::msecsTo() - remove after switching to Qt 4.7 #include diff --git a/Libs/PluginFramework/ctkPluginPrivate_p.h b/Libs/PluginFramework/ctkPlugin_p.h similarity index 100% rename from Libs/PluginFramework/ctkPluginPrivate_p.h rename to Libs/PluginFramework/ctkPlugin_p.h diff --git a/Libs/PluginFramework/ctkPlugins.cpp b/Libs/PluginFramework/ctkPlugins.cpp index 57e423a82e..c79c6133c9 100644 --- a/Libs/PluginFramework/ctkPlugins.cpp +++ b/Libs/PluginFramework/ctkPlugins.cpp @@ -19,19 +19,18 @@ =============================================================================*/ -#include "ctkPlugins_p.h" +#include -#include "ctkPluginPrivate_p.h" +#include "ctkPlugin_p.h" #include "ctkPluginArchive_p.h" #include "ctkPluginException.h" #include "ctkPluginFrameworkContext_p.h" +#include "ctkPlugins_p.h" #include "ctkVersionRange_p.h" #include #include -#include - //---------------------------------------------------------------------------- void ctkPlugins::checkIllegalState() const { diff --git a/Libs/PluginFramework/ctkRequirePlugin.cpp b/Libs/PluginFramework/ctkRequirePlugin.cpp index 1ec3b72253..ca1ea1d88c 100644 --- a/Libs/PluginFramework/ctkRequirePlugin.cpp +++ b/Libs/PluginFramework/ctkRequirePlugin.cpp @@ -19,10 +19,9 @@ =============================================================================*/ -#include "ctkRequirePlugin_p.h" - +#include "ctkPlugin_p.h" #include "ctkPluginConstants.h" -#include "ctkPluginPrivate_p.h" +#include "ctkRequirePlugin_p.h" //---------------------------------------------------------------------------- ctkRequirePlugin::ctkRequirePlugin(ctkPluginPrivate* requestor, diff --git a/Libs/PluginFramework/ctkServiceReference.cpp b/Libs/PluginFramework/ctkServiceReference.cpp index 7ba29774ac..470431f0de 100644 --- a/Libs/PluginFramework/ctkServiceReference.cpp +++ b/Libs/PluginFramework/ctkServiceReference.cpp @@ -19,16 +19,16 @@ =============================================================================*/ -#include "ctkServiceReference.h" -#include "ctkServiceReferencePrivate.h" -#include "ctkServiceRegistrationPrivate.h" -#include "ctkPluginPrivate_p.h" -#include "ctkPluginConstants.h" - #include #include #include +#include "ctkPlugin_p.h" +#include "ctkPluginConstants.h" +#include "ctkServiceReference.h" +#include "ctkServiceReference_p.h" +#include "ctkServiceRegistration_p.h" + //---------------------------------------------------------------------------- ctkServiceReference::ctkServiceReference() : d_ptr(new ctkServiceReferencePrivate(0)) diff --git a/Libs/PluginFramework/ctkServiceReferencePrivate.cpp b/Libs/PluginFramework/ctkServiceReference_p.cpp similarity index 98% rename from Libs/PluginFramework/ctkServiceReferencePrivate.cpp rename to Libs/PluginFramework/ctkServiceReference_p.cpp index 1a6da252e1..e780003926 100644 --- a/Libs/PluginFramework/ctkServiceReferencePrivate.cpp +++ b/Libs/PluginFramework/ctkServiceReference_p.cpp @@ -19,19 +19,18 @@ =============================================================================*/ -#include "ctkServiceReferencePrivate.h" +#include "ctkServiceReference_p.h" #include #include +#include "ctkPlugin_p.h" #include "ctkPluginConstants.h" +#include "ctkPluginFrameworkContext_p.h" #include "ctkServiceFactory.h" #include "ctkServiceException.h" -#include "ctkPluginPrivate_p.h" - #include "ctkServices_p.h" -#include "ctkServiceRegistrationPrivate.h" -#include "ctkPluginFrameworkContext_p.h" +#include "ctkServiceRegistration_p.h" //---------------------------------------------------------------------------- ctkServiceReferencePrivate::ctkServiceReferencePrivate(ctkServiceRegistrationPrivate* reg) diff --git a/Libs/PluginFramework/ctkServiceReferencePrivate.h b/Libs/PluginFramework/ctkServiceReference_p.h similarity index 100% rename from Libs/PluginFramework/ctkServiceReferencePrivate.h rename to Libs/PluginFramework/ctkServiceReference_p.h diff --git a/Libs/PluginFramework/ctkServiceRegistration.cpp b/Libs/PluginFramework/ctkServiceRegistration.cpp index 96dd8dc65f..4df89a4ea4 100644 --- a/Libs/PluginFramework/ctkServiceRegistration.cpp +++ b/Libs/PluginFramework/ctkServiceRegistration.cpp @@ -19,18 +19,17 @@ =============================================================================*/ -#include "ctkServiceRegistration.h" -#include "ctkServiceRegistrationPrivate.h" +#include + #include "ctkPluginFrameworkContext_p.h" -#include "ctkPluginPrivate_p.h" +#include "ctkPlugin_p.h" #include "ctkPluginConstants.h" - -#include "ctkServices_p.h" #include "ctkServiceFactory.h" +#include "ctkServiceRegistration.h" +#include "ctkServiceRegistration_p.h" +#include "ctkServices_p.h" #include "ctkServiceSlotEntry_p.h" -#include - #include //---------------------------------------------------------------------------- diff --git a/Libs/PluginFramework/ctkServiceRegistrationPrivate.cpp b/Libs/PluginFramework/ctkServiceRegistration_p.cpp similarity index 97% rename from Libs/PluginFramework/ctkServiceRegistrationPrivate.cpp rename to Libs/PluginFramework/ctkServiceRegistration_p.cpp index 582808c97a..ab8a0e0932 100644 --- a/Libs/PluginFramework/ctkServiceRegistrationPrivate.cpp +++ b/Libs/PluginFramework/ctkServiceRegistration_p.cpp @@ -19,7 +19,7 @@ =============================================================================*/ -#include "ctkServiceRegistrationPrivate.h" +#include "ctkServiceRegistration_p.h" //---------------------------------------------------------------------------- ctkServiceRegistrationPrivate::ctkServiceRegistrationPrivate( diff --git a/Libs/PluginFramework/ctkServiceRegistrationPrivate.h b/Libs/PluginFramework/ctkServiceRegistration_p.h similarity index 100% rename from Libs/PluginFramework/ctkServiceRegistrationPrivate.h rename to Libs/PluginFramework/ctkServiceRegistration_p.h diff --git a/Libs/PluginFramework/ctkServiceTracker.tpp b/Libs/PluginFramework/ctkServiceTracker.tpp index 8bf2abe0f8..2a243acc0f 100644 --- a/Libs/PluginFramework/ctkServiceTracker.tpp +++ b/Libs/PluginFramework/ctkServiceTracker.tpp @@ -20,7 +20,7 @@ =============================================================================*/ -#include "ctkServiceTrackerPrivate.h" +#include "ctkServiceTracker_p.h" #include "ctkTrackedService_p.h" #include "ctkServiceException.h" #include "ctkPluginConstants.h" diff --git a/Libs/PluginFramework/ctkServiceTrackerPrivate.h b/Libs/PluginFramework/ctkServiceTracker_p.h similarity index 99% rename from Libs/PluginFramework/ctkServiceTrackerPrivate.h rename to Libs/PluginFramework/ctkServiceTracker_p.h index 93d41fa9da..8a3751c333 100644 --- a/Libs/PluginFramework/ctkServiceTrackerPrivate.h +++ b/Libs/PluginFramework/ctkServiceTracker_p.h @@ -167,6 +167,6 @@ class ctkServiceTrackerPrivate }; -#include "ctkServiceTrackerPrivate.tpp" +#include "ctkServiceTracker_p.tpp" #endif // CTKSERVICETRACKERPRIVATE_H diff --git a/Libs/PluginFramework/ctkServiceTrackerPrivate.tpp b/Libs/PluginFramework/ctkServiceTracker_p.tpp similarity index 100% rename from Libs/PluginFramework/ctkServiceTrackerPrivate.tpp rename to Libs/PluginFramework/ctkServiceTracker_p.tpp diff --git a/Libs/PluginFramework/ctkServices.cpp b/Libs/PluginFramework/ctkServices.cpp index dccd5b9b5c..4beb41ad6a 100644 --- a/Libs/PluginFramework/ctkServices.cpp +++ b/Libs/PluginFramework/ctkServices.cpp @@ -31,7 +31,7 @@ #include "ctkPluginConstants.h" #include "ctkPluginFrameworkContext_p.h" #include "ctkServiceException.h" -#include "ctkServiceRegistrationPrivate.h" +#include "ctkServiceRegistration_p.h" #include "ctkLDAPExpr_p.h" //---------------------------------------------------------------------------- diff --git a/Libs/PluginFramework/ctkServices_p.h b/Libs/PluginFramework/ctkServices_p.h index d081515fdc..7eab76d73e 100644 --- a/Libs/PluginFramework/ctkServices_p.h +++ b/Libs/PluginFramework/ctkServices_p.h @@ -28,8 +28,8 @@ #include #include +#include "ctkPlugin_p.h" #include "ctkServiceRegistration.h" -#include "ctkPluginPrivate_p.h" /** diff --git a/Libs/Testing/CMake/ctkMacroGenerateMocs.cmake b/Libs/Testing/CMake/ctkMacroGenerateMocs.cmake deleted file mode 100644 index fde7085195..0000000000 --- a/Libs/Testing/CMake/ctkMacroGenerateMocs.cmake +++ /dev/null @@ -1,11 +0,0 @@ - -# QT4_GENERATE_MOCS(inputfile1 [inputfile2 ...]) - -macro(QT4_GENERATE_MOCS) - foreach(file ${ARGN}) - set(moc_file moc_${file}) - QT4_GENERATE_MOC(${file} ${moc_file}) - macro_add_file_dependencies(${file} ${CMAKE_CURRENT_BINARY_DIR}/${moc_file}) - endforeach() -endmacro() - diff --git a/Libs/Testing/CMakeLists.txt b/Libs/Testing/CMakeLists.txt index c26d4bd0af..9cf6d82f6b 100644 --- a/Libs/Testing/CMakeLists.txt +++ b/Libs/Testing/CMakeLists.txt @@ -1,8 +1,5 @@ project(CTKTesting) -# CMake Macros -include(CMake/ctkMacroGenerateMocs.cmake) - if(CTK_USE_QTTESTING) include(../QtTesting/CMake/ctkQtTesting.cmake) endif() diff --git a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest1.cpp b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest1.cpp index b769e833e4..04aa26afd0 100644 --- a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest1.cpp +++ b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest1.cpp @@ -65,7 +65,8 @@ int ctkVTKHistogramTest1( int argc, char * argv []) //------Test setDataArray-------------------------- int dataType = VTK_CHAR; - vtkSmartPointer newDataArray = vtkDataArray::CreateDataArray(dataType); + vtkSmartPointer newDataArray; + newDataArray.TakeReference(vtkDataArray::CreateDataArray(dataType)); defaultHistogram.setDataArray(newDataArray); if (defaultHistogram.dataArray() != newDataArray) { @@ -79,7 +80,7 @@ int ctkVTKHistogramTest1( int argc, char * argv []) defaultHistogram.build(); dataType = VTK_INT; - newDataArray = vtkDataArray::CreateDataArray(dataType); + newDataArray.TakeReference(vtkDataArray::CreateDataArray(dataType)); newDataArray->SetNumberOfComponents(1); newDataArray->InsertNextTuple1(50); newDataArray->InsertNextTuple1(143); @@ -98,7 +99,7 @@ int ctkVTKHistogramTest1( int argc, char * argv []) defaultHistogram.build(); dataType = VTK_FLOAT; - newDataArray = vtkDataArray::CreateDataArray(dataType); + newDataArray.TakeReference(vtkDataArray::CreateDataArray(dataType)); defaultHistogram.setDataArray(newDataArray); if (defaultHistogram.dataArray() != newDataArray) { diff --git a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest2.cpp b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest2.cpp index 54b97d973a..15e1488a72 100644 --- a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest2.cpp +++ b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest2.cpp @@ -25,7 +25,8 @@ int ctkVTKHistogramTest2( int argc, char * argv []) //------Test build-------------------------------- ctkVTKHistogram rgbHistogram; - vtkSmartPointer rgbDataArray = vtkDataArray::CreateDataArray(VTK_INT); + vtkSmartPointer rgbDataArray; + rgbDataArray.TakeReference(vtkDataArray::CreateDataArray(VTK_INT)); rgbDataArray->SetNumberOfComponents(3); rgbDataArray->InsertNextTuple3( 0, 50, 0); rgbDataArray->InsertNextTuple3(1000, 143, -1412); diff --git a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest3.cpp b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest3.cpp index 2760a1a411..cd30003baa 100644 --- a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest3.cpp +++ b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest3.cpp @@ -25,7 +25,8 @@ int ctkVTKHistogramTest3( int argc, char * argv []) //------Test build-------------------------------- ctkVTKHistogram histogram; - vtkSmartPointer dataArray = vtkDataArray::CreateDataArray(VTK_CHAR); + vtkSmartPointer dataArray; + dataArray.TakeReference(vtkDataArray::CreateDataArray(VTK_CHAR)); dataArray->InsertNextTuple1(0); dataArray->InsertNextTuple1(0); dataArray->InsertNextTuple1(0); diff --git a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest4.cpp b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest4.cpp index 19495fa743..a9919c25be 100644 --- a/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest4.cpp +++ b/Libs/Visualization/VTK/Core/Testing/Cpp/ctkVTKHistogramTest4.cpp @@ -12,6 +12,7 @@ // STD includes #include #include +#include int ctkVTKHistogramTest4( int argc, char * argv []) { @@ -25,12 +26,21 @@ int ctkVTKHistogramTest4( int argc, char * argv []) //------Test build-------------------------------- ctkVTKHistogram histogram; - vtkSmartPointer dataArray = vtkDataArray::CreateDataArray(VTK_FLOAT); + vtkSmartPointer dataArray; + dataArray.TakeReference(vtkDataArray::CreateDataArray(VTK_FLOAT)); dataArray->InsertNextTuple1( 10.001); dataArray->InsertNextTuple1( -0.231); dataArray->InsertNextTuple1( 220.0001); dataArray->InsertNextTuple1(1234.0); dataArray->InsertNextTuple1(220.0); + if(std::numeric_limits::has_quiet_NaN) + { + // These should be ignored. + const float positiveNaN = std::numeric_limits::quiet_NaN(); + dataArray->InsertNextTuple1(positiveNaN); + const float negativeNaN = - positiveNaN; + dataArray->InsertNextTuple1(negativeNaN); + } histogram.setDataArray(dataArray); if (histogram.dataArray() != dataArray) { diff --git a/Libs/Visualization/VTK/Core/ctkVTKHistogram.cpp b/Libs/Visualization/VTK/Core/ctkVTKHistogram.cpp index a2615cf8f0..493a0646e5 100644 --- a/Libs/Visualization/VTK/Core/ctkVTKHistogram.cpp +++ b/Libs/Visualization/VTK/Core/ctkVTKHistogram.cpp @@ -296,8 +296,8 @@ void populateIrregularBins(vtkIntArray* bins, const ctkVTKHistogram* histogram) ptr += component; for (; ptr < endPtr; ptr += componentNumber) { - if (std::numeric_limits::has_quiet_NaN && - std::numeric_limits::quiet_NaN() == *ptr) + if (std::numeric_limits::has_quiet_NaN && + vtkMath::IsNan(*ptr)) { continue; } diff --git a/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.cpp b/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.cpp index 893bb57d0c..1a6f70650c 100644 --- a/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.cpp +++ b/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.cpp @@ -56,6 +56,16 @@ void ctkVTKConnectionFactory::setInstance(ctkVTKConnectionFactory* newInstance) Self::Instance = newInstance; } +//----------------------------------------------------------------------------- +ctkVTKConnectionFactory::ctkVTKConnectionFactory() +{ +} + +//----------------------------------------------------------------------------- +ctkVTKConnectionFactory::~ctkVTKConnectionFactory() +{ +} + //----------------------------------------------------------------------------- ctkVTKConnection* ctkVTKConnectionFactory::createConnection(ctkVTKObjectEventsObserver* parent)const { diff --git a/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.h b/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.h index 30c4a9633b..12008410b8 100644 --- a/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.h +++ b/Libs/Visualization/VTK/Core/ctkVTKObjectEventsObserver.h @@ -54,6 +54,8 @@ class CTK_VISUALIZATION_VTK_CORE_EXPORT ctkVTKConnectionFactory virtual ctkVTKConnection* createConnection(ctkVTKObjectEventsObserver*)const; protected: + ctkVTKConnectionFactory(); + virtual ~ctkVTKConnectionFactory(); CTK_SINGLETON_DECLARE(ctkVTKConnectionFactory) }; CTK_SINGLETON_DECLARE_INITIALIZER(CTK_VISUALIZATION_VTK_CORE_EXPORT, ctkVTKConnectionFactory) diff --git a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp index f4e820f53c..8ea095b569 100644 --- a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp +++ b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp @@ -48,6 +48,9 @@ ctkVTKAbstractViewPrivate::ctkVTKAbstractViewPrivate(ctkVTKAbstractView& object) this->CornerAnnotation = vtkSmartPointer::New(); this->RequestTimer = 0; this->RenderEnabled = true; + this->FPSVisible = false; + this->FPSTimer = 0; + this->FPS = 0; } // -------------------------------------------------------------------------- @@ -68,6 +71,11 @@ void ctkVTKAbstractViewPrivate::init() QObject::connect(this->RequestTimer, SIGNAL(timeout()), q, SLOT(forceRender())); + this->FPSTimer = new QTimer(q); + this->FPSTimer->setInterval(1000); + QObject::connect(this->FPSTimer, SIGNAL(timeout()), + q, SLOT(updateFPS())); + this->setupCornerAnnotation(); this->setupRendering(); @@ -376,3 +384,53 @@ bool ctkVTKAbstractView::gradientBackground()const vtkRenderer* firstRenderer = d->firstRenderer(); return firstRenderer ? firstRenderer->GetGradientBackground() : false; } + +//---------------------------------------------------------------------------- +void ctkVTKAbstractView::setFPSVisible(bool show) +{ + Q_D(ctkVTKAbstractView); + if (d->FPSVisible == show) + { + return; + } + d->FPSVisible = show; + vtkRenderer* renderer = d->firstRenderer(); + if (d->FPSVisible) + { + d->FPSTimer->start(); + qvtkConnect(renderer, + vtkCommand::EndEvent, this, SLOT(onRender())); + } + else + { + d->FPSTimer->stop(); + qvtkDisconnect(renderer, + vtkCommand::EndEvent, this, SLOT(onRender())); + d->CornerAnnotation->SetText(1, ""); + } +} + +//---------------------------------------------------------------------------- +bool ctkVTKAbstractView::isFPSVisible()const +{ + Q_D(const ctkVTKAbstractView); + return d->FPSVisible; +} + +//---------------------------------------------------------------------------- +void ctkVTKAbstractView::onRender() +{ + Q_D(ctkVTKAbstractView); + ++d->FPS; +} + +//---------------------------------------------------------------------------- +void ctkVTKAbstractView::updateFPS() +{ + Q_D(ctkVTKAbstractView); + vtkRenderer* renderer = d->firstRenderer(); + double lastRenderTime = renderer ? renderer->GetLastRenderTimeInSeconds() : 0.; + QString fpsString = tr("FPS: %1(%2s)").arg(d->FPS).arg(lastRenderTime); + d->FPS = 0; + d->CornerAnnotation->SetText(1, fpsString.toLatin1()); +} diff --git a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h index 35f2fcbe92..1ccd1875a5 100644 --- a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h +++ b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h @@ -47,6 +47,10 @@ class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKAbstractView : public QWidget Q_PROPERTY(QColor backgroundColor2 READ backgroundColor2 WRITE setBackgroundColor) Q_PROPERTY(bool gradientBackground READ gradientBackground WRITE setGradientBackground) Q_PROPERTY(bool renderEnabled READ renderEnabled WRITE setRenderEnabled) + /// This property controls whether a corner annotation is visible with the + /// last FPS value. + /// false by default. + Q_PROPERTY(bool fpsVisible READ isFPSVisible WRITE setFPSVisible) public: typedef QWidget Superclass; @@ -82,6 +86,9 @@ public Q_SLOTS: /// Set corner annotation \a text virtual void setCornerAnnotationText(const QString& text); + /// Show/Hide the FPS annotation + void setFPSVisible(bool show); + public: /// Get underlying RenderWindow Q_INVOKABLE vtkRenderWindow* renderWindow()const; @@ -116,11 +123,21 @@ public Q_SLOTS: /// Return if rendering is enabled bool renderEnabled() const; + /// Return true if the FPS annotation is visible, false otherwise. + bool isFPSVisible() const; + + /// Return the current FPS + double fps()const; + virtual QSize minimumSizeHint()const; virtual QSize sizeHint()const; virtual bool hasHeightForWidth()const; virtual int heightForWidth(int width)const; +protected Q_SLOTS: + void onRender(); + void updateFPS(); + protected: QScopedPointer d_ptr; ctkVTKAbstractView(ctkVTKAbstractViewPrivate* pimpl, QWidget* parent); diff --git a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h index 0b12ec1ce1..910530a1a6 100644 --- a/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h +++ b/Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h @@ -62,6 +62,9 @@ class ctkVTKAbstractViewPrivate : public QObject QTimer* RequestTimer; QTime RequestTime; bool RenderEnabled; + bool FPSVisible; + QTimer* FPSTimer; + int FPS; vtkSmartPointer CornerAnnotation; }; diff --git a/Libs/Widgets/CMakeLists.txt b/Libs/Widgets/CMakeLists.txt index 7fc9b353fb..ba9965bc3e 100644 --- a/Libs/Widgets/CMakeLists.txt +++ b/Libs/Widgets/CMakeLists.txt @@ -108,6 +108,11 @@ set(KIT_SRCS ctkModalityWidget.h ctkPathLineEdit.cpp ctkPathLineEdit.h + ctkPathListButtonsWidget.cpp + ctkPathListButtonsWidget.h + ctkPathListButtonsWidget_p.h + ctkPathListWidget.cpp + ctkPathListWidget.h ctkPixmapIconEngine.cpp ctkPixmapIconEngine.h ctkPopupWidget.cpp @@ -225,7 +230,8 @@ set(KIT_MOC_SRCS ctkMenuComboBox_p.h ctkMessageBox.h ctkModalityWidget.h - ctkPathLineEdit.h + ctkPathListButtonsWidget.h + ctkPathListButtonsWidget_p.h ctkPopupWidget.h ctkPopupWidget_p.h ctkQImageView.h @@ -261,6 +267,11 @@ set(KIT_MOC_SRCS ctkWorkflowWidgetStep_p.h ) +QT4_GENERATE_MOCS( + ctkPathLineEdit.h + ctkPathListWidget.h +) + # UI files set(KIT_UI_FORMS Resources/UI/ctkAddRemoveComboBox.ui @@ -269,6 +280,7 @@ set(KIT_UI_FORMS Resources/UI/ctkErrorLogWidget.ui Resources/UI/ctkMaterialPropertyWidget.ui Resources/UI/ctkModalityWidget.ui + Resources/UI/ctkPathListButtonsWidget.ui Resources/UI/ctkScreenshotDialog.ui Resources/UI/ctkSettingsDialog.ui Resources/UI/ctkSliderWidget.ui diff --git a/Libs/Widgets/Documentation/CTKWidgets.dox b/Libs/Widgets/Documentation/CTKWidgets.dox index 81995e913e..026baf3c10 100644 --- a/Libs/Widgets/Documentation/CTKWidgets.dox +++ b/Libs/Widgets/Documentation/CTKWidgets.dox @@ -1,6 +1,6 @@ /** -\defgroup Widgets +\defgroup Widgets Widgets \ingroup Libs \ingroup Project_Widgets diff --git a/Libs/Widgets/Plugins/CMakeLists.txt b/Libs/Widgets/Plugins/CMakeLists.txt index f36274e0ca..db3d18d80c 100644 --- a/Libs/Widgets/Plugins/CMakeLists.txt +++ b/Libs/Widgets/Plugins/CMakeLists.txt @@ -64,6 +64,10 @@ set(PLUGIN_SRCS ctkModalityWidgetPlugin.h ctkPathLineEditPlugin.cpp ctkPathLineEditPlugin.h + ctkPathListButtonsWidgetPlugin.cpp + ctkPathListButtonsWidgetPlugin.h + ctkPathListWidgetPlugin.cpp + ctkPathListWidgetPlugin.h ctkPopupWidgetPlugin.cpp ctkPopupWidgetPlugin.h ctkRangeSliderPlugin.cpp @@ -120,6 +124,8 @@ set(PLUGIN_MOC_SRCS ctkMenuButtonPlugin.h ctkModalityWidgetPlugin.h ctkPathLineEditPlugin.h + ctkPathListButtonsWidgetPlugin.h + ctkPathListWidgetPlugin.h ctkPopupWidgetPlugin.h ctkRangeSliderPlugin.h ctkRangeWidgetPlugin.h diff --git a/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.cpp b/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.cpp new file mode 100644 index 0000000000..a18bc114a4 --- /dev/null +++ b/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.cpp @@ -0,0 +1,70 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// CTK includes +#include "ctkPathListButtonsWidgetPlugin.h" +#include "ctkPathListButtonsWidget.h" + +//----------------------------------------------------------------------------- +ctkPathListButtonsWidgetPlugin::ctkPathListButtonsWidgetPlugin(QObject* pluginParent) + : QObject(pluginParent) +{ + +} + +//----------------------------------------------------------------------------- +QWidget *ctkPathListButtonsWidgetPlugin::createWidget(QWidget* parentForWidget) +{ + ctkPathListButtonsWidget* newWidget = new ctkPathListButtonsWidget(parentForWidget); + return newWidget; +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidgetPlugin::domXml() const +{ + return "\n" + "\n"; +} + +// -------------------------------------------------------------------------- +QIcon ctkPathListButtonsWidgetPlugin::icon() const +{ + return QIcon(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidgetPlugin::includeFile() const +{ + return "ctkPathListButtonsWidget.h"; +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidgetPlugin::isContainer() const +{ + return false; +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidgetPlugin::name() const +{ + return "ctkPathListButtonsWidget"; +} diff --git a/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.h b/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.h new file mode 100644 index 0000000000..21b86872b2 --- /dev/null +++ b/Libs/Widgets/Plugins/ctkPathListButtonsWidgetPlugin.h @@ -0,0 +1,46 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkPathListButtonsWidgetPlugin_h +#define __ctkPathListButtonsWidgetPlugin_h + +// CTK includes +#include "ctkWidgetsAbstractPlugin.h" + +class CTK_WIDGETS_PLUGINS_EXPORT ctkPathListButtonsWidgetPlugin : + public QObject, + public ctkWidgetsAbstractPlugin +{ + Q_OBJECT + +public: + ctkPathListButtonsWidgetPlugin(QObject *_parent = 0); + + QWidget *createWidget(QWidget *_parent); + QString domXml() const; + QIcon icon() const; + QString includeFile() const; + bool isContainer() const; + QString name() const; + +}; + +#endif diff --git a/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.cpp b/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.cpp new file mode 100644 index 0000000000..d9a743d9ab --- /dev/null +++ b/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.cpp @@ -0,0 +1,70 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// CTK includes +#include "ctkPathListWidgetPlugin.h" +#include "ctkPathListWidget.h" + +//----------------------------------------------------------------------------- +ctkPathListWidgetPlugin::ctkPathListWidgetPlugin(QObject* pluginParent) + : QObject(pluginParent) +{ + +} + +//----------------------------------------------------------------------------- +QWidget *ctkPathListWidgetPlugin::createWidget(QWidget* parentForWidget) +{ + ctkPathListWidget* newWidget = new ctkPathListWidget(parentForWidget); + return newWidget; +} + +//----------------------------------------------------------------------------- +QString ctkPathListWidgetPlugin::domXml() const +{ + return "\n" + "\n"; +} + +// -------------------------------------------------------------------------- +QIcon ctkPathListWidgetPlugin::icon() const +{ + return QIcon(":/Icons/listview.png"); +} + +//----------------------------------------------------------------------------- +QString ctkPathListWidgetPlugin::includeFile() const +{ + return "ctkPathListWidget.h"; +} + +//----------------------------------------------------------------------------- +bool ctkPathListWidgetPlugin::isContainer() const +{ + return false; +} + +//----------------------------------------------------------------------------- +QString ctkPathListWidgetPlugin::name() const +{ + return "ctkPathListWidget"; +} diff --git a/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.h b/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.h new file mode 100644 index 0000000000..a72bb66876 --- /dev/null +++ b/Libs/Widgets/Plugins/ctkPathListWidgetPlugin.h @@ -0,0 +1,46 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkPathListWidgetPlugin_h +#define __ctkPathListWidgetPlugin_h + +// CTK includes +#include "ctkWidgetsAbstractPlugin.h" + +class CTK_WIDGETS_PLUGINS_EXPORT ctkPathListWidgetPlugin : + public QObject, + public ctkWidgetsAbstractPlugin +{ + Q_OBJECT + +public: + ctkPathListWidgetPlugin(QObject *_parent = 0); + + QWidget *createWidget(QWidget *_parent); + QString domXml() const; + QIcon icon() const; + QString includeFile() const; + bool isContainer() const; + QString name() const; + +}; + +#endif diff --git a/Libs/Widgets/Plugins/ctkWidgetsPlugins.h b/Libs/Widgets/Plugins/ctkWidgetsPlugins.h index 77f6d3a0f4..851ca8bf00 100644 --- a/Libs/Widgets/Plugins/ctkWidgetsPlugins.h +++ b/Libs/Widgets/Plugins/ctkWidgetsPlugins.h @@ -50,6 +50,8 @@ #include "ctkMenuButtonPlugin.h" #include "ctkModalityWidgetPlugin.h" #include "ctkPathLineEditPlugin.h" +#include "ctkPathListButtonsWidgetPlugin.h" +#include "ctkPathListWidgetPlugin.h" #include "ctkPopupWidgetPlugin.h" #include "ctkRangeSliderPlugin.h" #include "ctkRangeWidgetPlugin.h" @@ -98,6 +100,8 @@ class CTK_WIDGETS_PLUGINS_EXPORT ctkWidgetsPlugins : public QObject, << new ctkMenuButtonPlugin << new ctkModalityWidgetPlugin << new ctkPathLineEditPlugin + << new ctkPathListButtonsWidgetPlugin + << new ctkPathListWidgetPlugin << new ctkPopupWidgetPlugin << new ctkRangeSliderPlugin << new ctkRangeWidgetPlugin diff --git a/Libs/Widgets/Resources/UI/ctkPathListButtonsWidget.ui b/Libs/Widgets/Resources/UI/ctkPathListButtonsWidget.ui new file mode 100644 index 0000000000..487a3c5ca5 --- /dev/null +++ b/Libs/Widgets/Resources/UI/ctkPathListButtonsWidget.ui @@ -0,0 +1,64 @@ + + + ctkPathListButtonsWidget + + + + 0 + 0 + 302 + 25 + + + + Form + + + + 0 + + + + + Add files + + + Add files + + + + + + + Add a directory + + + Add directory + + + + + + + Remove selected entries + + + Remove + + + + + + + Edit current entry + + + Edit + + + + + + + + diff --git a/Libs/Widgets/Testing/Cpp/CMakeLists.txt b/Libs/Widgets/Testing/Cpp/CMakeLists.txt index df1bb56db2..e4de13f4f6 100644 --- a/Libs/Widgets/Testing/Cpp/CMakeLists.txt +++ b/Libs/Widgets/Testing/Cpp/CMakeLists.txt @@ -21,6 +21,7 @@ set(TEST_SOURCES ctkComboBoxTest1.cpp ctkCompleterTest1.cpp ctkConsoleTest1.cpp + ctkCoordinatesWidgetTest.cpp ctkCoordinatesWidgetTest1.cpp ctkCrosshairLabelTest1.cpp ctkDirectoryButtonTest1.cpp @@ -50,6 +51,8 @@ set(TEST_SOURCES ctkMessageBoxDontShowAgainTest.cpp ctkModalityWidgetTest1.cpp ctkPathLineEditTest1.cpp + ctkPathListWidgetTest.cpp + ctkPathListWidgetWithButtonsTest.cpp ctkPopupWidgetTest1.cpp ctkRangeSliderTest.cpp ctkRangeSliderTest1.cpp @@ -170,9 +173,12 @@ endif() set(Tests_MOC_CPP) QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS}) QT4_GENERATE_MOCS( + ctkCoordinatesWidgetTest.cpp ctkFlatProxyModelTest.cpp ctkFontButtonTest.cpp ctkMessageBoxDontShowAgainTest.cpp + ctkPathListWidgetTest.cpp + ctkPathListWidgetWithButtonsTest.cpp ctkRangeSliderTest.cpp ctkSettingsPanelTest.cpp ) @@ -210,6 +216,7 @@ SIMPLE_TEST( ctkColorPickerButtonTest1 ) SIMPLE_TEST( ctkComboBoxTest1 ) SIMPLE_TEST( ctkCompleterTest1 ) SIMPLE_TEST( ctkConsoleTest1 ) +SIMPLE_TEST( ctkCoordinatesWidgetTest ) SIMPLE_TEST( ctkCoordinatesWidgetTest1 ) SIMPLE_TEST( ctkCrosshairLabelTest1 ) SIMPLE_TEST( ctkDateRangeWidgetTest1 ) @@ -242,6 +249,8 @@ SIMPLE_TEST( ctkMenuComboBoxTest3 ) SIMPLE_TEST( ctkMessageBoxDontShowAgainTest ) SIMPLE_TEST( ctkModalityWidgetTest1 ) SIMPLE_TEST( ctkPathLineEditTest1 ) +SIMPLE_TEST( ctkPathListWidgetTest ) +SIMPLE_TEST( ctkPathListWidgetWithButtonsTest ) SIMPLE_TEST( ctkPopupWidgetTest1 ) SIMPLE_TEST( ctkRangeSliderTest ) SIMPLE_TEST( ctkRangeSliderTest1 ) diff --git a/Libs/Widgets/Testing/Cpp/ctkColorDialogTest1.cpp b/Libs/Widgets/Testing/Cpp/ctkColorDialogTest1.cpp index cfc3e76f46..9a532371a1 100644 --- a/Libs/Widgets/Testing/Cpp/ctkColorDialogTest1.cpp +++ b/Libs/Widgets/Testing/Cpp/ctkColorDialogTest1.cpp @@ -93,9 +93,4 @@ int ctkColorDialogTest1(int argc, char * argv [] ) } return app.exec(); - - ctkColorDialog::addDefaultTab(extraPanel, "Extra"); - QColor color = ctkColorDialog::getColor(Qt::black,0 , "", 0); - return EXIT_SUCCESS; - } diff --git a/Libs/Widgets/Testing/Cpp/ctkColorDialogTest2.cpp b/Libs/Widgets/Testing/Cpp/ctkColorDialogTest2.cpp index 885b25f650..dfcee1c6c4 100644 --- a/Libs/Widgets/Testing/Cpp/ctkColorDialogTest2.cpp +++ b/Libs/Widgets/Testing/Cpp/ctkColorDialogTest2.cpp @@ -46,9 +46,12 @@ int ctkColorDialogTest2(int argc, char * argv [] ) QTimer::singleShot(300, &app, SLOT(quit())); } - // The opened dialog blocks QTimers which prevents the test - // from being quit. QColor color = ctkColorDialog::getColor(Qt::black,0 , "", QColorDialog::DontUseNativeDialog); - + if (color.isValid()) + { + std::cout << "The color dialog should have been quit without a valid color." + << std::endl; + return EXIT_FAILURE; + } return app.exec(); } diff --git a/Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetTest.cpp b/Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetTest.cpp new file mode 100644 index 0000000000..63f6c6b6f9 --- /dev/null +++ b/Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetTest.cpp @@ -0,0 +1,100 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkCoordinatesWidget.h" +#include "ctkTest.h" + +// ---------------------------------------------------------------------------- +class ctkCoordinatesWidgetTester: public QObject +{ + Q_OBJECT +private slots: + void testDefaults(); + + void testNormalized(); + void testNormalized_data(); +}; + +// ---------------------------------------------------------------------------- +void ctkCoordinatesWidgetTester::testDefaults() +{ + ctkCoordinatesWidget coordinatesWidget; + QCOMPARE(coordinatesWidget.dimension(), 3); + QCOMPARE(coordinatesWidget.decimals(), 3); + QCOMPARE(coordinatesWidget.singleStep(), 1.); + QCOMPARE(coordinatesWidget.minimum(), -100000.); + QCOMPARE(coordinatesWidget.maximum(), 100000.); + QCOMPARE(coordinatesWidget.isNormalized(), false); + QCOMPARE(coordinatesWidget.coordinatesAsString(), QString("0,0,0")); + QCOMPARE(QVector3D(coordinatesWidget.coordinates()[0], + coordinatesWidget.coordinates()[1], + coordinatesWidget.coordinates()[2]), + QVector3D(0., 0., 0.)); +} + +// ---------------------------------------------------------------------------- +void ctkCoordinatesWidgetTester::testNormalized() +{ + ctkCoordinatesWidget coordinatesWidget; + QFETCH(QVector3D, coordinates); + coordinatesWidget.setCoordinates(coordinates.x(), coordinates.y(), coordinates.z()); + + coordinatesWidget.setNormalized(true); + QVector3D after(coordinatesWidget.coordinates()[0], + coordinatesWidget.coordinates()[1], + coordinatesWidget.coordinates()[2]); + QVector3D normalized = coordinates.normalized(); + // CoodinatesWidget has only 3 significant decimals (so just test the 2 first + // decimals because the last one might off of 1). + QCOMPARE(static_cast(normalized.x() * 100), static_cast(after.x() * 100)); + QCOMPARE(static_cast(normalized.y() * 100), static_cast(after.y() * 100)); + QCOMPARE(static_cast(normalized.z() * 100), static_cast(after.z() * 100)); + QCOMPARE(coordinatesWidget.minimum(), -1.); + QCOMPARE(coordinatesWidget.maximum(), 1.); +} + +// ---------------------------------------------------------------------------- +void ctkCoordinatesWidgetTester::testNormalized_data() +{ + QTest::addColumn("coordinates"); + + QTest::newRow("0,0,0") << QVector3D(); + QTest::newRow("1,0,0") << QVector3D(1., 0., 0.); + QTest::newRow("0,1,0") << QVector3D(0., 1., 0.); + QTest::newRow("0,0,-1") << QVector3D(0., 0., -1.); + QTest::newRow("2,0,0") << QVector3D(2., 0., 0.); + QTest::newRow("2,2,0") << QVector3D(2., 2., 0.); + QTest::newRow("0,-2,2") << QVector3D(0., -2., 2.); + QTest::newRow("1,2,3") << QVector3D(1., 2., 3.); + QTest::newRow("-1,-2,-3") << QVector3D(-1., -2., -3.); +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkCoordinatesWidgetTest) +#include "moc_ctkCoordinatesWidgetTest.cpp" diff --git a/Libs/Widgets/Testing/Cpp/ctkPathListWidgetTest.cpp b/Libs/Widgets/Testing/Cpp/ctkPathListWidgetTest.cpp new file mode 100644 index 0000000000..f9b0b83d15 --- /dev/null +++ b/Libs/Widgets/Testing/Cpp/ctkPathListWidgetTest.cpp @@ -0,0 +1,307 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include + +// CTK includes +#include "ctkPathListWidget.h" +#include "ctkTest.h" + +// STD includes +#include +#include + +//----------------------------------------------------------------------------- +class ctkPathListWidgetTester : public QObject +{ + Q_OBJECT + +private slots: + + void testDefaults(); + void testChangeOptions(); + void testMode(); + void testRelativeAndAbsolutePaths(); + void testSimilarPaths(); + void testSetPaths(); + void testEditPaths(); +}; + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testDefaults() +{ + ctkPathListWidget pathList; + + QSignalSpy pathListChangedSpy(&pathList, SIGNAL(pathsChanged(QStringList,QStringList))); + + // The default options are Readable and Exists + + QCOMPARE(pathList.fileOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable); + QCOMPARE(pathList.directoryOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable); + + QVERIFY(!pathList.addPath("/shouldnotexist/")); + QVERIFY(!pathList.addPath("/should/also/not/exist")); + + QVERIFY(pathList.addPath(QDir::tempPath())); + QVERIFY(!pathList.addPath(QDir::tempPath())); + + QVERIFY(!pathList.removePath("/shouldnotexist/")); + + + QList expectedArgs; + expectedArgs.push_back(QStringList(QDir::tempPath())); + expectedArgs.push_back(QStringList()); + + QCOMPARE(pathList.paths(), expectedArgs.front().toStringList()); + + QCOMPARE(pathListChangedSpy.count(), 1); + QCOMPARE(pathListChangedSpy.front(), expectedArgs); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testChangeOptions() +{ + ctkPathListWidget pathList; + + QSignalSpy pathListChangedSpy(&pathList, SIGNAL(pathsChanged(QStringList,QStringList))); + + // The default options are Readable and Exists + + QCOMPARE(pathList.fileOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable); + QCOMPARE(pathList.directoryOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable); + + QCOMPARE(pathList.mode(), ctkPathListWidget::Any); + + QTemporaryFile tmpFile; + QVERIFY(tmpFile.open()); + + QVERIFY(!tmpFile.permissions().testFlag(QFile::ExeOwner)); + + QVERIFY(pathList.addPath(tmpFile.fileName())); + QCOMPARE(pathList.path(0), tmpFile.fileName()); + pathList.clear(); + QCOMPARE(pathList.count(), 0); + QCOMPARE(pathList.path(0), QString()); + + QCOMPARE(pathListChangedSpy.size(), 2); + QCOMPARE(pathListChangedSpy.at(0), QList() << (QStringList() << tmpFile.fileName()) << QStringList()); + QCOMPARE(pathListChangedSpy.at(1), QList() << QStringList() << (QStringList() << tmpFile.fileName())); + + pathListChangedSpy.clear(); + + // Add another temporary non-executable file + QTemporaryFile tmpFile2; + QVERIFY(tmpFile2.open()); + QVERIFY(pathList.addPath(tmpFile2.fileName())); + QCOMPARE(pathList.count(), 1); + QCOMPARE(pathList.path(0), tmpFile2.fileName()); + + pathListChangedSpy.clear(); + + // Changing the file options. This should invalidate tmpFile2 and remove it + pathList.setFileOptions(pathList.fileOptions() | ctkPathListWidget::Executable); + QCOMPARE(pathList.fileOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable | ctkPathListWidget::Executable); + pathList.setDirectoryOptions(pathList.directoryOptions() | ctkPathListWidget::Writable); + QCOMPARE(pathList.directoryOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable | ctkPathListWidget::Writable); + + QCOMPARE(pathList.count(), 0); + + QCOMPARE(pathListChangedSpy.count(), 1); + QCOMPARE(pathListChangedSpy.at(0), QList() << QStringList() << (QStringList() << tmpFile2.fileName())); + pathListChangedSpy.clear(); + + // The tmp file is not executable, so it should not be added now + QVERIFY(!pathList.addPath(tmpFile.fileName())); + QVERIFY(pathListChangedSpy.isEmpty()); + + QVERIFY(tmpFile.setPermissions(tmpFile.permissions() | QFile::ExeOwner)); + + // Try again + QVERIFY(pathList.addPath(tmpFile.fileName())); + QCOMPARE(pathList.count(), 1); + QCOMPARE(pathList.path(0), tmpFile.fileName()); + + // Change the file options again. This should not invalidate the executable temporary file + pathList.setFileOptions(ctkPathListWidget::Exists | ctkPathListWidget::Readable); + QCOMPARE(pathList.fileOptions(), ctkPathListWidget::Exists | ctkPathListWidget::Readable); + QCOMPARE(pathList.paths(), QStringList() << tmpFile.fileName()); + + // Add the non-executable tmpFile2 again + pathList.addPath(tmpFile2.fileName()); + QCOMPARE(pathList.count(), 2); + QCOMPARE(pathList.paths(), QStringList() << tmpFile.fileName() << tmpFile2.fileName()); + + QCOMPARE(pathListChangedSpy.count(), 2); + pathListChangedSpy.clear(); + + // Remove all + pathList.clear(); + QCOMPARE(pathList.count(), 0); + QCOMPARE(pathListChangedSpy.count(), 1); + QCOMPARE(pathListChangedSpy.at(0), QList() << QStringList() + << (QStringList() << tmpFile.fileName() << tmpFile2.fileName())); + pathListChangedSpy.clear(); + + // Add two at once + pathList.addPaths(QStringList() << tmpFile.fileName() << tmpFile2.fileName()); + QCOMPARE(pathList.count(), 2); + QCOMPARE(pathList.path(0), tmpFile.fileName()); + QCOMPARE(pathList.path(1), tmpFile2.fileName()); + + QCOMPARE(pathListChangedSpy.count(), 1); + QCOMPARE(pathListChangedSpy.at(0), QList() + << (QStringList() << tmpFile.fileName() << tmpFile2.fileName()) + << QStringList()); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testMode() +{ + ctkPathListWidget listWidget; + + listWidget.setFileOptions(ctkPathListWidget::None); + QVERIFY(listWidget.fileOptions() == ctkPathListWidget::None); + listWidget.setDirectoryOptions(ctkPathListWidget::None); + QVERIFY(listWidget.directoryOptions() == ctkPathListWidget::None); + + QVERIFY(listWidget.addPath("/a")); + QVERIFY(listWidget.addPath("/a/")); + + listWidget.setMode(ctkPathListWidget::FilesOnly); + QVERIFY(listWidget.addPath("/b")); + QVERIFY(!listWidget.addPath("/b/")); + + listWidget.setMode(ctkPathListWidget::DirectoriesOnly); + QVERIFY(!listWidget.addPath("/c")); + QVERIFY(listWidget.addPath("/c/")); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testRelativeAndAbsolutePaths() +{ + ctkPathListWidget listWidget; + + listWidget.setFileOptions(ctkPathListWidget::None); + QVERIFY(listWidget.fileOptions() == ctkPathListWidget::None); + listWidget.setDirectoryOptions(ctkPathListWidget::None); + QVERIFY(listWidget.directoryOptions() == ctkPathListWidget::None); + + QStringList paths = QStringList() + << "/some/absolute/path/to/file" + << "/some/absolute/path/to/dir/" + << "relative/path/to/file" + << "relative/path/to/dir/"; + + QStringList resultPaths = QStringList() + << "/some/absolute/path/to/file" + << "/some/absolute/path/to/dir/" + << QDir::currentPath() + "/relative/path/to/file" + << QDir::currentPath() + "/relative/path/to/dir/"; + + QCOMPARE(listWidget.addPaths(paths), resultPaths); + + QCOMPARE(listWidget.path(0), resultPaths[0]); + QCOMPARE(listWidget.path(1), resultPaths[1]); + QCOMPARE(listWidget.path(2), resultPaths[2]); + QCOMPARE(listWidget.path(3), resultPaths[3]); + + QCOMPARE(listWidget.files(), QStringList() << paths[0] << paths[2]); + QCOMPARE(listWidget.files(true), QStringList() << resultPaths[0] << resultPaths[2]); + QCOMPARE(listWidget.directories(), QStringList() << paths[1] << paths[3]); + QCOMPARE(listWidget.directories(true), QStringList() << resultPaths[1] << resultPaths[3]); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testSimilarPaths() +{ + ctkPathListWidget listWidget; + + listWidget.setFileOptions(ctkPathListWidget::None); + listWidget.setDirectoryOptions(ctkPathListWidget::None); + + QVERIFY(listWidget.addPath("/one/path")); + QVERIFY(listWidget.addPath("/one/path/")); + QVERIFY(listWidget.addPath("/one")); + QVERIFY(listWidget.addPath("/one/")); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testSetPaths() +{ + ctkPathListWidget listWidget; + + listWidget.setFileOptions(ctkPathListWidget::None); + QVERIFY(listWidget.fileOptions() == ctkPathListWidget::None); + listWidget.setDirectoryOptions(ctkPathListWidget::None); + QVERIFY(listWidget.directoryOptions() == ctkPathListWidget::None); + + QVERIFY(listWidget.addPath("/file/a")); + QVERIFY(listWidget.addPath("/file/b")); + QVERIFY(listWidget.addPath("/dir/a/")); + + QSignalSpy pathListChangedSpy(&listWidget, SIGNAL(pathsChanged(QStringList,QStringList))); + + QStringList newPaths = QStringList() + << "/new/file/x" + << "/file/b" + << "/new/dir/x"; + + listWidget.setPaths(newPaths); + QCOMPARE(pathListChangedSpy.count(), 1); + QCOMPARE(pathListChangedSpy.front().at(0).toStringList(), QStringList() << "/new/file/x" << "/new/dir/x"); +} + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetTester::testEditPaths() +{ + ctkPathListWidget listWidget; + + listWidget.setFileOptions(ctkPathListWidget::None); + listWidget.setDirectoryOptions(ctkPathListWidget::None); + + QVERIFY(listWidget.addPath("/file/a")); + QVERIFY(listWidget.addPath("/file/b")); + QVERIFY(listWidget.addPath("/dir/a/")); + + QSignalSpy pathListChangedSpy(&listWidget, SIGNAL(pathsChanged(QStringList,QStringList))); + + QVERIFY(!listWidget.editPath(QModelIndex(), "bla")); + QVERIFY(!listWidget.editPath(listWidget.model()->index(0,0), "/new/file/a/")); + QVERIFY(listWidget.editPath(listWidget.model()->index(0,0), "/new/file/a")); + QCOMPARE(listWidget.path(0), QString("/new/file/a")); + + QVERIFY(listWidget.editPath("/dir/a/", "/new/dir/a/")); + QCOMPARE(listWidget.path(2), QString("/new/dir/a/")); + + QCOMPARE(pathListChangedSpy.count(), 2); + QCOMPARE(pathListChangedSpy.at(0).at(0).toString(), QString("/new/file/a")); + QCOMPARE(pathListChangedSpy.at(0).at(1).toString(), QString("/file/a")); + + QCOMPARE(pathListChangedSpy.at(1).at(0).toString(), QString("/new/dir/a/")); + QCOMPARE(pathListChangedSpy.at(1).at(1).toString(), QString("/dir/a/")); +} + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkPathListWidgetTest) +#include "moc_ctkPathListWidgetTest.cpp" diff --git a/Libs/Widgets/Testing/Cpp/ctkPathListWidgetWithButtonsTest.cpp b/Libs/Widgets/Testing/Cpp/ctkPathListWidgetWithButtonsTest.cpp new file mode 100644 index 0000000000..1c0dc3d1ca --- /dev/null +++ b/Libs/Widgets/Testing/Cpp/ctkPathListWidgetWithButtonsTest.cpp @@ -0,0 +1,99 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkPathListWidget.h" +#include "ctkPathListButtonsWidget.h" +#include "ctkUtils.h" +#include "ctkTest.h" + +// STD includes +#include +#include + +//----------------------------------------------------------------------------- +class ctkPathListWidgetWithButtonsTester : public QObject +{ + Q_OBJECT + +private slots: + + void testButtons(); + +}; + +// ---------------------------------------------------------------------------- +void ctkPathListWidgetWithButtonsTester::testButtons() +{ + QWidget topLevel; + topLevel.setLayout(new QHBoxLayout()); + + ctkPathListWidget pathList(&topLevel); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(1); + pathList.setSizePolicy(sizePolicy); + + ctkPathListButtonsWidget pathListButtons(&topLevel); + pathListButtons.init(&pathList); + pathListButtons.setOrientation(Qt::Vertical); + + + topLevel.layout()->addWidget(&pathList); + topLevel.layout()->addWidget(&pathListButtons); + + topLevel.show(); + QTest::qWaitForWindowShown(&topLevel); + + struct CloseModalDialog : public QRunnable + { + void run() + { + QTest::qWait(1000); + QTimer::singleShot(0, QApplication::activeModalWidget(), SLOT(accept())); + } + }; + QThreadPool::globalInstance()->start(new CloseModalDialog); + QTest::mouseClick(pathListButtons.buttonAddDirectory(), Qt::LeftButton); + + QCOMPARE(pathList.count(), 1); + QVERIFY(!pathListButtons.buttonRemove()->isEnabled()); + QVERIFY(!pathListButtons.buttonEdit()->isEnabled()); + + pathList.selectAll(); + + QVERIFY(pathListButtons.buttonRemove()->isEnabled()); + QVERIFY(pathListButtons.buttonEdit()->isEnabled()); + + QTest::mouseClick(pathListButtons.buttonRemove(), Qt::LeftButton); + QCOMPARE(pathList.count(), 0); +} + + +// ---------------------------------------------------------------------------- +CTK_TEST_MAIN(ctkPathListWidgetWithButtonsTest) +#include "moc_ctkPathListWidgetWithButtonsTest.cpp" diff --git a/Libs/Widgets/ctkCheckablePushButton.h b/Libs/Widgets/ctkCheckablePushButton.h index 2d310a6ec9..333cc21360 100644 --- a/Libs/Widgets/ctkCheckablePushButton.h +++ b/Libs/Widgets/ctkCheckablePushButton.h @@ -64,13 +64,13 @@ class CTK_WIDGETS_EXPORT ctkCheckablePushButton : public QPushButton /// /// Set the alignment of the text on the button, - /// Qt::Left|Qt::VCenter by default. + /// Qt::AlignLeft|Qt::AlignVCenter by default. void setButtonTextAlignment(Qt::Alignment textAlignment); Qt::Alignment buttonTextAlignment()const; /// /// Set the alignment of the indicator (arrow) on the button, - /// Qt::Left|Qt::VCenter by default. + /// Qt::AlignLeft|Qt::AlignVCenter by default. void setIndicatorAlignment(Qt::Alignment indicatorAlignment); Qt::Alignment indicatorAlignment()const; diff --git a/Libs/Widgets/ctkCollapsibleButton.h b/Libs/Widgets/ctkCollapsibleButton.h index b4f4f7595c..bff9a4c6f6 100644 --- a/Libs/Widgets/ctkCollapsibleButton.h +++ b/Libs/Widgets/ctkCollapsibleButton.h @@ -35,11 +35,11 @@ class QStyleOptionButton; /// \ingroup Widgets /// A collapsible button that shows/hides its children depending on its /// checked/collapsed property. -/// Warning: As ctkCollapsibleButton forces the Visiblity of its children to +/// Warning: <old behavior> As ctkCollapsibleButton forces the Visiblity of its children to /// true when it get expanded, any child Visibility property is lost. All the widgets /// will then be visible. To avoid this behavior, use an intermediate widget that /// contains all the children (they would become grandchildren and their Visibility property -/// will remain relative to their parent, ctkCollapsibleButton's unique child widget. +/// will remain relative to their parent, ctkCollapsibleButton's unique child widget.</old behavior> /// Note: The user QAbstractButton::icon is not visible (it's placeholder is used to display the /// collapsible state class CTK_WIDGETS_EXPORT ctkCollapsibleButton : public QAbstractButton @@ -100,13 +100,13 @@ class CTK_WIDGETS_EXPORT ctkCollapsibleButton : public QAbstractButton /// /// Set the alignment of the text on the button, - /// Qt::Left|Qt::VCenter by default. + /// Qt::AlignLeft|Qt::AlignVCenter by default. void setButtonTextAlignment(Qt::Alignment textAlignment); Qt::Alignment buttonTextAlignment()const; /// /// Set the alignment of the indicator (arrow) on the button, - /// Qt::Left|Qt::VCenter by default. + /// Qt::AlignLeft|Qt::AlignVCenter by default. void setIndicatorAlignment(Qt::Alignment indicatorAlignment); Qt::Alignment indicatorAlignment()const; diff --git a/Libs/Widgets/ctkColorDialog.cpp b/Libs/Widgets/ctkColorDialog.cpp index d897829fc6..876b7cf001 100644 --- a/Libs/Widgets/ctkColorDialog.cpp +++ b/Libs/Widgets/ctkColorDialog.cpp @@ -30,6 +30,7 @@ QList ctkColorDialog::DefaultTabs; int ctkColorDialog::DefaultTab = -1; +QString ctkColorDialog::LastColorName = QString(); //------------------------------------------------------------------------------ class ctkColorDialogPrivate @@ -42,6 +43,7 @@ class ctkColorDialogPrivate void init(); QTabWidget* LeftTabWidget; QWidget* BasicTab; + QString ColorName; }; //------------------------------------------------------------------------------ @@ -72,6 +74,9 @@ void ctkColorDialogPrivate::init() // well. q->setSizeGripEnabled(true); q->layout()->setSizeConstraint(QLayout::SetDefaultConstraint); + + QObject::connect(q, SIGNAL(currentColorChanged(QColor)), + q, SLOT(resetColorName())); } //------------------------------------------------------------------------------ @@ -146,34 +151,54 @@ QColor ctkColorDialog::getColor(const QColor &initial, QWidget *parent, const QS foreach(QWidget* tab, ctkColorDialog::DefaultTabs) { dlg.insertTab(tab->property("tabIndex").toInt(), tab, tab->windowTitle()); - if (tab->property("signal").isValid()) + if (tab->property("colorSignal").isValid()) { - QObject::connect(tab, tab->property("signal").toString().toLatin1(), + QObject::connect(tab, tab->property("colorSignal").toString().toLatin1(), &dlg, SLOT(setColor(QColor))); } + if (tab->property("nameSignal").isValid()) + { + QObject::connect(tab, tab->property("nameSignal").toString().toLatin1(), + &dlg, SLOT(setColorName(QString))); + } } dlg.setCurrentTab(ctkColorDialog::DefaultTab); dlg.exec(); foreach(QWidget* tab, ctkColorDialog::DefaultTabs) { dlg.removeTab(dlg.indexOf(tab)); - if (tab->property("signal").isValid()) + if (tab->property("colorSignal").isValid()) { - QObject::disconnect(tab, tab->property("signal").toString().toLatin1(), + QObject::disconnect(tab, tab->property("colorSignal").toString().toLatin1(), &dlg, SLOT(setColor(QColor))); } + if (tab->property("nameSignal").isValid()) + { + QObject::disconnect(tab, tab->property("nameSignal").toString().toLatin1(), + &dlg, SLOT(setColorName(QString))); + } tab->setParent(0); tab->hide(); } - + ctkColorDialog::LastColorName = dlg.colorName(); return dlg.selectedColor(); } //------------------------------------------------------------------------------ -void ctkColorDialog::insertDefaultTab(int tabIndex, QWidget* widget, const QString& label, const char* signal) +QString ctkColorDialog::getColorName() +{ + return ctkColorDialog::LastColorName; +} + +//------------------------------------------------------------------------------ +void ctkColorDialog::insertDefaultTab(int tabIndex, QWidget* widget, + const QString& label, + const char* colorSignal, + const char* nameSignal) { widget->setWindowTitle(label); - widget->setProperty("signal", signal); + widget->setProperty("colorSignal", colorSignal); + widget->setProperty("nameSignal", nameSignal); widget->setProperty("tabIndex", tabIndex); ctkColorDialog::DefaultTabs << widget; @@ -192,4 +217,27 @@ void ctkColorDialog::setColor(const QColor& color) this->QColorDialog::setCurrentColor(color); } +//------------------------------------------------------------------------------ +void ctkColorDialog::setColorName(const QString& name) +{ + Q_D(ctkColorDialog); + if (d->ColorName == name) + { + return; + } + d->ColorName = name; + emit currentColorNameChanged(d->ColorName); +} + +//------------------------------------------------------------------------------ +QString ctkColorDialog::colorName()const +{ + Q_D(const ctkColorDialog); + return d->ColorName; +} +//------------------------------------------------------------------------------ +void ctkColorDialog::resetColorName() +{ + this->setColorName(QString()); +} diff --git a/Libs/Widgets/ctkColorDialog.h b/Libs/Widgets/ctkColorDialog.h index c1c8e427f0..e91ce4e358 100644 --- a/Libs/Widgets/ctkColorDialog.h +++ b/Libs/Widgets/ctkColorDialog.h @@ -43,14 +43,19 @@ class CTK_WIDGETS_EXPORT ctkColorDialog : public QColorDialog explicit ctkColorDialog(QWidget* parent = 0); explicit ctkColorDialog(const QColor& initial, QWidget* parent = 0); virtual ~ctkColorDialog(); - + /// Add an extra widget under the file format combobox. If a label is /// given, it will appear in the first column. /// The widget is reparented to ctkColorDialog /// The ownership of the widget is taken. - /// You must manually connect the color changed signal of the widget + /// You must manually connect the color changed signal of the widget /// to ctkColorDialog::setColor(QColor) + /// Same apply if you want to specify a color name, you must connect the + /// color name changed signal to ctkColorDialog::setColorName(QString) but + /// you have to make sure the color name is set after setColor as it always + /// resets the color name. inline void addTab(QWidget* widget, const QString& label); + /// Same as addTab(), in addition, \a tabIndex control the tab index of the widget. /// If index is -1, the tab is appended (same as addDefaultTab). The last /// tab added with an index of 0 will be the first tab open @@ -67,11 +72,14 @@ class CTK_WIDGETS_EXPORT ctkColorDialog : public QColorDialog /// Return the extra widget if any /// Be careful with the "Basic" tab. QWidget* widget(int index)const; - + /// Returns the index position of the page occupied by the widget w, /// or -1 if the widget cannot be found int indexOf(QWidget* widget)const; + /// Return the current color name if any has been set. + QString colorName()const; + /// Pops up a modal color dialog with the given window \a title (or "Select Color" if none is /// specified), lets the user choose a color, and returns that color. The color is initially set /// to \a initial. The dialog is a child of \a parent. It returns an invalid (see @@ -81,28 +89,48 @@ class CTK_WIDGETS_EXPORT ctkColorDialog : public QColorDialog /// QColorDialog::DontUseNativeDialog is forced static QColor getColor(const QColor &initial, QWidget *parent, const QString &title, ColorDialogOptions options = 0); - /// Add a custom widget as an additional tab of the color dialog created by - /// ctkColorDialog::getColor. \a label is title of the tab and \a signal is the signal fired by + /// Return the last selected color name if any. getColorName() call is only + /// valid after a getColor() call. + /// \sa getColor + static QString getColorName(); + + /// Add a custom widget as an additional tab of the color dialog created by + /// ctkColorDialog::getColor. \a label is title of the tab and \a signal is the signal fired by /// the widget whenever a QColor is changed, typically: SIGNAL(currentColorChanged(QColor)). It /// is internally connected to set the current color of the dialog - static inline void addDefaultTab(QWidget* widget, const QString& label, const char* signal = 0); + static inline void addDefaultTab(QWidget* widget, const QString& label, + const char* colorSignal = 0, + const char* nameSignal = 0); /// Same as addDefaultTab, in addition, \a tabIndex control the tab index of the widget. /// If index is -1, the tab is appended (same as addDefaultTab). The last /// tab added with an index of 0 will be the first tab open - static void insertDefaultTab(int tabIndex, QWidget* widget, const QString& label, const char* signal = 0); + static void insertDefaultTab(int tabIndex, QWidget* widget, const QString& label, + const char* colorSignal = 0, + const char* nameSignal = 0); /// Index of the tab to make default (active when getColor is called). /// -1 for the "Basic Colors", it's the default behavior static void setDefaultTab(int index); public Q_SLOTS: - /// Slotify QColorDialog::setCurrentColor(QColor) + /// Slot-ify QColorDialog::setCurrentColor(QColor) void setColor(const QColor& color); + /// Set the color name. + /// Note that each time the color is changed the name is reset. + void setColorName(const QString& name); + +Q_SIGNALS: + void currentColorNameChanged(const QString& colorName); + +protected Q_SLOTS: + void resetColorName(); + protected: QScopedPointer d_ptr; static QList DefaultTabs; static int DefaultTab; + static QString LastColorName; private: Q_DECLARE_PRIVATE(ctkColorDialog); Q_DISABLE_COPY(ctkColorDialog); @@ -115,9 +143,11 @@ void ctkColorDialog::addTab(QWidget* widget, const QString& label) } //------------------------------------------------------------------------------ -void ctkColorDialog::addDefaultTab(QWidget* widget, const QString& label, const char* signal) +void ctkColorDialog::addDefaultTab(QWidget* widget, const QString& label, + const char* colorSignal, + const char* nameSignal) { - ctkColorDialog::insertDefaultTab(-1, widget, label, signal); + ctkColorDialog::insertDefaultTab(-1, widget, label, colorSignal, nameSignal); } #endif diff --git a/Libs/Widgets/ctkColorPickerButton.cpp b/Libs/Widgets/ctkColorPickerButton.cpp index f44ab5f47b..aaec71c4a2 100644 --- a/Libs/Widgets/ctkColorPickerButton.cpp +++ b/Libs/Widgets/ctkColorPickerButton.cpp @@ -42,9 +42,11 @@ class ctkColorPickerButtonPrivate ctkColorPickerButtonPrivate(ctkColorPickerButton& object); void init(); void computeIcon(); + QString text()const; QIcon Icon; QColor Color; + QString ColorName; bool DisplayColorName; ctkColorPickerButton::ColorDialogOptions DialogOptions; mutable QSize CachedSizeHint; @@ -55,6 +57,7 @@ ctkColorPickerButtonPrivate::ctkColorPickerButtonPrivate(ctkColorPickerButton& o : q_ptr(&object) { this->Color = Qt::black; + this->ColorName = QString(); this->DisplayColorName = true; this->DialogOptions = 0; } @@ -84,6 +87,24 @@ void ctkColorPickerButtonPrivate::computeIcon() this->Icon = QIcon(pix); } +//----------------------------------------------------------------------------- +QString ctkColorPickerButtonPrivate::text()const +{ + Q_Q(const ctkColorPickerButton); + if (!this->DisplayColorName) + { + return q->text(); + } + if (this->ColorName.isEmpty()) + { + return this->Color.name(); + } + else + { + return this->ColorName; + } +} + //----------------------------------------------------------------------------- ctkColorPickerButton::ctkColorPickerButton(QWidget* _parent) : QPushButton(_parent) @@ -123,25 +144,28 @@ ctkColorPickerButton::~ctkColorPickerButton() void ctkColorPickerButton::changeColor() { Q_D(ctkColorPickerButton); - QColor res; - QColorDialog::ColorDialogOptions options; - options |= QColorDialog::ColorDialogOption( - static_cast(d->DialogOptions & ShowAlphaChannel)); - options |= QColorDialog::ColorDialogOption( - static_cast(d->DialogOptions & NoButtons)); - options |= QColorDialog::ColorDialogOption( - static_cast(d->DialogOptions & DontUseNativeDialog)); + QColor newColor; + QString newColorName; + QColorDialog::ColorDialogOptions options; + options |= QColorDialog::ColorDialogOption( + static_cast(d->DialogOptions & ShowAlphaChannel)); + options |= QColorDialog::ColorDialogOption( + static_cast(d->DialogOptions & NoButtons)); + options |= QColorDialog::ColorDialogOption( + static_cast(d->DialogOptions & DontUseNativeDialog)); if (d->DialogOptions & UseCTKColorDialog) { - res = ctkColorDialog::getColor(d->Color, this, QString(""),options); + newColor = ctkColorDialog::getColor(d->Color, this, QString(""),options); + newColorName = ctkColorDialog::getColorName(); } else { - res = QColorDialog::getColor(d->Color, this, QString(""), options); + newColor = QColorDialog::getColor(d->Color, this, QString(""), options); } - if (res.isValid()) + if (newColor.isValid()) { - this->setColor(res); + this->setColor(newColor); + this->setColorName(newColorName); } } @@ -209,6 +233,29 @@ QColor ctkColorPickerButton::color()const return d->Color; } +//----------------------------------------------------------------------------- +void ctkColorPickerButton::setColorName(const QString& newColorName) +{ + Q_D(ctkColorPickerButton); + if (newColorName == d->ColorName) + { + return; + } + + d->ColorName = newColorName; + d->CachedSizeHint = QSize(); + this->update(); + this->updateGeometry(); + emit colorNameChanged(d->ColorName); +} + +//----------------------------------------------------------------------------- +QString ctkColorPickerButton::colorName()const +{ + Q_D(const ctkColorPickerButton); + return d->ColorName; +} + //----------------------------------------------------------------------------- void ctkColorPickerButton::paintEvent(QPaintEvent *) { @@ -216,10 +263,7 @@ void ctkColorPickerButton::paintEvent(QPaintEvent *) QStylePainter p(this); QStyleOptionButton option; this->initStyleOption(&option); - if (d->DisplayColorName) - { - option.text = d->Color.name(); - } + option.text = d->text(); option.icon = d->Icon; p.drawControl(QStyle::CE_PushButton, option); } @@ -228,7 +272,7 @@ void ctkColorPickerButton::paintEvent(QPaintEvent *) QSize ctkColorPickerButton::sizeHint()const { Q_D(const ctkColorPickerButton); - if (d->DisplayColorName || !this->text().isEmpty()) + if (!d->DisplayColorName && !this->text().isEmpty()) { return this->QPushButton::sizeHint(); } @@ -237,17 +281,31 @@ QSize ctkColorPickerButton::sizeHint()const return d->CachedSizeHint; } + // If no text, the sizehint is a QToolButton sizeHint QStyleOptionButton pushButtonOpt; this->initStyleOption(&pushButtonOpt); - QStyleOptionToolButton opt; - (&opt)->QStyleOption::operator=(pushButtonOpt); - opt.arrowType = Qt::NoArrow; - opt.icon = d->Icon; + pushButtonOpt.text = d->text(); int iconSize = this->style()->pixelMetric(QStyle::PM_SmallIconSize); - opt.iconSize = QSize(iconSize, iconSize); - opt.rect.setSize(opt.iconSize); // PM_MenuButtonIndicator depends on the height - d->CachedSizeHint = this->style()->sizeFromContents( - QStyle::CT_ToolButton, &opt, opt.iconSize, this). - expandedTo(QApplication::globalStrut()); + if (pushButtonOpt.text == QString()) + { + QStyleOptionToolButton opt; + (&opt)->QStyleOption::operator=(pushButtonOpt); + opt.arrowType = Qt::NoArrow; + opt.icon = d->Icon; + opt.iconSize = QSize(iconSize, iconSize); + opt.rect.setSize(opt.iconSize); // PM_MenuButtonIndicator depends on the height + d->CachedSizeHint = this->style()->sizeFromContents( + QStyle::CT_ToolButton, &opt, opt.iconSize, this). + expandedTo(QApplication::globalStrut()); + } + else + { + pushButtonOpt.icon = d->Icon; + pushButtonOpt.iconSize = QSize(iconSize, iconSize); + pushButtonOpt.rect.setSize(pushButtonOpt.iconSize); // PM_MenuButtonIndicator depends on the height + d->CachedSizeHint = (style()->sizeFromContents( + QStyle::CT_PushButton, &pushButtonOpt, pushButtonOpt.iconSize, this). + expandedTo(QApplication::globalStrut())); + } return d->CachedSizeHint; } diff --git a/Libs/Widgets/ctkColorPickerButton.h b/Libs/Widgets/ctkColorPickerButton.h index ed8b95b16d..aca2748b73 100644 --- a/Libs/Widgets/ctkColorPickerButton.h +++ b/Libs/Widgets/ctkColorPickerButton.h @@ -39,8 +39,22 @@ class CTK_WIDGETS_EXPORT ctkColorPickerButton : public QPushButton { Q_OBJECT Q_FLAGS(ColorDialogOption ColorDialogOptions) + + /// This property controls the name of the color. + /// Black (0,0,0) by default. Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged USER true) + + /// This property controls the name of the color. + /// If empty (default), the color in the format "#RRGGBB" is displayed in the + /// button if \a displayColorName is true, otherwise, the color name is used. + Q_PROPERTY(QString colorName READ colorName WRITE setColorName NOTIFY colorNameChanged) + + /// This properties controls whether the name of the color is shown on the + /// button if true or the button text instead. True by default. + /// \sa colorName, QPushButton::text Q_PROPERTY(bool displayColorName READ displayColorName WRITE setDisplayColorName DESIGNABLE true) + + /// This property controls the properties of the dialog used in \a changeColor Q_PROPERTY(ColorDialogOptions dialogOptions READ dialogOptions WRITE setDialogOptions) public: enum ColorDialogOption { @@ -53,20 +67,33 @@ class CTK_WIDGETS_EXPORT ctkColorPickerButton : public QPushButton /// By default, the color is black explicit ctkColorPickerButton(QWidget* parent = 0); + /// By default, the color is black. The text will be shown on the button if /// displayColorName is false, otherwise the color name is shown. /// \sa QPushButton::setText explicit ctkColorPickerButton(const QString& text, QWidget* parent = 0 ); + /// The text will be shown on the button if /// displayColorName is false, otherwise the color name is shown. /// \sa setColor, QPushButton::setText explicit ctkColorPickerButton(const QColor& color, const QString & text, QWidget* parent = 0 ); + virtual ~ctkColorPickerButton(); - /// /// Current selected color QColor color()const; + /// Current selected color name. + /// Returns the name of the color in the format "#RRGGBB" or the string set + /// by setColorName(). + /// \sa color(), setColorName() + QString colorName()const; + + /// Set the current color name. + /// This allows you to give name other than the default "#RRGGBB" + /// Set an invalid QString to restore the default color names + void setColorName(const QString& name); + /// /// Display the color name after color selection bool displayColorName()const; @@ -87,8 +114,10 @@ public Q_SLOTS: /// Set a new current color without opening a dialog void setColor(const QColor& color); - /// /// Opens a color dialog to select a new current color. + /// If the CTK color dialog (\a UseCTKColorDialog) is used, then the color + /// name is also set if the user selects a named color. + /// \sa ctkColorDialog, color, colorName void changeColor(); /// @@ -101,6 +130,9 @@ public Q_SLOTS: /// by the user when choosing a color from the color dialog void colorChanged(QColor); + /// This signaled is fired anytime a new color name is set. + void colorNameChanged(QString); + protected Q_SLOTS: void onToggled(bool change = true); diff --git a/Libs/Widgets/ctkCoordinatesWidget.cpp b/Libs/Widgets/ctkCoordinatesWidget.cpp index d2d362b463..1dd4763ff0 100644 --- a/Libs/Widgets/ctkCoordinatesWidget.cpp +++ b/Libs/Widgets/ctkCoordinatesWidget.cpp @@ -25,6 +25,9 @@ // CTK includes #include "ctkCoordinatesWidget.h" +// STD includes +#include + //------------------------------------------------------------------------------ ctkCoordinatesWidget::ctkCoordinatesWidget(QWidget* _parent) :QWidget(_parent) { @@ -32,9 +35,10 @@ ctkCoordinatesWidget::ctkCoordinatesWidget(QWidget* _parent) :QWidget(_parent) this->SingleStep = 1.; this->Minimum = -100000.; this->Maximum = 100000.; + this->Normalized = false; this->Dimension = 3; this->Coordinates = new double [this->Dimension]; - + QHBoxLayout* hboxLayout = new QHBoxLayout(this); this->setLayout(hboxLayout); for (int i = 0; i < this->Dimension; ++i) @@ -59,7 +63,7 @@ void ctkCoordinatesWidget::addSpinBox() spinBox->setSingleStep(this->SingleStep); spinBox->setMinimum(this->Minimum); spinBox->setMaximum(this->Maximum); - connect( spinBox, SIGNAL(valueChanged(double)), + connect( spinBox, SIGNAL(valueChanged(double)), this, SLOT(updateCoordinate(double))); this->layout()->addWidget(spinBox); } @@ -151,6 +155,30 @@ double ctkCoordinatesWidget::maximum() const return this->Maximum; } +//------------------------------------------------------------------------------ +void ctkCoordinatesWidget::setNormalized(bool normalized) +{ + this->Normalized = normalized; + if (this->Normalized) + { + double* normalizedCoordinates = new double[this->Dimension]; + memcpy(normalizedCoordinates, this->Coordinates, sizeof(double)*this->Dimension); + ctkCoordinatesWidget::normalize(normalizedCoordinates, this->Dimension); + + this->setMinimum(-1.); + this->setMaximum(1.); + + this->setCoordinates(normalizedCoordinates); + delete [] normalizedCoordinates; + } +} + +//------------------------------------------------------------------------------ +bool ctkCoordinatesWidget::isNormalized() const +{ + return this->Normalized; +} + //------------------------------------------------------------------------------ void ctkCoordinatesWidget::setDecimals(int newDecimals) { @@ -228,17 +256,21 @@ QString ctkCoordinatesWidget::coordinatesAsString()const } //------------------------------------------------------------------------------ -void ctkCoordinatesWidget::setCoordinates(double* _pos) +void ctkCoordinatesWidget::setCoordinates(double* coordinates) { for (int i = 0; i < this->Dimension; ++i) { - this->Coordinates[i] = _pos[i]; + this->Coordinates[i] = coordinates[i]; + } + if (this->Normalized) + { + this->normalize(this->Coordinates, this->Dimension); } bool blocked = this->blockSignals(true); for (int i = 0; i < this->Dimension; ++i) { QLayoutItem* item = this->layout()->itemAt(i); - QDoubleSpinBox* spinBox = + QDoubleSpinBox* spinBox = item ? dynamic_cast(item->widget()) : 0; if (spinBox) { @@ -249,6 +281,34 @@ void ctkCoordinatesWidget::setCoordinates(double* _pos) this->updateCoordinates(); } +//------------------------------------------------------------------------------ +void ctkCoordinatesWidget::setCoordinates(double x, double y, double z, double w) +{ + double* coordinates = new double[this->Dimension]; + if (this->Dimension >= 1) + { + coordinates[0] = x; + } + if (this->Dimension >= 2) + { + coordinates[1] = y; + } + if (this->Dimension >= 3) + { + coordinates[2] = z; + } + if (this->Dimension >= 4) + { + coordinates[3] = w; + } + for (int i = 4; i < this->Dimension; ++i) + { + coordinates[i] = this->Coordinates[i]; + } + this->setCoordinates(coordinates); + delete [] coordinates; +} + //------------------------------------------------------------------------------ double const * ctkCoordinatesWidget::coordinates()const { @@ -258,18 +318,64 @@ double const * ctkCoordinatesWidget::coordinates()const //------------------------------------------------------------------------------ void ctkCoordinatesWidget::updateCoordinate(double coordinate) { + double den = 0.; + int element = -1; for (int i = 0; i < this->Dimension; ++i) { QLayoutItem* item = this->layout()->itemAt(i); - QDoubleSpinBox* spinBox = + QDoubleSpinBox* spinBox = item ? qobject_cast(item->widget()) : 0; if ( spinBox && spinBox == this->sender()) { this->Coordinates[i] = coordinate; - break; + element = i; + } + else + { + den += this->Coordinates[i]*this->Coordinates[i]; } } - emit coordinatesChanged(this->Coordinates); + Q_ASSERT(element != -1); + if (this->isNormalized()) + { + // Old Values xx + yy + zz = 1 + // New values: x'x' + y'y' + z'z' = 1 + // Say we are changing y into y': + // x'x' + z'z' = 1 - y'y' + // Let's pose a the coef to multiply x into x' that keeps the norm to 1: + // axax + azaz = 1 - y'y' + // aa(xx + zz) = 1 - y'y' + // a = sqrt( (1 - y'y') / (xx + zz) ) + bool mult = true; + if (den != 0.0) + { + mult = true; + den = sqrt( (1. - coordinate * coordinate) / den); + } + else if (this->Dimension > 1) + { + mult = false; + den = sqrt((1. - coordinate*coordinate) / (this->Dimension - 1)); + } + double* normalizedCoordinates = new double[this->Dimension]; + for (int i = 0; i < this->Dimension; ++i) + { + if (i != element) + { + normalizedCoordinates[i] = mult ? this->Coordinates[i] * den : den; + } + else + { + normalizedCoordinates[i] = this->Coordinates[i]; + } + } + this->setCoordinates(normalizedCoordinates); + delete [] normalizedCoordinates; + } + else + { + emit coordinatesChanged(this->Coordinates); + } } //------------------------------------------------------------------------------ @@ -278,7 +384,7 @@ void ctkCoordinatesWidget::updateCoordinates() for (int i = 0; i < this->Dimension; ++i) { QLayoutItem* item = this->layout()->itemAt(i); - QDoubleSpinBox* spinBox = + QDoubleSpinBox* spinBox = item ? qobject_cast(item->widget()) : 0; if ( spinBox) { @@ -287,3 +393,39 @@ void ctkCoordinatesWidget::updateCoordinates() } emit coordinatesChanged(this->Coordinates); } + +//------------------------------------------------------------------------------ +void ctkCoordinatesWidget::normalize() +{ + double* normalizedCoordinates = new double[this->Dimension]; + memcpy(normalizedCoordinates, this->Coordinates, + sizeof(double) * this->Dimension); + ctkCoordinatesWidget::normalize(normalizedCoordinates, this->Dimension); + this->setCoordinates(normalizedCoordinates); + delete [] normalizedCoordinates; +} + +//------------------------------------------------------------------------------ +double ctkCoordinatesWidget::normalize(double* coordinates, int dimension) +{ + double den = ctkCoordinatesWidget::norm( coordinates, dimension ); + if ( den != 0.0 ) + { + for (int i = 0; i < dimension; ++i) + { + coordinates[i] /= den; + } + } + return den; +} + +//------------------------------------------------------------------------------ +double ctkCoordinatesWidget::norm(double* coordinates, int dimension) +{ + double sum = 0.; + for (int i = 0; i < dimension; ++i) + { + sum += coordinates[i] * coordinates[i]; + } + return sqrt(sum); +} diff --git a/Libs/Widgets/ctkCoordinatesWidget.h b/Libs/Widgets/ctkCoordinatesWidget.h index 232220f88c..edac781535 100644 --- a/Libs/Widgets/ctkCoordinatesWidget.h +++ b/Libs/Widgets/ctkCoordinatesWidget.h @@ -35,10 +35,16 @@ /// TODO: use pimpl class CTK_WIDGETS_EXPORT ctkCoordinatesWidget : public QWidget { - Q_OBJECT + Q_OBJECT + + Q_PROPERTY(int dimension READ dimension WRITE setDimension) + /// This property controls whether the coordinates must be normalized. + /// If true, the norm of the coordinates is enforced to be 1. + /// False by default. + Q_PROPERTY(bool normalized READ isNormalized WRITE setNormalized) + Q_PROPERTY(int decimals READ decimals WRITE setDecimals) Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep STORED false) - Q_PROPERTY(int dimension READ dimension WRITE setDimension) Q_PROPERTY(double minimum READ minimum WRITE setMinimum) Q_PROPERTY(double maximum READ maximum WRITE setMaximum) @@ -48,49 +54,56 @@ class CTK_WIDGETS_EXPORT ctkCoordinatesWidget : public QWidget explicit ctkCoordinatesWidget(QWidget* parent = 0); virtual ~ctkCoordinatesWidget(); - /// /// Set/Get the dimension of the point /// The default dimension is 3 void setDimension(int dim); int dimension() const; - - /// - /// Set/Get the number of decimals of each coordinate QDoubleSpinBoxes - /// The default single step is 3 + + /// Set/Get the number of decimals of each coordinate QDoubleSpinBoxes + /// The default number of decimals is 3. void setDecimals(int decimals); int decimals() const; - - /// - /// Set/Get the single step of each coordinate QDoubleSpinBoxes + /// Set/Get the single step of each coordinate QDoubleSpinBoxes /// The default single step is 1. void setSingleStep(double step); double singleStep() const; - /// - /// Set/Get the minimum value of each coordinate QDoubleSpinBoxes + /// Set/Get the minimum value of each coordinate QDoubleSpinBoxes /// The default minimum is -100000. void setMinimum(double minimum); double minimum() const; - /// - /// Set/Get the maximum value of each coordinate QDoubleSpinBoxes + /// Set/Get the maximum value of each coordinate QDoubleSpinBoxes /// The default maximum is 100000. void setMaximum(double minimum); double maximum() const; - /// - /// Set/Get the coordinates. Use commas between numbers - /// i.e. "0,0.0,0." + /// Change the normalized property. If \a normalize is true, it normalizes + /// the current coordinates, the range of possible values is reset to [-1, 1]. + /// \sa isNormalized + void setNormalized(bool normalize); + bool isNormalized()const; + + /// Return the norm of the coordinates. + double norm()const; + + /// Set/Get the coordinates. Use commas to separate elements, spaces are + /// allowed: e.g. "0,0.0, 0." void setCoordinatesAsString(QString pos); QString coordinatesAsString()const; - /// /// Set/Get the coordinates /// The default values are 0. void setCoordinates(double* pos); double const * coordinates()const; + /// Convenient function that sets up to 4 elements of the coordinates. + void setCoordinates(double x, double y = 0., double z = 0., double w = 0.); + +public Q_SLOTS: + void normalize(); + Q_SIGNALS: /// /// valueChanged is fired anytime a coordinate is modified, the returned @@ -105,10 +118,17 @@ protected Q_SLOTS: protected: void addSpinBox(); + /// Normalize coordinates vector and return the previous norm. + static double normalize(double* coordinates, int dimension); + + /// Compute the norm of a coordinates \a dimension vector + static double norm(double* coordinates, int dimension); + int Decimals; double SingleStep; double Minimum; double Maximum; + bool Normalized; int Dimension; double* Coordinates; }; diff --git a/Libs/Widgets/ctkDirectoryButton.cpp b/Libs/Widgets/ctkDirectoryButton.cpp index 18f387d546..64a46d05b9 100644 --- a/Libs/Widgets/ctkDirectoryButton.cpp +++ b/Libs/Widgets/ctkDirectoryButton.cpp @@ -81,7 +81,6 @@ void ctkDirectoryButtonPrivate::init() //----------------------------------------------------------------------------- void ctkDirectoryButtonPrivate::updateDisplayText() { - Q_Q(ctkDirectoryButton); QString buttonText = this->DisplayText; if (buttonText.isNull()) { diff --git a/Libs/Widgets/ctkMenuComboBox.cpp b/Libs/Widgets/ctkMenuComboBox.cpp index 8ab7872c12..92810614a2 100644 --- a/Libs/Widgets/ctkMenuComboBox.cpp +++ b/Libs/Widgets/ctkMenuComboBox.cpp @@ -126,7 +126,7 @@ void ctkMenuComboBoxPrivate::init() // ------------------------------------------------------------------------ QAction* ctkMenuComboBoxPrivate::actionByTitle(const QString& text, const QMenu* parentMenu) { - if (parentMenu->title() == text) + if (!parentMenu || parentMenu->title() == text) { return 0; } diff --git a/Libs/Widgets/ctkPathLineEdit.cpp b/Libs/Widgets/ctkPathLineEdit.cpp index 136c88c94a..edfde3fc8e 100644 --- a/Libs/Widgets/ctkPathLineEdit.cpp +++ b/Libs/Widgets/ctkPathLineEdit.cpp @@ -19,6 +19,8 @@ =========================================================================*/ // Qt includes +#include +#include #include #include #include @@ -29,6 +31,8 @@ #include #include #include +#include +#include // CTK includes #include "ctkPathLineEdit.h" @@ -45,9 +49,22 @@ class ctkPathLineEditPrivate public: ctkPathLineEditPrivate(ctkPathLineEdit& object); void init(); + QSize recomputeSizeHint(QSize& sh)const; void updateFilter(); + void adjustPathLineEditSize(); + + void _q_recomputeCompleterPopupSize(); + + void createPathLineEditWidget(bool useComboBox); + QString settingKey()const; + + QLineEdit* LineEdit; QComboBox* ComboBox; + QToolButton* BrowseButton; //!< "..." button + + int MinimumContentsLength; + ctkPathLineEdit::SizeAdjustPolicy SizeAdjustPolicy; QString Label; //!< used in file dialogs QStringList NameFilters; //!< Regular expression (in wildcard mode) used to help the user to complete the line @@ -59,9 +76,13 @@ class ctkPathLineEditPrivate #endif bool HasValidInput; //!< boolean that stores the old state of valid input + QString SettingKey; static QString sCurrentDirectory; //!< Content the last value of the current directory static int sMaxHistory; //!< Size of the history, if the history is full and a new value is added, the oldest value is dropped + + mutable QSize SizeHint; + mutable QSize MinimumSizeHint; }; QString ctkPathLineEditPrivate::sCurrentDirectory = ""; @@ -69,31 +90,154 @@ int ctkPathLineEditPrivate::sMaxHistory = 5; //----------------------------------------------------------------------------- ctkPathLineEditPrivate::ctkPathLineEditPrivate(ctkPathLineEdit& object) - :q_ptr(&object) + : q_ptr(&object) + , LineEdit(0) + , ComboBox(0) + , BrowseButton(0) + , MinimumContentsLength(0) + , SizeAdjustPolicy(ctkPathLineEdit::AdjustToContentsOnFirstShow) + , Filters(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Readable) + , HasValidInput(false) { - this->ComboBox = 0; - this->HasValidInput = false; - this->Filters = QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Readable; } //----------------------------------------------------------------------------- void ctkPathLineEditPrivate::init() { Q_Q(ctkPathLineEdit); - this->ComboBox = new QComboBox(q); + QHBoxLayout* layout = new QHBoxLayout(q); - layout->addWidget(this->ComboBox); layout->setContentsMargins(0,0,0,0); + layout->setSpacing(0); // no space between the combobx and button + + this->createPathLineEditWidget(true); + + this->BrowseButton = new QToolButton(q); + this->BrowseButton->setText("..."); + // Don't vertically stretch the path line edit unnecessary + this->BrowseButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored)); + this->BrowseButton->setToolTip(q->tr("Open a dialog")); + + QObject::connect(this->BrowseButton,SIGNAL(clicked()), + q, SLOT(browse())); + + layout->addWidget(this->BrowseButton); - this->ComboBox->setEditable(true); q->setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::LineEdit)); +} + +//------------------------------------------------------------------------------ +void ctkPathLineEditPrivate::createPathLineEditWidget(bool useComboBox) +{ + Q_Q(ctkPathLineEdit); + + QString path = q->currentPath(); + + if (useComboBox) + { + this->ComboBox = new QComboBox(q); + this->ComboBox->setEditable(true); + this->ComboBox->setInsertPolicy(QComboBox::NoInsert); + this->LineEdit = this->ComboBox->lineEdit(); + } + else + { + this->ComboBox = 0; + this->LineEdit = new QLineEdit(q); + } + + if (q->layout() && q->layout()->itemAt(0)) + { + delete q->layout()->itemAt(0)->widget(); + } + qobject_cast(q->layout())->insertWidget( + 0, + this->ComboBox ? qobject_cast(this->ComboBox) : + qobject_cast(this->LineEdit)); + + this->updateFilter(); + q->retrieveHistory(); + q->setCurrentPath(path); - QObject::connect(this->ComboBox,SIGNAL(editTextChanged(QString)), + QObject::connect(this->LineEdit, SIGNAL(textChanged(QString)), q, SLOT(setCurrentDirectory(QString))); - QObject::connect(this->ComboBox,SIGNAL(editTextChanged(QString)), + QObject::connect(this->LineEdit, SIGNAL(textChanged(QString)), q, SLOT(updateHasValidInput())); + q->updateGeometry(); +} + +//------------------------------------------------------------------------------ +QSize ctkPathLineEditPrivate::recomputeSizeHint(QSize& sh)const +{ + Q_Q(const ctkPathLineEdit); + if (!sh.isValid()) + { + int frame = 0; + if (this->ComboBox) + { + QStyleOptionComboBox option; + int arrowWidth = this->ComboBox->style()->subControlRect( + QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxArrow, this->ComboBox).width() + + (this->ComboBox->hasFrame() ? 2 : 0); + frame = 2 * (this->ComboBox->hasFrame() ? 3 : 0) + + arrowWidth + + 1; // for mac style, not sure why + } + else + { + QStyleOptionFrame option; + int frameWidth = this->LineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, q); + int horizontalMargin = 2; // QLineEditPrivate::horizontalMargin + // See QLineEdit::sizeHint + frame = 2 * frameWidth + + this->LineEdit->textMargins().left() + + this->LineEdit->textMargins().right() + + this->LineEdit->contentsMargins().left() + + this->LineEdit->contentsMargins().right() + + 2 * horizontalMargin; + } + int browseWidth = 0; + if (q->showBrowseButton()) + { + browseWidth = this->BrowseButton->minimumSizeHint().width(); + } + + // text width + int textWidth = 0; + if (&sh == &this->SizeHint || this->MinimumContentsLength == 0) + { + switch (SizeAdjustPolicy) + { + case ctkPathLineEdit::AdjustToContents: + case ctkPathLineEdit::AdjustToContentsOnFirstShow: + if (this->LineEdit->text().isEmpty()) + { + textWidth = 7 * this->LineEdit->fontMetrics().width(QLatin1Char('x')); + } + else + { + textWidth = this->LineEdit->fontMetrics().boundingRect(this->LineEdit->text()).width() + 8; + } + break; + case QComboBox::AdjustToMinimumContentsLength: + default: + ; + } + } + + if (this->MinimumContentsLength > 0) + { + textWidth = qMax(textWidth, this->MinimumContentsLength * this->LineEdit->fontMetrics().width(QLatin1Char('X'))); + } + + int height = (this->ComboBox ? this->ComboBox->minimumSizeHint() : + this->LineEdit->minimumSizeHint()).height(); + sh.rwidth() = frame + textWidth + browseWidth; + sh.rheight() = height; + } + return sh.expandedTo(QApplication::globalStrut()); } //----------------------------------------------------------------------------- @@ -106,7 +250,66 @@ void ctkPathLineEditPrivate::updateFilter() ctk::nameFiltersToExtensions(this->NameFilters), this->Filters | QDir::NoDotAndDotDot | QDir::AllDirs, QDir::Name|QDir::DirsLast, newCompleter)); - this->ComboBox->setCompleter(newCompleter); + this->LineEdit->setCompleter(newCompleter); + + QObject::connect(this->LineEdit->completer()->completionModel(), SIGNAL(layoutChanged()), + q, SLOT(_q_recomputeCompleterPopupSize())); + + // don't accept invalid path + QRegExpValidator* validator = new QRegExpValidator( + ctk::nameFiltersToRegExp(this->NameFilters), q); + this->LineEdit->setValidator(validator); +} + +//----------------------------------------------------------------------------- +void ctkPathLineEditPrivate::adjustPathLineEditSize() +{ + Q_Q(ctkPathLineEdit); + if (q->sizeAdjustPolicy() == ctkPathLineEdit::AdjustToContents) + { + q->updateGeometry(); + q->adjustSize(); + q->update(); + } +} + +//----------------------------------------------------------------------------- +void ctkPathLineEditPrivate::_q_recomputeCompleterPopupSize() +{ + QSize lineEditSize = this->LineEdit->size(); + + QAbstractItemView* view = this->LineEdit->completer()->popup(); + const QFontMetrics& fm = view->fontMetrics(); + + int iconWidth = 0; + int textWidth = 0; + + QStyleOptionFrame option; + int frameWidth = view->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, view); + int frame = 2 * frameWidth + + view->contentsMargins().left() + + view->contentsMargins().right(); + + QAbstractItemModel* model = this->LineEdit->completer()->completionModel(); + for (int i = 0; i < model->rowCount(); ++i) + { + QVariant icon = model->data(model->index(i, 0), Qt::DecorationRole); + if (icon.isValid() && icon.canConvert()) + { + iconWidth = qMax(iconWidth, icon.value().availableSizes().front().width() + 4); + } + textWidth = qMax(textWidth, fm.boundingRect(model->data(model->index(i, 0)).toString()).width()); + } + + view->setMinimumWidth(qMax(frame + iconWidth + textWidth, lineEditSize.width())); +} + +//----------------------------------------------------------------------------- +QString ctkPathLineEditPrivate::settingKey()const +{ + Q_Q(const ctkPathLineEdit); + return QString("ctkPathLineEdit/") + + (this->SettingKey.isEmpty() ? q->objectName() : this->SettingKey); } //----------------------------------------------------------------------------- @@ -162,9 +365,6 @@ void ctkPathLineEdit::setNameFilters(const QStringList &nameFilters) Q_D(ctkPathLineEdit); d->NameFilters = nameFilters; d->updateFilter(); - d->ComboBox->lineEdit()->setValidator( - new QRegExpValidator( - ctk::nameFiltersToRegExp(d->NameFilters), this)); } //----------------------------------------------------------------------------- @@ -273,12 +473,18 @@ void ctkPathLineEdit::browse() void ctkPathLineEdit::retrieveHistory() { Q_D(ctkPathLineEdit); + if (d->ComboBox == 0) + { + return; + } + QString path = this->currentPath(); + bool wasBlocking = this->blockSignals(true); d->ComboBox->clear(); // fill the combobox using the QSettings QSettings settings; - QString key = "ctkPathLineEdit/" + this->objectName(); - QStringList history = settings.value(key).toStringList(); - foreach(QString path, history) + QString key = d->settingKey(); + const QStringList history = settings.value(key).toStringList(); + foreach(const QString& path, history) { d->ComboBox->addItem(path); if (d->ComboBox->count() >= ctkPathLineEditPrivate::sMaxHistory) @@ -286,25 +492,32 @@ void ctkPathLineEdit::retrieveHistory() break; } } - //select the most recent file location - if (this->currentPath().isEmpty()) + // Restore path or select the most recent file location if none set. + if (path.isEmpty()) { + this->blockSignals(wasBlocking); d->ComboBox->setCurrentIndex(0); } + else + { + this->setCurrentPath(path); + this->blockSignals(wasBlocking); + } } //----------------------------------------------------------------------------- void ctkPathLineEdit::addCurrentPathToHistory() { Q_D(ctkPathLineEdit); - if (this->currentPath().isEmpty()) + if (d->ComboBox == 0 || + this->currentPath().isEmpty()) { return; } QSettings settings; //keep the same values, add the current value //if more than m_MaxHistory entrees, drop the oldest. - QString key = "ctkPathLineEdit/" + this->objectName(); + QString key = d->settingKey(); QStringList history = settings.value(key).toStringList(); if (history.contains(this->currentPath())) { @@ -312,7 +525,7 @@ void ctkPathLineEdit::addCurrentPathToHistory() } history.push_front(this->currentPath()); settings.setValue(key, history); - int index =d->ComboBox->findText(this->currentPath()); + int index = d->ComboBox->findText(this->currentPath()); if (index >= 0) { d->ComboBox->removeItem(index); @@ -352,14 +565,14 @@ QComboBox* ctkPathLineEdit::comboBox() const QString ctkPathLineEdit::currentPath()const { Q_D(const ctkPathLineEdit); - return d->ComboBox->currentText(); + return d->LineEdit ? d->LineEdit->text() : QString(); } //------------------------------------------------------------------------------ void ctkPathLineEdit::setCurrentPath(const QString& path) { Q_D(ctkPathLineEdit); - d->ComboBox->setEditText(path); + d->LineEdit->setText(path); } //------------------------------------------------------------------------------ @@ -374,16 +587,126 @@ void ctkPathLineEdit::updateHasValidInput() Q_D(ctkPathLineEdit); bool oldHasValidInput = d->HasValidInput; - d->HasValidInput = d->ComboBox->lineEdit()->hasAcceptableInput(); + d->HasValidInput = d->LineEdit->hasAcceptableInput(); if (d->HasValidInput) { - QFileInfo fileInfo(d->ComboBox->currentText()); + QFileInfo fileInfo(this->currentPath()); ctkPathLineEditPrivate::sCurrentDirectory = fileInfo.isFile() ? fileInfo.absolutePath() : fileInfo.absoluteFilePath(); - emit currentPathChanged(d->ComboBox->currentText()); + emit currentPathChanged(this->currentPath()); } - if ( d->HasValidInput != oldHasValidInput) + if (d->HasValidInput != oldHasValidInput) { emit validInputChanged(d->HasValidInput); } + + if (d->SizeAdjustPolicy == AdjustToContents) + { + d->SizeHint = QSize(); + d->adjustPathLineEditSize(); + this->updateGeometry(); + } +} + +//------------------------------------------------------------------------------ +QString ctkPathLineEdit::settingKey()const +{ + Q_D(const ctkPathLineEdit); + return d->SettingKey; +} + +//------------------------------------------------------------------------------ +void ctkPathLineEdit::setSettingKey(const QString& key) +{ + Q_D(ctkPathLineEdit); + d->SettingKey = key; + this->retrieveHistory(); +} + +//------------------------------------------------------------------------------ +bool ctkPathLineEdit::showBrowseButton()const +{ + Q_D(const ctkPathLineEdit); + return d->BrowseButton->isVisibleTo(const_cast(this)); +} + +//------------------------------------------------------------------------------ +void ctkPathLineEdit::setShowBrowseButton(bool visible) +{ + Q_D(ctkPathLineEdit); + d->BrowseButton->setVisible(visible); +} + +//------------------------------------------------------------------------------ +bool ctkPathLineEdit::showHistoryButton()const +{ + Q_D(const ctkPathLineEdit); + return d->ComboBox ? true: false; +} + +//------------------------------------------------------------------------------ +void ctkPathLineEdit::setShowHistoryButton(bool visible) +{ + Q_D(ctkPathLineEdit); + d->createPathLineEditWidget(visible); +} + +//------------------------------------------------------------------------------ +ctkPathLineEdit::SizeAdjustPolicy ctkPathLineEdit::sizeAdjustPolicy() const +{ + Q_D(const ctkPathLineEdit); + return d->SizeAdjustPolicy; +} + +//------------------------------------------------------------------------------ +void ctkPathLineEdit::setSizeAdjustPolicy(ctkPathLineEdit::SizeAdjustPolicy policy) +{ + Q_D(ctkPathLineEdit); + if (policy == d->SizeAdjustPolicy) + return; + + d->SizeAdjustPolicy = policy; + d->SizeHint = QSize(); + d->adjustPathLineEditSize(); + this->updateGeometry(); +} + +//------------------------------------------------------------------------------ +int ctkPathLineEdit::minimumContentsLength()const +{ + Q_D(const ctkPathLineEdit); + return d->MinimumContentsLength; +} + +//------------------------------------------------------------------------------ +void ctkPathLineEdit::setMinimumContentsLength(int length) +{ + Q_D(ctkPathLineEdit); + if (d->MinimumContentsLength == length || length < 0) return; + + d->MinimumContentsLength = length; + + if (d->SizeAdjustPolicy == AdjustToContents || + d->SizeAdjustPolicy == AdjustToMinimumContentsLength) + { + d->SizeHint = QSize(); + d->adjustPathLineEditSize(); + this->updateGeometry(); + } +} + +//------------------------------------------------------------------------------ +QSize ctkPathLineEdit::minimumSizeHint()const +{ + Q_D(const ctkPathLineEdit); + return d->recomputeSizeHint(d->MinimumSizeHint); } + +//------------------------------------------------------------------------------ +QSize ctkPathLineEdit::sizeHint()const +{ + Q_D(const ctkPathLineEdit); + return d->recomputeSizeHint(d->SizeHint); +} + +#include "moc_ctkPathLineEdit.h" diff --git a/Libs/Widgets/ctkPathLineEdit.h b/Libs/Widgets/ctkPathLineEdit.h index 14d98b09c3..aa5663beaa 100644 --- a/Libs/Widgets/ctkPathLineEdit.h +++ b/Libs/Widgets/ctkPathLineEdit.h @@ -46,15 +46,15 @@ MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. #define __ctkPathLineEdit_h // Qt includes -#include #include +#include class QComboBox; // CTK includes #include "ctkWidgetsExport.h" class ctkPathLineEditPrivate; -/** +/** * \ingroup Widgets * \brief Advanced line edit to select file or directory */ @@ -74,9 +74,42 @@ class CTK_WIDGETS_EXPORT ctkPathLineEdit: public QWidget Q_PROPERTY(QFileDialog::Options options READ options WRITE setOptions) #else Q_PROPERTY(Options options READ options WRITE setOptions) - Q_FLAGS(Option Options); + Q_FLAGS(Option Options) #endif + /// This property controls the key used to search the settings for recorded + /// paths. + /// If multiple path line edits share the same key, their history is then + /// shared. + /// If an empty key string is given, the object name is used as key. + /// Setting the key automatically retrieve the history from settings + /// Empty by default. + /// \sa retrieveHistory(), addCurrentPathToHistory(), showHistoryButton + Q_PROPERTY(QString settingKey READ settingKey WRITE setSettingKey ) + + /// This property controls whether the browse ("...") button is visible or + /// not. Clicking on the button calls opens a dialog to select the current path. + /// True by default + /// \sa browse() + Q_PROPERTY(bool showBrowseButton READ showBrowseButton WRITE setShowBrowseButton) + + /// This property controls whether the history button (arrow button that opens + /// the history menu) is visible or not. + /// True by default. + /// \sa retrieveHistory(), addCurrentPathToHistory(), settingKey + Q_PROPERTY(bool showHistoryButton READ showHistoryButton WRITE setShowHistoryButton) + + /// This property holds the policy describing how the size of the path line edit widget + /// changes when the content changes. + /// The default value is AdjustToContentsOnFirstShow. + Q_PROPERTY(SizeAdjustPolicy sizeAdjustPolicy READ sizeAdjustPolicy WRITE setSizeAdjustPolicy) + + /// This property holds the minimum number of characters that should fit into + /// the path line edit. + /// The default value is 0. + /// If this property is set to a positive value, the minimumSizeHint() and sizeHint() take it into account. + Q_PROPERTY(int minimumContentsLength READ minimumContentsLength WRITE setMinimumContentsLength) + public: enum Filter { Dirs = 0x001, Files = 0x002, @@ -116,6 +149,17 @@ class CTK_WIDGETS_EXPORT ctkPathLineEdit: public QWidget Q_DECLARE_FLAGS(Options, Option) #endif + enum SizeAdjustPolicy + { + /// The path line edit will always adjust to the contents. + AdjustToContents, + /// The path line edit will adjust to its contents the first time it is shown. + AdjustToContentsOnFirstShow, + /// The combobox will adjust to minimumContentsLength. For performance reasons + /// use this policy on large models. + AdjustToMinimumContentsLength + }; + /** Default constructor */ ctkPathLineEdit(QWidget *parent = 0); @@ -152,14 +196,43 @@ class CTK_WIDGETS_EXPORT ctkPathLineEdit: public QWidget const Options& options()const; #endif - /** Change the current extension of the edit line. - * If there is no extension yet, set it - */ + /// Change the current extension of the edit line. + /// If there is no extension yet, set it void setCurrentFileExtension(const QString& extension); + QString settingKey()const; + void setSettingKey(const QString& key); + + bool showBrowseButton()const; + void setShowBrowseButton(bool visible); + + bool showHistoryButton()const; + void setShowHistoryButton(bool visible); + + /// the policy describing how the size of the combobox changes + /// when the content changes + /// + /// The default value is \c AdjustToContentsOnFirstShow. + /// + /// \sa SizeAdjustPolicy + SizeAdjustPolicy sizeAdjustPolicy() const; + + void setSizeAdjustPolicy(SizeAdjustPolicy policy); + + int minimumContentsLength()const; + void setMinimumContentsLength(int lenght); + /// Return the combo box internally used by the path line edit QComboBox* comboBox() const; + /// The width returned, in pixels, is the length of the file name (with no + /// path) if any. Otherwise, it's enough for 15 to 20 characters. + virtual QSize minimumSizeHint()const; + + /// The width returned, in pixels, is the entire length of the current path + /// if any. Otherwise, it's enough for 15 to 20 characters. + virtual QSize sizeHint()const; + Q_SIGNALS: /** the signal is emit when the state of hasValidInput changed */ @@ -170,20 +243,22 @@ class CTK_WIDGETS_EXPORT ctkPathLineEdit: public QWidget public Q_SLOTS: void setCurrentPath(const QString& path); - /** Open a QFileDialog to select a file or directory and set current text to it - * You would probably connect a browse push button like this: - * connect(myPushButton,SIGNAL(clicked()),myPathLineEdit,SLOT(browse())) - */ + /// Open a QFileDialog to select a file or directory and set current text to it + /// You would probably connect a browse push button like this: + /// connect(myPushButton,SIGNAL(clicked()),myPathLineEdit,SLOT(browse())) + /// As a conveniency, such button is provided by default via the browseButton + /// \sa showBrowseButton void browse(); - /** Load the history of the paths. To be restored the inputs must have been saved by - * saveCurrentPathInHistory(). - */ - + /// Load the history of the paths that have been saved in the application + /// settings with addCurrentPathToHistory(). + /// The history is identified using \a settingKey + /// \sa addCurrentPathToHistory(), showHistoryButton, settingKey void retrieveHistory(); - /** Save the current value (this->currentPath()) into the history. That value will be retrieved - * next time the retrieveHistory() - */ + + /// Save the current value (this->currentPath()) into the history. That value + /// will be retrieved next time retrieveHistory() is called. + /// \sa retrieveHistory(), showHistoryButton, settingKey void addCurrentPathToHistory(); protected Q_SLOTS: @@ -196,6 +271,8 @@ protected Q_SLOTS: private: Q_DECLARE_PRIVATE(ctkPathLineEdit); Q_DISABLE_COPY(ctkPathLineEdit); + + Q_PRIVATE_SLOT(d_ptr, void _q_recomputeCompleterPopupSize()) }; Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPathLineEdit::Filters) diff --git a/Libs/Widgets/ctkPathListButtonsWidget.cpp b/Libs/Widgets/ctkPathListButtonsWidget.cpp new file mode 100644 index 0000000000..be5f240b2c --- /dev/null +++ b/Libs/Widgets/ctkPathListButtonsWidget.cpp @@ -0,0 +1,716 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) University College London. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include + +// Qt includes +#include +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkPathListButtonsWidget.h" +#include "ctkPathListButtonsWidget_p.h" + +//----------------------------------------------------------------------------- +// ctkPathListButtonsWidgetPrivate methods + +//----------------------------------------------------------------------------- +ctkPathListButtonsWidgetPrivate::~ctkPathListButtonsWidgetPrivate() +{ +} + +//----------------------------------------------------------------------------- +ctkPathListButtonsWidgetPrivate::ctkPathListButtonsWidgetPrivate(ctkPathListButtonsWidget& object) + : QObject(&object) + , q_ptr(&object) + , PathListWidget(NULL) +{ +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::init() +{ + Q_Q(ctkPathListButtonsWidget); + this->setupUi(q); + + q->unsetIconAddFilesButton(); + q->unsetIconAddDirectoryButton(); + q->unsetIconRemoveButton(); + q->unsetIconEditButton(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::setupUi(QWidget * widget) +{ + this->Ui_ctkPathListButtonsWidget::setupUi(widget); + + connect(this->AddFilesButton, SIGNAL(clicked()), SLOT(on_AddFilesButton_clicked())); + connect(this->AddDirectoryButton, SIGNAL(clicked()), SLOT(on_AddDirButton_clicked())); + connect(this->RemoveButton, SIGNAL(clicked()), SLOT(on_RemoveButton_clicked())); + connect(this->EditButton, SIGNAL(clicked()), SLOT(on_EditButton_clicked())); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::on_AddFilesButton_clicked() +{ + if (!this->PathListWidget) return; + + QStringList paths = this->openAddFilesDialog(true); + this->addPathsWithWarningMessage(paths); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::on_AddDirButton_clicked() +{ + if (!this->PathListWidget) return; + + QStringList paths = this->openAddDirDialog(); + this->addPathsWithWarningMessage(paths); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::on_RemoveButton_clicked() +{ + if (!this->PathListWidget) return; + + this->PathListWidget->removeSelectedPaths(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::on_EditButton_clicked() +{ + Q_Q(ctkPathListButtonsWidget); + + if (!this->PathListWidget) return; + + QString currentPath = this->PathListWidget->currentPath(true); + + QStringList paths; + if (this->PathListWidget->isFile(currentPath)) + { + paths = this->openAddFilesDialog(false); + } + else + { + paths = this->openAddDirDialog(); + } + + if (!paths.isEmpty()) + { + if (!this->PathListWidget->editPath(currentPath, paths.front())) + { + QMessageBox::information(q, tr("Editing the path failed"), + QString(tr("Failed to change path:\n\n%1\n\nto path\n\n%2\n\nPlease check your permissions.")) + .arg(currentPath).arg(paths.front())); + } + } +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::on_PathListWidget_selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ + Q_UNUSED(selected) + Q_UNUSED(deselected) + + bool hasSelection = this->PathListWidget->selectionModel()->hasSelection(); + this->EditButton->setEnabled(hasSelection); + this->RemoveButton->setEnabled(hasSelection); +} + +//----------------------------------------------------------------------------- +QStringList ctkPathListButtonsWidgetPrivate::openAddFilesDialog(bool multiple) +{ + Q_Q(ctkPathListButtonsWidget); + + if (!this->PathListWidget) return QStringList(); + + QString caption; + if (multiple) + { + caption = tr("Select one or more files"); + } + else + { + caption = tr("Select a file"); + } + + QFileDialog fileDialog(q, caption); + fileDialog.setReadOnly(true); + + if (multiple) + { + fileDialog.setFileMode(QFileDialog::ExistingFiles); + } + else + { + fileDialog.setFileMode(QFileDialog::ExistingFile); + } + + QString currentPath = this->PathListWidget->currentPath(true); + currentPath = currentPath.left(currentPath.lastIndexOf('/') + 1); + if (!currentPath.isEmpty()) + { + fileDialog.setDirectory(currentPath); + } + + // We use a proxy model as a workaround for the broken QFileDialog::setFilter() method. + // See for example https://bugreports.qt-project.org/browse/QTBUG-10244 + class FileFilterProxyModel : public QSortFilterProxyModel + { + public: + FileFilterProxyModel(ctkPathListWidget::PathOptions fileOptions) + : FileOptions(fileOptions) + {} + + protected: + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const + { + QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); + QFileSystemModel* fileModel = qobject_cast(sourceModel()); + + QFileInfo fileInfo = fileModel->fileInfo(sourceIndex); + + if(fileInfo.isFile()) + { + if (FileOptions.testFlag(ctkPathListWidget::Readable) && + !fileInfo.isReadable()) + { + return false; + } + if (FileOptions.testFlag(ctkPathListWidget::Writable) && + !fileInfo.isWritable()) + { + return false; + } + if (FileOptions.testFlag(ctkPathListWidget::Executable)&& + !fileInfo.isExecutable()) + { + return false; + } + return true; + } + else + { + // Show all readable directories + return fileInfo.isReadable(); + } + } + + private: + ctkPathListWidget::PathOptions FileOptions; + }; + + fileDialog.setProxyModel(new FileFilterProxyModel(this->PathListWidget->fileOptions())); + + if (fileDialog.exec() == QDialog::Accepted) + { + return fileDialog.selectedFiles(); + } + return QStringList(); +} + +//----------------------------------------------------------------------------- +QStringList ctkPathListButtonsWidgetPrivate::openAddDirDialog() +{ + Q_Q(ctkPathListButtonsWidget); + + if (!this->PathListWidget) return QStringList(); + + QString caption = tr("Select a directory"); + QFileDialog fileDialog(q, caption); + + fileDialog.setFileMode(QFileDialog::Directory); + fileDialog.setOption(QFileDialog::ShowDirsOnly); + + QString currentPath = this->PathListWidget->currentPath(true); + if (!currentPath.isEmpty()) + { + fileDialog.setDirectory(currentPath); + } + + // We use a proxy model as a workaround for the broken QFileDialog::setFilter() method. + // See for example https://bugreports.qt-project.org/browse/QTBUG-10244 + class DirFilterProxyModel : public QSortFilterProxyModel + { + public: + DirFilterProxyModel(ctkPathListWidget::PathOptions dirOptions) + : DirOptions(dirOptions) + {} + + protected: + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const + { + QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); + QFileSystemModel* fileModel = qobject_cast(sourceModel()); + + QFileInfo fileInfo = fileModel->fileInfo(sourceIndex); + + if (DirOptions.testFlag(ctkPathListWidget::Readable) && + !fileInfo.isReadable()) + { + return false; + } + // Do not check for the Writable flag, since it makes navigation from + // non-writable folders to writable sub-folders hard. +// if (DirOptions.testFlag(ctkPathListWidget::Writable) && +// !fileInfo.isWritable()) +// { +// return false; +// } + return true; + } + + private: + ctkPathListWidget::PathOptions DirOptions; + }; + + fileDialog.setProxyModel(new DirFilterProxyModel(this->PathListWidget->directoryOptions())); + + if (fileDialog.exec() == QDialog::Accepted) + { + return fileDialog.selectedFiles(); + } + return QStringList(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidgetPrivate::addPathsWithWarningMessage(const QStringList& paths) +{ + Q_Q(ctkPathListButtonsWidget); + + QStringList addedPaths = this->PathListWidget->addPaths(paths); + if (addedPaths != paths) + { + QString problematicPaths; + foreach(const QString& path, paths) + { + if (!addedPaths.contains(path) && !this->PathListWidget->contains(path)) + { + problematicPaths += path + '\n'; + } + } + if (!problematicPaths.isEmpty()) + { + QMessageBox::information(q, tr("Adding paths failed"), + QString(tr("Failed to add the following paths:\n\n%1\nPlease check your permissions.")) + .arg(problematicPaths)); + } + } +} + + +//----------------------------------------------------------------------------- +// ctkPathListButtonsWidget methods + +//----------------------------------------------------------------------------- +ctkPathListButtonsWidget::~ctkPathListButtonsWidget() +{ +} + +void ctkPathListButtonsWidget::init(ctkPathListWidget *pathListWidget) +{ + Q_D(ctkPathListButtonsWidget); + d->PathListWidget = pathListWidget; + + if (d->PathListWidget->selectionModel()->selectedIndexes().isEmpty()) + { + d->RemoveButton->setEnabled(false); + d->EditButton->setEnabled(false); + } + + switch(d->PathListWidget->mode()) + { + case ctkPathListWidget::FilesOnly: + d->AddDirectoryButton->setVisible(false); + break; + case ctkPathListWidget::DirectoriesOnly: + d->AddFilesButton->setVisible(false); + break; + default: + break; + } + + connect(d->PathListWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + d, SLOT(on_PathListWidget_selectionChanged(QItemSelection,QItemSelection))); + connect(d->PathListWidget, SIGNAL(pathActivated(QString)), d, SLOT(on_EditButton_clicked())); +} + +//----------------------------------------------------------------------------- +ctkPathListButtonsWidget::ctkPathListButtonsWidget(QWidget* newParent) + : Superclass(newParent) + , d_ptr(new ctkPathListButtonsWidgetPrivate(*this)) +{ + Q_D(ctkPathListButtonsWidget); + d->init(); +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidget::isAddFilesButtonVisible() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton->isVisible(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setAddFilesButtonVisible(bool visible) +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setVisible(visible); +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidget::isAddDirectoryButtonVisible() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddDirectoryButton->isVisible(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setAddDirectoryButtonVisible(bool visible) +{ + Q_D(ctkPathListButtonsWidget); + d->AddDirectoryButton->setVisible(visible); +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidget::isRemoveButtonVisible() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->RemoveButton->isVisible(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setRemoveButtonVisible(bool visible) +{ + Q_D(ctkPathListButtonsWidget); + d->RemoveButton->setVisible(visible); +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidget::isEditButtonVisible() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->EditButton->isVisible(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setEditButtonVisible(bool visible) +{ + Q_D(ctkPathListButtonsWidget); + d->EditButton->setVisible(visible); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::textAddFilesButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton->text(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::textAddDirectoryButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddDirectoryButton->text(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::textRemoveButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->RemoveButton->text(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::textEditButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->EditButton->text(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setTextAddFilesButton(const QString& text) +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setText(text); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setTextAddDirectoryButton(const QString& text) +{ + Q_D(ctkPathListButtonsWidget); + d->AddDirectoryButton->setText(text); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setTextRemoveButton(const QString& text) +{ + Q_D(ctkPathListButtonsWidget); + d->RemoveButton->setText(text); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setTextEditButton(const QString& text) +{ + Q_D(ctkPathListButtonsWidget); + d->EditButton->setText(text); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::toolTipAddFilesButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton->toolTip(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::toolTipAddDirectoryButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddDirectoryButton->toolTip(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::toolTipRemoveButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->RemoveButton->toolTip(); +} + +//----------------------------------------------------------------------------- +QString ctkPathListButtonsWidget::toolTipEditButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->EditButton->toolTip(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setToolTipAddFilesButton(const QString& toolTip) +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setToolTip(toolTip); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setToolTipAddDirectoryButton(const QString& toolTip) +{ + Q_D(ctkPathListButtonsWidget); + d->AddDirectoryButton->setToolTip(toolTip); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setToolTipRemoveButton(const QString& toolTip) +{ + Q_D(ctkPathListButtonsWidget); + d->RemoveButton->setToolTip(toolTip); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setToolTipEditButton(const QString& toolTip) +{ + Q_D(ctkPathListButtonsWidget); + d->EditButton->setToolTip(toolTip); +} + +//----------------------------------------------------------------------------- +QIcon ctkPathListButtonsWidget::iconAddFilesButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton->icon(); +} + +//----------------------------------------------------------------------------- +QIcon ctkPathListButtonsWidget::iconAddDirectoryButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddDirectoryButton->icon(); +} + +//----------------------------------------------------------------------------- +QIcon ctkPathListButtonsWidget::iconRemoveButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->RemoveButton->icon(); +} + +//----------------------------------------------------------------------------- +QIcon ctkPathListButtonsWidget::iconEditButton() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->EditButton->icon(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setIconAddFilesButton(const QIcon& icon) +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setIcon(icon); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setIconAddDirectoryButton(const QIcon& icon) +{ + Q_D(ctkPathListButtonsWidget); + d->AddDirectoryButton->setIcon(icon); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setIconRemoveButton(const QIcon& icon) +{ + Q_D(ctkPathListButtonsWidget); + d->RemoveButton->setIcon(icon); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setIconEditButton(const QIcon& icon) +{ + Q_D(ctkPathListButtonsWidget); + d->EditButton->setIcon(icon); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::unsetIconAddFilesButton() +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setIcon(QIcon(":/Icons/plus.png")); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::unsetIconAddDirectoryButton() +{ + Q_D(ctkPathListButtonsWidget); + d->AddDirectoryButton->setIcon(QIcon(":/Icons/plus.png")); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::unsetIconRemoveButton() +{ + Q_D(ctkPathListButtonsWidget); + d->RemoveButton->setIcon(QIcon(":/Icons/minus.png")); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::unsetIconEditButton() +{ + Q_D(ctkPathListButtonsWidget); + d->EditButton->setIcon(QIcon(":/Icons/edit.png")); +} + +//----------------------------------------------------------------------------- +bool ctkPathListButtonsWidget::isButtonsAutoRaise() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton->autoRaise(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setButtonsAutoRaise(bool autoRaise) +{ + Q_D(ctkPathListButtonsWidget); + d->AddFilesButton->setAutoRaise(autoRaise); + d->AddDirectoryButton->setAutoRaise(autoRaise); + d->RemoveButton->setAutoRaise(autoRaise); + d->EditButton->setAutoRaise(autoRaise); +} + +//----------------------------------------------------------------------------- +int ctkPathListButtonsWidget::buttonSpacing() const +{ + return this->layout()->spacing(); +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setButtonSpacing(int spacing) +{ + this->layout()->setSpacing(spacing); +} + +//----------------------------------------------------------------------------- +Qt::Orientation ctkPathListButtonsWidget::orientation() const +{ + return qobject_cast(this->layout()) ? Qt::Vertical : Qt::Horizontal; +} + +//----------------------------------------------------------------------------- +void ctkPathListButtonsWidget::setOrientation(Qt::Orientation orientation) +{ + QVBoxLayout* verticalLayout = qobject_cast(this->layout()); + if (verticalLayout && orientation == Qt::Vertical) + { + return; + } + + QLayout* oldLayout = this->layout(); + QLayout* newLayout = NULL; + if (orientation == Qt::Vertical) + { + newLayout = new QVBoxLayout; + } + else + { + newLayout = new QHBoxLayout; + } + newLayout->setContentsMargins(0,0,0,0); + newLayout->setSpacing(oldLayout->spacing()); + + QLayoutItem* item = 0; + while((item = oldLayout->takeAt(0))) + { + if (item->widget()) + { + newLayout->addWidget(item->widget()); + } + } + delete oldLayout; + this->setLayout(newLayout); +} + +//----------------------------------------------------------------------------- +QToolButton *ctkPathListButtonsWidget::buttonAddFiles() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddFilesButton; +} + +//----------------------------------------------------------------------------- +QToolButton *ctkPathListButtonsWidget::buttonAddDirectory() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->AddDirectoryButton; +} + +//----------------------------------------------------------------------------- +QToolButton *ctkPathListButtonsWidget::buttonEdit() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->EditButton; +} + +//----------------------------------------------------------------------------- +QToolButton *ctkPathListButtonsWidget::buttonRemove() const +{ + Q_D(const ctkPathListButtonsWidget); + return d->RemoveButton; +} diff --git a/Libs/Widgets/ctkPathListButtonsWidget.h b/Libs/Widgets/ctkPathListButtonsWidget.h new file mode 100644 index 0000000000..44ccdfb93c --- /dev/null +++ b/Libs/Widgets/ctkPathListButtonsWidget.h @@ -0,0 +1,157 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) University College London. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkPathListButtonsWidget_h +#define __ctkPathListButtonsWidget_h + +// Qt includes +#include + + +// QtGUI includes +#include "ctkWidgetsExport.h" +#include "ctkPathListWidget.h" + +class ctkPathListButtonsWidgetPrivate; + +class QToolButton; + + +/// \ingroup Widgets +/// +/// \brief A widget with add, remove and edit buttons to be used together with ctkPathListWidget. +/// +/// This widget should be initialized with a ctkPathListWidget instance in order to work properly. +/// +/// \sa init(ctkPathListWidget*) +/// +/// \author m.clarkson@ucl.ac.uk +/// \author s.zelzer@dkfz-heidelberg.de +/// +class CTK_WIDGETS_EXPORT ctkPathListButtonsWidget : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(bool showAddFilesButton READ isAddFilesButtonVisible WRITE setAddFilesButtonVisible) + Q_PROPERTY(bool showAddDirectoryButton READ isAddDirectoryButtonVisible WRITE setAddDirectoryButtonVisible) + Q_PROPERTY(bool showRemoveButton READ isRemoveButtonVisible WRITE setRemoveButtonVisible) + Q_PROPERTY(bool showEditButton READ isEditButtonVisible WRITE setEditButtonVisible) + + Q_PROPERTY(QString textAddFilesButton READ textAddFilesButton WRITE setTextAddFilesButton) + Q_PROPERTY(QString textAddDirectoryButton READ textAddDirectoryButton WRITE setTextAddDirectoryButton) + Q_PROPERTY(QString textRemoveButton READ textRemoveButton WRITE setTextRemoveButton) + Q_PROPERTY(QString textEditButton READ textEditButton WRITE setTextEditButton) + + Q_PROPERTY(QString toolTipAddFilesButton READ toolTipAddFilesButton WRITE setToolTipAddFilesButton) + Q_PROPERTY(QString toolTipAddDirectoryButton READ toolTipAddDirectoryButton WRITE setToolTipAddDirectoryButton) + Q_PROPERTY(QString toolTipRemoveButton READ toolTipRemoveButton WRITE setToolTipRemoveButton) + Q_PROPERTY(QString toolTipEditButton READ toolTipEditButton WRITE setToolTipEditButton) + + Q_PROPERTY(QIcon iconAddFilesButton READ iconAddFilesButton WRITE setIconAddFilesButton RESET unsetIconAddFilesButton) + Q_PROPERTY(QIcon iconAddDirectoryButton READ iconAddDirectoryButton WRITE setIconAddDirectoryButton RESET unsetIconAddDirectoryButton) + Q_PROPERTY(QIcon iconRemoveButton READ iconRemoveButton WRITE setIconRemoveButton RESET unsetIconRemoveButton) + Q_PROPERTY(QIcon iconEditButton READ iconEditButton WRITE setIconEditButton RESET unsetIconEditButton) + + Q_PROPERTY(bool buttonsAutoRaise READ isButtonsAutoRaise WRITE setButtonsAutoRaise) + Q_PROPERTY(int buttonSpacing READ buttonSpacing WRITE setButtonSpacing) + + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + +public: + + /// Superclass typedef + typedef QWidget Superclass; + + ctkPathListButtonsWidget(QWidget* parent = 0); + virtual ~ctkPathListButtonsWidget(); + + /// Initialize this widget with a ctkPathListWidget. + void init(ctkPathListWidget* pathListWidget); + + bool isAddFilesButtonVisible() const; + void setAddFilesButtonVisible(bool visible); + + bool isAddDirectoryButtonVisible() const; + void setAddDirectoryButtonVisible(bool visible); + + bool isRemoveButtonVisible() const; + void setRemoveButtonVisible(bool visible); + + bool isEditButtonVisible() const; + void setEditButtonVisible(bool visible); + + QString textAddFilesButton() const; + QString textAddDirectoryButton() const; + QString textRemoveButton() const; + QString textEditButton() const; + + void setTextAddFilesButton(const QString& text); + void setTextAddDirectoryButton(const QString& text); + void setTextRemoveButton(const QString& text); + void setTextEditButton(const QString& text); + + QString toolTipAddFilesButton() const; + QString toolTipAddDirectoryButton() const; + QString toolTipRemoveButton() const; + QString toolTipEditButton() const; + + void setToolTipAddFilesButton(const QString& toolTip); + void setToolTipAddDirectoryButton(const QString& toolTip); + void setToolTipRemoveButton(const QString& toolTip); + void setToolTipEditButton(const QString& toolTip); + + QIcon iconAddFilesButton() const; + QIcon iconAddDirectoryButton() const; + QIcon iconRemoveButton() const; + QIcon iconEditButton() const; + + void setIconAddFilesButton(const QIcon& icon); + void setIconAddDirectoryButton(const QIcon& icon); + void setIconRemoveButton(const QIcon& icon); + void setIconEditButton(const QIcon& icon); + + void unsetIconAddFilesButton(); + void unsetIconAddDirectoryButton(); + void unsetIconRemoveButton(); + void unsetIconEditButton(); + + bool isButtonsAutoRaise() const; + void setButtonsAutoRaise(bool autoRaise); + + int buttonSpacing() const; + void setButtonSpacing(int spacing); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + + QToolButton* buttonAddFiles() const; + QToolButton* buttonAddDirectory() const; + QToolButton* buttonEdit() const; + QToolButton* buttonRemove() const; + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkPathListButtonsWidget) + Q_DISABLE_COPY(ctkPathListButtonsWidget) +}; + +#endif diff --git a/Libs/Widgets/ctkPathListButtonsWidget_p.h b/Libs/Widgets/ctkPathListButtonsWidget_p.h new file mode 100644 index 0000000000..ccb69f38d5 --- /dev/null +++ b/Libs/Widgets/ctkPathListButtonsWidget_p.h @@ -0,0 +1,74 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) University College London. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkPathListButtonsWidget_p_h +#define __ctkPathListButtonsWidget_p_h + +// Qt includes +#include + +// CTK includes +#include "ui_ctkPathListButtonsWidget.h" + +class ctkPathListButtonsWidget; +class ctkPathListWidget; + +class QFileDialog; + +//----------------------------------------------------------------------------- +class ctkPathListButtonsWidgetPrivate : public QObject, public Ui_ctkPathListButtonsWidget +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkPathListButtonsWidget) + +protected: + + ctkPathListButtonsWidget* const q_ptr; + +public: + + explicit ctkPathListButtonsWidgetPrivate(ctkPathListButtonsWidget& object); + virtual ~ctkPathListButtonsWidgetPrivate(); + + void init(); + void setupUi(QWidget * parent); + +public Q_SLOTS: + + void on_AddFilesButton_clicked(); + void on_AddDirButton_clicked(); + void on_RemoveButton_clicked(); + void on_EditButton_clicked(); + + void on_PathListWidget_selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + +public: + + ctkPathListWidget* PathListWidget; + +private: + + QStringList openAddFilesDialog(bool multiple = true); + QStringList openAddDirDialog(); + + void addPathsWithWarningMessage(const QStringList& paths); +}; + +#endif diff --git a/Libs/Widgets/ctkPathListWidget.cpp b/Libs/Widgets/ctkPathListWidget.cpp new file mode 100644 index 0000000000..2186cc2291 --- /dev/null +++ b/Libs/Widgets/ctkPathListWidget.cpp @@ -0,0 +1,849 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. + and was partially funded by NIH grant 3P41RR013218-12S1 + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include + +// QtGUI includes +#include "ctkPathListWidget.h" + +// -------------------------------------------------------------------------- +// ctkPathListWidgetPrivate + +//----------------------------------------------------------------------------- +class ctkPathListWidgetPrivate +{ + Q_DECLARE_PUBLIC(ctkPathListWidget) + +protected: + ctkPathListWidget* const q_ptr; + +public: + + enum PathType { + Unknown, + File, + Directory + }; + + ctkPathListWidgetPrivate(ctkPathListWidget& object); + + void _q_emitPathClicked(const QModelIndex &index); + void _q_emitPathDoubleClicked(const QModelIndex &index); + void _q_emitPathActivated(const QModelIndex &index); + void _q_emitCurrentPathChanged(const QModelIndex ¤t, const QModelIndex &previous); + + bool addPath(const QString& path); + bool removePath(const QString& path); + + void fileOptionsChanged(); + void directoryOptionsChanged(); + + PathType pathType(const QString& absolutePath) const; + + bool isValidPath(const QString& absoluteFilePath, PathType pathType) const; + bool isValidFile(const QString& absoluteFilePath) const; + bool isValidDir(const QString& absoluteDirPath) const; + + QStandardItemModel PathListModel; + ctkPathListWidget::Mode Mode; + ctkPathListWidget::PathOptions FileOptions; + ctkPathListWidget::PathOptions DirectoryOptions; + QIcon FileIcon; + QIcon DirectoryIcon; +}; + +// -------------------------------------------------------------------------- +// ctkPathListWidgetPrivate methods + +#include "moc_ctkPathListWidget.h" + +// -------------------------------------------------------------------------- +ctkPathListWidgetPrivate::ctkPathListWidgetPrivate(ctkPathListWidget& object) + : q_ptr(&object) + , Mode(ctkPathListWidget::Any) + , FileOptions(ctkPathListWidget::Exists | ctkPathListWidget::Readable) + , DirectoryOptions(ctkPathListWidget::Exists | ctkPathListWidget::Readable) +{ +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::_q_emitPathClicked(const QModelIndex &index) +{ + Q_Q(ctkPathListWidget); + emit q->pathClicked(this->PathListModel.data(index, ctkPathListWidget::AbsolutePathRole).toString()); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::_q_emitPathDoubleClicked(const QModelIndex &index) +{ + Q_Q(ctkPathListWidget); + emit q->pathDoubleClicked(this->PathListModel.data(index, ctkPathListWidget::AbsolutePathRole).toString()); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::_q_emitPathActivated(const QModelIndex &index) +{ + Q_Q(ctkPathListWidget); + emit q->pathActivated(this->PathListModel.data(index, ctkPathListWidget::AbsolutePathRole).toString()); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::_q_emitCurrentPathChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_Q(ctkPathListWidget); + QString currentPath = this->PathListModel.data(current, ctkPathListWidget::AbsolutePathRole).toString(); + QString previousPath = this->PathListModel.data(previous, ctkPathListWidget::AbsolutePathRole).toString(); + emit q->currentPathChanged(currentPath, previousPath); +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidgetPrivate::addPath(const QString& path) +{ + Q_Q(ctkPathListWidget); + QString absolutePath = QFileInfo(path).absoluteFilePath(); + if (q->contains(absolutePath)) + { + return false; + } + + PathType pathType = this->pathType(absolutePath); + if (!this->isValidPath(absolutePath, pathType)) + { + return false; + } + QStandardItem * item = new QStandardItem(path); + item->setData(QVariant(absolutePath), Qt::ToolTipRole); + item->setData(QVariant(absolutePath), ctkPathListWidget::AbsolutePathRole); + if (pathType == File && !this->FileIcon.isNull()) + { + item->setData(this->FileIcon, Qt::DecorationRole); + } + else if (pathType == Directory && !this->DirectoryIcon.isNull()) + { + item->setData(this->DirectoryIcon, Qt::DecorationRole); + } + this->PathListModel.appendRow(item); + return true; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidgetPrivate::removePath(const QString& path) +{ + QString absolutePath = QFileInfo(path).absoluteFilePath(); + QModelIndexList foundIndices = this->PathListModel.match(this->PathListModel.index(0, 0), + ctkPathListWidget::AbsolutePathRole, + absolutePath); + Q_ASSERT(foundIndices.count() < 2); + if (!foundIndices.isEmpty()) + { + this->PathListModel.removeRow(foundIndices.front().row()); + return true; + } + return false; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::fileOptionsChanged() +{ + QStringList removedPaths; + for(int i = 0; i < this->PathListModel.rowCount();) + { + QModelIndex index = this->PathListModel.index(i, 0); + QString filePath = this->PathListModel.data(index, ctkPathListWidget::AbsolutePathRole).toString(); + if (!this->isValidFile(filePath)) + { + this->PathListModel.removeRow(i); + removedPaths << filePath; + } + else + { + ++i; + } + } + + if (!removedPaths.empty()) + { + Q_Q(ctkPathListWidget); + emit q->pathsChanged(QStringList(), removedPaths); + } +} + +// -------------------------------------------------------------------------- +void ctkPathListWidgetPrivate::directoryOptionsChanged() +{ + QStringList removedPaths; + for(int i = 0; i < this->PathListModel.rowCount();) + { + QModelIndex index = this->PathListModel.index(i, 0); + QString dirPath = this->PathListModel.data(index, ctkPathListWidget::AbsolutePathRole).toString(); + if (!this->isValidDir(dirPath)) + { + this->PathListModel.removeRow(i); + removedPaths << dirPath; + } + else + { + ++i; + } + } + + if (!removedPaths.empty()) + { + Q_Q(ctkPathListWidget); + emit q->pathsChanged(QStringList(), removedPaths); + } +} + +// -------------------------------------------------------------------------- +ctkPathListWidgetPrivate::PathType ctkPathListWidgetPrivate::pathType(const QString& absolutePath) const +{ + QFileInfo fileInfo(absolutePath); + if (fileInfo.exists()) + { + if (fileInfo.isFile()) + { + return File; + } + else if (fileInfo.isDir()) + { + return Directory; + } + return Unknown; + } + // Check if path is a file or directory by looking for a trailing slash + else if (absolutePath.endsWith('/')) + { + return Directory; + } + else + { + return File; + } +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidgetPrivate::isValidPath(const QString& absoluteFilePath, PathType pathType) const +{ + switch (pathType) + { + case Unknown: + if (this->Mode == ctkPathListWidget::Any) + { + return true; + } + return false; + case File: + return this->isValidFile(absoluteFilePath); + case Directory: + return this->isValidDir(absoluteFilePath); + default: + return false; + } +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidgetPrivate::isValidFile(const QString& absoluteFilePath) const +{ + if (this->Mode == ctkPathListWidget::DirectoriesOnly) + { + return false; + } + + if (this->FileOptions.testFlag(ctkPathListWidget::None)) + { + return true; + } + else + { + QFileInfo fileInfo(absoluteFilePath); + if (fileInfo.exists()) + { + if (!fileInfo.isFile()) + { + return false; + } + if (this->FileOptions.testFlag(ctkPathListWidget::Readable) && + !fileInfo.isReadable()) + { + return false; + } + if (this->FileOptions.testFlag(ctkPathListWidget::Writable) && + !fileInfo.isWritable()) + { + return false; + } + if (this->FileOptions.testFlag(ctkPathListWidget::Executable) && + !fileInfo.isExecutable()) + { + return false; + } + return true; + } + else + { + return !this->FileOptions.testFlag(ctkPathListWidget::Exists); + } + } +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidgetPrivate::isValidDir(const QString& absoluteDirPath) const +{ + if (this->Mode == ctkPathListWidget::FilesOnly) + { + return false; + } + + if (this->DirectoryOptions.testFlag(ctkPathListWidget::None)) + { + return true; + } + else + { + QFileInfo fileInfo(absoluteDirPath); + if (fileInfo.exists()) + { + if (!fileInfo.isDir()) + { + return false; + } + if (this->DirectoryOptions.testFlag(ctkPathListWidget::Readable) && + !fileInfo.isReadable()) + { + return false; + } + if (this->DirectoryOptions.testFlag(ctkPathListWidget::Writable) && + !fileInfo.isWritable()) + { + return false; + } + if (this->DirectoryOptions.testFlag(ctkPathListWidget::Executable) && + !fileInfo.isExecutable()) + { + return false; + } + return true; + } + else + { + return !this->FileOptions.testFlag(ctkPathListWidget::Exists); + } + } +} + +// -------------------------------------------------------------------------- +// ctkPathListWidget methods + +// -------------------------------------------------------------------------- +ctkPathListWidget::ctkPathListWidget(QWidget* _parent) + : Superclass(_parent) + , d_ptr(new ctkPathListWidgetPrivate(*this)) +{ + Q_D(ctkPathListWidget); + + this->setSelectionBehavior(QAbstractItemView::SelectRows); + this->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->setEditTriggers(QAbstractItemView::NoEditTriggers); + + this->unsetFileIcon(); + this->unsetDirectoryIcon(); + + QListView::setModel(&d->PathListModel); + + // signals + this->connect(this, SIGNAL(clicked(QModelIndex)), SLOT(_q_emitPathClicked(QModelIndex))); + this->connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(_q_emitPathDoubleClicked(QModelIndex))); + this->connect(this, SIGNAL(activated(QModelIndex)), SLOT(_q_emitPathActivated(QModelIndex))); + this->connect(this->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + SLOT(_q_emitCurrentPathChanged(QModelIndex,QModelIndex))); +} + +// -------------------------------------------------------------------------- +ctkPathListWidget::~ctkPathListWidget() +{ +} + +// -------------------------------------------------------------------------- +ctkPathListWidget::Mode ctkPathListWidget::mode() const +{ + Q_D(const ctkPathListWidget); + return d->Mode; +} + +// -------------------------------------------------------------------------- +QIcon ctkPathListWidget::fileIcon() const +{ + Q_D(const ctkPathListWidget); + return d->FileIcon; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setFileIcon(const QIcon& icon) +{ + Q_D(ctkPathListWidget); + d->FileIcon = icon; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::unsetFileIcon() +{ + Q_D(ctkPathListWidget); + d->FileIcon = QApplication::style()->standardIcon(QStyle::SP_FileIcon); +} + +// -------------------------------------------------------------------------- +QIcon ctkPathListWidget::directoryIcon() const +{ + Q_D(const ctkPathListWidget); + return d->DirectoryIcon; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setDirectoryIcon(const QIcon& icon) +{ + Q_D(ctkPathListWidget); + d->DirectoryIcon = icon; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::unsetDirectoryIcon() +{ + Q_D(ctkPathListWidget); + d->DirectoryIcon = QApplication::style()->standardIcon(QStyle::SP_DirIcon); +} + +// -------------------------------------------------------------------------- +ctkPathListWidget::PathOptions ctkPathListWidget::fileOptions() const +{ + Q_D(const ctkPathListWidget); + return d->FileOptions; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setFileOptions(PathOptions fileOptions) +{ + Q_D(ctkPathListWidget); + if (d->FileOptions != fileOptions) + { + d->FileOptions = fileOptions; + d->fileOptionsChanged(); + } +} + +// -------------------------------------------------------------------------- +ctkPathListWidget::PathOptions ctkPathListWidget::directoryOptions() const +{ + Q_D(const ctkPathListWidget); + return d->DirectoryOptions; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setDirectoryOptions(PathOptions directoryOptions) +{ + Q_D(ctkPathListWidget); + if (d->DirectoryOptions != directoryOptions) + { + d->DirectoryOptions = directoryOptions; + d->directoryOptionsChanged(); + } +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::files(bool absolutePath) const +{ + Q_D(const ctkPathListWidget); + QStringList fileList; + int role = Qt::DisplayRole; + if (absolutePath) + { + role = ctkPathListWidget::AbsolutePathRole; + } + for(int i = 0; i < d->PathListModel.rowCount(); ++i) + { + QString filePath = d->PathListModel.data(d->PathListModel.index(i, 0), role).toString(); + if (d->pathType(filePath) == ctkPathListWidgetPrivate::File) + { + fileList << filePath; + } + } + return fileList; +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::directories(bool absolutePath) const +{ + Q_D(const ctkPathListWidget); + QStringList pathList; + int role = Qt::DisplayRole; + if (absolutePath) + { + role = ctkPathListWidget::AbsolutePathRole; + } + for(int i = 0; i < d->PathListModel.rowCount(); ++i) + { + QString dirPath = d->PathListModel.data(d->PathListModel.index(i, 0), role).toString(); + if (d->pathType(dirPath) == ctkPathListWidgetPrivate::Directory) + { + pathList << dirPath; + } + } + return pathList; +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::paths(bool absolutePath)const +{ + Q_D(const ctkPathListWidget); + QStringList pathList; + int role = Qt::DisplayRole; + if (absolutePath) + { + role = ctkPathListWidget::AbsolutePathRole; + } + for(int i = 0; i < d->PathListModel.rowCount(); ++i) + { + pathList << d->PathListModel.data(d->PathListModel.index(i, 0), role).toString(); + } + return pathList; +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::selectedPaths(bool absolutePath)const +{ + Q_D(const ctkPathListWidget); + QStringList pathList; + int role = Qt::DisplayRole; + if (absolutePath) + { + role = ctkPathListWidget::AbsolutePathRole; + } + QModelIndexList selectedIndexes = this->selectionModel()->selectedRows(); + foreach(const QModelIndex& index, selectedIndexes) + { + pathList << d->PathListModel.data(index, role).toString(); + } + return pathList; +} + +// -------------------------------------------------------------------------- +QString ctkPathListWidget::currentPath(bool absolutePath) const +{ + Q_D(const ctkPathListWidget); + + QModelIndex currentIndex = this->currentIndex(); + if (!currentIndex.isValid()) + { + return QString(); + } + + int role = absolutePath ? static_cast(AbsolutePathRole) + : static_cast(Qt::DisplayRole); + return d->PathListModel.data(currentIndex, role).toString(); +} + +// -------------------------------------------------------------------------- +int ctkPathListWidget::count() const +{ + return this->model()->rowCount(); +} + +// -------------------------------------------------------------------------- +QString ctkPathListWidget::path(int row) const +{ + Q_D(const ctkPathListWidget); + if (row < 0 || row >= count()) + { + return QString(); + } + return d->PathListModel.data(d->PathListModel.index(row, 0), AbsolutePathRole).toString(); +} + +// -------------------------------------------------------------------------- +QStandardItem* ctkPathListWidget::item(int row) const +{ + Q_D(const ctkPathListWidget); + return d->PathListModel.item(row); +} + +// -------------------------------------------------------------------------- +QStandardItem *ctkPathListWidget::item(const QString &absolutePath) const +{ + Q_D(const ctkPathListWidget); + QModelIndexList result = d->PathListModel.match(d->PathListModel.index(0,0), AbsolutePathRole, + absolutePath, 1, Qt::MatchExactly); + Q_ASSERT(result.count() < 2); + if (result.isEmpty()) + { + return NULL; + } + else + { + return d->PathListModel.item(result.front().row()); + } +} + +// -------------------------------------------------------------------------- +QString ctkPathListWidget::pathAt(const QPoint& point) const +{ + Q_D(const ctkPathListWidget); + return d->PathListModel.data(indexAt(point), AbsolutePathRole).toString(); +} + +// -------------------------------------------------------------------------- +QStandardItem* ctkPathListWidget::itemAt(const QPoint &point) const +{ + Q_D(const ctkPathListWidget); + QModelIndex index = this->indexAt(point); + if (index.isValid()) + { + return d->PathListModel.item(index.row()); + } + return NULL; +} + +// -------------------------------------------------------------------------- +int ctkPathListWidget::row(const QString& path) const +{ + Q_D(const ctkPathListWidget); + QModelIndexList result = d->PathListModel.match(d->PathListModel.index(0,0), AbsolutePathRole, + QFileInfo(path).absoluteFilePath(), 1, Qt::MatchExactly); + Q_ASSERT(result.count() < 2); + if (!result.isEmpty()) + { + return result.front().row(); + } + return -1; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::editPath(const QString &oldPath, const QString &newPath) +{ + Q_D(ctkPathListWidget); + + QString oldAbsolutePath = QFileInfo(oldPath).absoluteFilePath(); + QModelIndexList matched = d->PathListModel.match(d->PathListModel.index(0,0), AbsolutePathRole, + oldAbsolutePath, 1, Qt::MatchExactly); + Q_ASSERT(matched.size() < 2); + if (matched.isEmpty()) + { + return false; + } + return this->editPath(matched.front(), newPath); +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::editPath(const QModelIndex &index, const QString &newPath) +{ + Q_D(ctkPathListWidget); + + if (!index.isValid()) + { + return false; + } + + QString oldAbsolutePath = d->PathListModel.data(index, AbsolutePathRole).toString(); + ctkPathListWidgetPrivate::PathType oldPathType = d->pathType(oldAbsolutePath); + ctkPathListWidgetPrivate::PathType newPathType = d->pathType(newPath); + if (oldPathType != newPathType) + { + return false; + } + + if (!d->isValidPath(newPath, newPathType)) + { + return false; + } + + QString newAbsolutePath = QFileInfo(newPath).absoluteFilePath(); + d->PathListModel.setData(index, newPath, Qt::DisplayRole); + d->PathListModel.setData(index, newAbsolutePath, AbsolutePathRole); + + emit this->pathsChanged(QStringList(newAbsolutePath), QStringList(oldAbsolutePath)); + return true; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::isFile(const QString &path) const +{ + Q_D(const ctkPathListWidget); + return d->pathType(path) == ctkPathListWidgetPrivate::File; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::isDirectory(const QString &path) const +{ + Q_D(const ctkPathListWidget); + return d->pathType(path) == ctkPathListWidgetPrivate::Directory; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setMode(ctkPathListWidget::Mode mode) +{ + Q_D(ctkPathListWidget); + d->Mode = mode; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::contains(const QString& path)const +{ + Q_D(const ctkPathListWidget); + QString absolutePath = QFileInfo(path).absoluteFilePath(); + QModelIndexList foundIndexes = d->PathListModel.match( + d->PathListModel.index(0, 0), ctkPathListWidget::AbsolutePathRole, + QVariant(absolutePath), 1, Qt::MatchExactly); + Q_ASSERT(foundIndexes.size() < 2); + return (foundIndexes.size() != 0); +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::addPath(const QString& path) +{ + return !this->addPaths(QStringList() << path).empty(); +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::addPaths(const QStringList &paths) +{ + Q_D(ctkPathListWidget); + + QStringList addedPaths; + foreach(const QString& path, paths) + { + if (d->addPath(path)) + { + addedPaths << QFileInfo(path).absoluteFilePath(); + } + } + + if (!addedPaths.empty()) + { + emit this->pathsChanged(addedPaths, QStringList()); + } + return addedPaths; +} + +// -------------------------------------------------------------------------- +bool ctkPathListWidget::removePath(const QString& path) +{ + return !this->removePaths(QStringList() << path).empty(); +} + +// -------------------------------------------------------------------------- +QStringList ctkPathListWidget::removePaths(const QStringList &paths) +{ + Q_D(ctkPathListWidget); + + QStringList removedPaths; + foreach(const QString& path, paths) + { + if (d->removePath(path)) + { + removedPaths << QFileInfo(path).absoluteFilePath(); + } + } + + if (!removedPaths.empty()) + { + emit this->pathsChanged(QStringList(), removedPaths); + } + return removedPaths; +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::removeSelectedPaths() +{ + Q_D(ctkPathListWidget); + + QModelIndexList selectedIndexes = this->selectionModel()->selectedRows(); + if (selectedIndexes.empty()) return; + + QStringList removedPaths; + while(selectedIndexes.count() > 0) + { + removedPaths << d->PathListModel.data(selectedIndexes.front(), AbsolutePathRole).toString(); + d->PathListModel.removeRow(selectedIndexes.front().row()); + selectedIndexes = this->selectionModel()->selectedRows(); + } + + emit this->pathsChanged(QStringList(), removedPaths); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::clear() +{ + Q_D(ctkPathListWidget); + if (d->PathListModel.rowCount() == 0) return; + + QStringList removedPaths = this->paths(true); + d->PathListModel.clear(); + emit this->pathsChanged(QStringList(), removedPaths); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setPaths(const QStringList& paths) +{ + Q_D(ctkPathListWidget); + + QStringList addedPaths; + QStringList removedPaths; + + QStringList absolutePaths; + foreach(const QString& path, paths) + { + absolutePaths << QFileInfo(path).absoluteFilePath(); + } + + foreach(const QString& path, this->paths(true)) + { + if (!absolutePaths.contains(path) && d->removePath(path)) + { + removedPaths << path; + } + } + + for(int i = 0; i < paths.count(); ++i) + { + if (!this->contains(paths[i]) && d->addPath(paths[i])) + { + addedPaths << absolutePaths[i]; + } + } + + if (addedPaths.isEmpty() && removedPaths.empty()) + { + return; + } + + emit this->pathsChanged(addedPaths, removedPaths); +} + +// -------------------------------------------------------------------------- +void ctkPathListWidget::setModel(QAbstractItemModel*) +{ + Q_ASSERT(!"ctkPathListWidget::setModel() - Changing the model of the ctkPathListWidget is not allowed."); +} diff --git a/Libs/Widgets/ctkPathListWidget.h b/Libs/Widgets/ctkPathListWidget.h new file mode 100644 index 0000000000..4386b06a22 --- /dev/null +++ b/Libs/Widgets/ctkPathListWidget.h @@ -0,0 +1,322 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. + and was partially funded by NIH grant 3P41RR013218-12S1 + +=========================================================================*/ + +#ifndef __ctkPathListWidget_h +#define __ctkPathListWidget_h + +// Qt includes +#include + +// QtGUI includes +#include "ctkWidgetsExport.h" + +class ctkPathListWidgetPrivate; + +class QStandardItem; + +/// \ingroup Widgets +/// +/// \brief The ctkPathListWidget lists files and/or directories. +/// +/// The ctkPathListWidget is a QListView sub-class tailored specifically for displaying +/// lists of file and/or directory entries. A \e path denotes either a file or a directory. +/// Paths can be relative or absolute and the range of valid paths can be constrained +/// by setting file and directory options. +/// +class CTK_WIDGETS_EXPORT ctkPathListWidget : public QListView +{ + Q_OBJECT + + /// The current list of paths. + Q_PROPERTY(QStringList paths READ paths WRITE setPaths NOTIFY pathsChanged) + + /// The mode for this ctkPathListWidget. + Q_PROPERTY(Mode mode READ mode WRITE setMode) + + /// Constraints on the file type. + Q_PROPERTY(PathOptions fileOptions READ fileOptions WRITE setFileOptions) + + /// Constraints on the directory type. + Q_PROPERTY(PathOptions directoryOptions READ directoryOptions WRITE setDirectoryOptions) + + /// The icon to be shown for a file entry. + Q_PROPERTY(QIcon fileIcon READ fileIcon WRITE setFileIcon RESET unsetFileIcon) + + /// The icon to be shown for a directory entry. + Q_PROPERTY(QIcon directoryIcon READ directoryIcon WRITE setDirectoryIcon RESET unsetDirectoryIcon) + + Q_FLAGS(PathOption PathOptions) + Q_ENUMS(Mode) + +public: + + enum + { + /// A role for getting the absolute path from items in this list views model. + AbsolutePathRole = Qt::UserRole + 1 + }; + + /// Describes constraints on paths. + enum PathOption + { + /// No constraints + None = 0x00, + /// The path must exist in the file system. + Exists = 0x01, + /// The path must be readable by the current user. + Readable = 0x02, + /// The path must be writable by the current user. + Writable = 0x04, + /// The path must be executable by the current user. + Executable = 0x08 + }; + Q_DECLARE_FLAGS(PathOptions, PathOption) + + enum Mode + { + /// Allow all paths. + Any = 0, + /// Allow only file entries. + FilesOnly, + /// Allow only directory entries. + DirectoriesOnly + }; + + /// Superclass typedef + typedef QListView Superclass; + + /// Constructor + explicit ctkPathListWidget(QWidget* parent = 0); + + /// Destructor + virtual ~ctkPathListWidget(); + + /// \return The current widget mode. + Mode mode() const; + + /// \return The QIcon used for file entries. + QIcon fileIcon() const; + + /// Sets a QIcon to be used for file entries. + /// \param icon The new file entry icon. + void setFileIcon(const QIcon& icon); + + /// Un-set any custom file icon. + void unsetFileIcon(); + + /// \return The QIcon used for directory entries. + QIcon directoryIcon() const; + + /// Sets a QIcon to be used for directory entries. + /// \param icon The new directory entry icon. + void setDirectoryIcon(const QIcon& icon); + + /// Un-set any custom directory icon. + void unsetDirectoryIcon(); + + /// \return The file entry constraints. + PathOptions fileOptions() const; + + /// Set new file entry constraints. + /// \param fileOptions The file entry constraints. + void setFileOptions(PathOptions fileOptions); + + /// \return The directory entry constraints. + PathOptions directoryOptions() const; + + /// Set new directory entry constraints. + /// \param directoryOptions The directory entry constraints. + void setDirectoryOptions(PathOptions directoryOptions); + + /// Checks if an entry with the given \a path already exists. + /// \return true if the \a path has already been added, false otherwise. + bool contains(const QString& path)const; + + /// Get all file entries. + /// \param absolutePath If true, resolve all entries to absolute paths. + /// \return A list of all file entries. + QStringList files(bool absolutePath = false) const; + + /// Get all directory entries. + /// \param absolutePath If true, resolve all entries to absolute paths. + /// \return A list of all directory entries. + QStringList directories(bool absolutePath = false) const; + + /// Get all path entries. + /// \param absolutePath If true, resolve all entries to absolute paths. + /// \return A list of all entries. + QStringList paths(bool absolutePath = false) const; + + /// Get all selected path entries. + /// \param absolutePath If true, resolve all entries to absolute paths. + /// \return A list of all selected entries. + QStringList selectedPaths(bool absolutePath = false) const; + + /// Get the currently focused path entry. + /// \param absolutePath If true, resolve all entries to absolute paths. + /// \return The focused path entry or a null QString if no entry is focused. + QString currentPath(bool absolutePath = false) const; + + /// \return The current entry count. + int count() const; + + /// \return The absolute path for \a row or a null QString if \a row is out of range. + QString path(int row) const; + + /// \return The item for \a row or NULL if \a row is out of range. + QStandardItem* item(int row) const; + + /// \return The item for the given absolute path or NULL if the the path is not known. + QStandardItem* item(const QString& absolutePath) const; + + /// \return The absolute path for the entry located at the point \a point (in the + /// widget coordinate system) or a null QString if no entry could be found for \a point. + QString pathAt(const QPoint& point) const; + + /// \return The item for the entry located at the point \a point (in the widget + /// coordinate system) or NULL if no ite could be found for \a point. + QStandardItem* itemAt(const QPoint& point) const; + + /// \see pathAt(const QPoint&) + QString pathAt(int x, int y) const { return pathAt(QPoint(x, y)); } + + /// \see itemAt(const QPoint&) + QStandardItem* itemAt(int x, int y) const { return itemAt(QPoint(x, y)); } + + /// \return The row number for the given \a path or -1 if \a path is not in the list of current + /// entries. + int row(const QString& path) const; + + /// Changes \a oldPath to the new value given by \a newPath. Does nothing if \a oldPath is not + /// in the list or \a newPath does not fullfill the current path options (constraints). + /// \param oldPath The path to be edited. + /// \param newPath The new path replacing \a oldPath. + /// \return true if the old path was successfully changed, false otherwise. + bool editPath(const QString& oldPath, const QString& newPath); + + /// Changes the path value of \a index to \a newPath. + /// \param index The model index for which the path will be changed. + /// \param newPath The new path replacing the path value of \a index. + /// + /// \sa editPath(const QString&, const QString&) + bool editPath(const QModelIndex& index, const QString& newPath); + + /// \return Returns true if the given path is treated as a file, + /// false otherwise. + bool isFile(const QString& path) const; + + /// \return Returns true if the given path is treated as a directory, + /// false otherwise. + bool isDirectory(const QString& path) const; + +public Q_SLOTS: + + /// Set the mode for controlling the path type. + /// \param mode The path mode. + void setMode(Mode mode); + + /// Depending on the mode and path constraints, add \a path to the entry list and emit signal pathListChanged(). + /// \param path The path to add. + /// \return true if the path was added, false otherwise. + /// + /// \sa pathListChanged() + bool addPath(const QString& path); + + /// Depending on the mode and path constraints, add \a paths to the entry list and emit signal pathListChanged(). + /// \param paths The paths to add. + /// \return The absolute paths of all added entries from \a paths. + /// + /// \sa pathListChanged() + QStringList addPaths(const QStringList& paths); + + /// Remove all entries and set all valid entries in \a paths as the current list. + /// The signal pathListChanged() is emitted if the old list of paths is + /// different from the provided one. + /// \param paths The new path list. + /// + /// \sa addPaths(), pathListChanged() + void setPaths(const QStringList& paths); + + /// Remove \a path from the list. + /// The signal pathListChanged() is emitted if the path was in the list. + /// \param path The path to remove. + /// \return true if \a path was removed, false otherwise. + /// + /// \sa pathListChanged() + bool removePath(const QString& path); + + /// Remove \a paths from the list. + /// \param paths The paths to remove. + /// \return The absolute paths of all removed entries from \a paths. + QStringList removePaths(const QStringList& paths); + + /// Remove all currently selected paths from the list. + void removeSelectedPaths(); + + /// Remove all paths from the list. + void clear(); + +Q_SIGNALS: + + /// This signal is emitted when paths are added or removed to the list. + /// \param added The newly added absolute paths. + /// \param removed The removed absolute paths. + void pathsChanged(const QStringList& added, const QStringList& removed); + + /// The user clicked on a path entry. + void pathClicked(const QString& absolutePath); + + /// The user double-clicked on a path entry. + void pathDoubleClicked(const QString& absolutePath); + + /// This signal is emitted when the \a absolutePath entry is activated. The entry is activated when the user + /// clicks or double clicks on it, depending on the system configuration. It is also activated + /// when the user presses the activation key (on Windows and X11 this is the Return key, on + /// Mac OS X it is Ctrl+0). + void pathActivated(const QString& absolutePath); + + /// This signal is emitted whenever the current item changes. + /// \param currentAbsolutePath The new current path entry. + /// \param previousAbsolutePath The path entry that previously had the focus. + void currentPathChanged(const QString& currentAbsolutePath, const QString& previousAbsolutePath); + +protected: + QScopedPointer d_ptr; + +private: + + void setModel(QAbstractItemModel *model); + + Q_DECLARE_PRIVATE(ctkPathListWidget) + Q_DISABLE_COPY(ctkPathListWidget) + + Q_PRIVATE_SLOT(d_func(), void _q_emitPathClicked(const QModelIndex& index)) + Q_PRIVATE_SLOT(d_func(), void _q_emitPathDoubleClicked(const QModelIndex& index)) + Q_PRIVATE_SLOT(d_func(), void _q_emitPathActivated(const QModelIndex& index)) + Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentPathChanged(const QModelIndex &previous, const QModelIndex ¤t)) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPathListWidget::PathOptions) + +#endif + diff --git a/Libs/Widgets/ctkThumbnailLabel.cpp b/Libs/Widgets/ctkThumbnailLabel.cpp index e2ddca4099..71879d211a 100644 --- a/Libs/Widgets/ctkThumbnailLabel.cpp +++ b/Libs/Widgets/ctkThumbnailLabel.cpp @@ -45,6 +45,7 @@ class ctkThumbnailLabelPrivate: public Ui_ctkThumbnailLabel // Constructor ctkThumbnailLabelPrivate(ctkThumbnailLabel* parent); + virtual ~ctkThumbnailLabelPrivate(); virtual void setupUi(QWidget* widget); @@ -74,6 +75,11 @@ ctkThumbnailLabelPrivate::ctkThumbnailLabelPrivate(ctkThumbnailLabel* parent) this->TransformationMode = Qt::FastTransformation; } +//---------------------------------------------------------------------------- +ctkThumbnailLabelPrivate::~ctkThumbnailLabelPrivate() +{ +} + //---------------------------------------------------------------------------- void ctkThumbnailLabelPrivate::setupUi(QWidget* widget) { diff --git a/Plugins/org.commontk.slicermodule/CMakeLists.txt b/Plugins/org.commontk.slicermodule/CMakeLists.txt deleted file mode 100644 index ac0efcf2c8..0000000000 --- a/Plugins/org.commontk.slicermodule/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -project(org_commontk_slicermodule) - -set(PLUGIN_export_directive "org_commontk_slicermodule_EXPORT") - -set(PLUGIN_SRCS - ctkSlicerModulePlugin.cpp - ctkSlicerModulePlugin_p.h - ctkSlicerModuleReader.cpp - ctkSlicerModuleReader.h - ctkSlicerModuleStringConverter.h - ctkSlicerModuleStringConverter.cpp -) - -# Files which should be processed by Qts moc -set(PLUGIN_MOC_SRCS - ctkSlicerModulePlugin_p.h - ctkSlicerModuleReader.h - ctkSlicerModuleStringConverter.h -) - -# Qt Designer files which should be processed by Qts uic -set(PLUGIN_UI_FORMS -) - -# QRC Files which should be compiled into the plugin -set(PLUGIN_resources - Resources/org_commontk_slicermodule.qrc -) - - -# Update CTK_BASE_LIBRARIES with QT libraries -if(QT4_FOUND) - set(CTK_BASE_LIBRARIES ${CTK_BASE_LIBRARIES} ${QT_LIBRARIES} CACHE INTERNAL "CTK base libraries" FORCE) -endif() - -#Compute the plugin dependencies -ctkFunctionGetTargetLibraries(PLUGIN_target_libraries) - -ctkMacroBuildPlugin( - NAME ${PROJECT_NAME} - EXPORT_DIRECTIVE ${PLUGIN_export_directive} - SRCS ${PLUGIN_SRCS} - MOC_SRCS ${PLUGIN_MOC_SRCS} - UI_FORMS ${PLUGIN_UI_FORMS} - RESOURCES ${PLUGIN_resources} - TARGET_LIBRARIES ${PLUGIN_target_libraries} -) - -set( QT_USE_QTXML ON ) -set( QT_USE_QTXMLPATTERNS ON ) -include(${QT_USE_FILE}) -target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES}) - -# Testing -if(BUILD_TESTING) - add_subdirectory(Testing) -endif() diff --git a/Plugins/org.commontk.slicermodule/Resources/Xml/slicerModuleDescription.xsd b/Plugins/org.commontk.slicermodule/Resources/Xml/slicerModuleDescription.xsd deleted file mode 100644 index 778f875126..0000000000 --- a/Plugins/org.commontk.slicermodule/Resources/Xml/slicerModuleDescription.xsd +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Plugins/org.commontk.slicermodule/Resources/org_commontk_slicermodule.qrc b/Plugins/org.commontk.slicermodule/Resources/org_commontk_slicermodule.qrc deleted file mode 100644 index 6fe99d18c9..0000000000 --- a/Plugins/org.commontk.slicermodule/Resources/org_commontk_slicermodule.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - Xml/slicerModuleDescription.xsd - - diff --git a/Plugins/org.commontk.slicermodule/Testing/Cpp/CMakeLists.txt b/Plugins/org.commontk.slicermodule/Testing/Cpp/CMakeLists.txt deleted file mode 100644 index edd55336d9..0000000000 --- a/Plugins/org.commontk.slicermodule/Testing/Cpp/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(KIT ${PROJECT_NAME}) - -create_test_sourcelist(Tests ${KIT}CppTests.cpp - ctkSlicerModuleTest.cpp - ) - -SET (TestsToRun ${Tests}) -REMOVE (TestsToRun ${KIT}CppTests.cpp) - -set(LIBRARY_NAME ${PROJECT_NAME}) - -add_executable(${KIT}CppTests ${Tests}) -target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES}) - -set(TEST_DATA ${${PROJECT_NAME}_SOURCE_DIR}/TestData) - -# -# Add Tests -# - -SIMPLE_TEST( ctkSlicerModuleTest ${TEST_DATA}/ParserTest1.xml ) - diff --git a/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest1.xml b/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest1.xml deleted file mode 100644 index b8ce934f13..0000000000 --- a/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest1.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - registration - NAMIC sample registration - Registers two images together using a rigid transform and MI - 1.0 - - - Daniel Blezek - - - - Parameters used for registration - - HistogramBins - -b - --histogrambins - Number of histogram bins to use for Mattes Mutual Information - - 30 - - 1 - 500 - 5 - - - - - SpatialSamples - -s - --spatialsamples - Number of spatial samples to use in estimating Mattes Mutual Information - - 10000 - - 1000 - 50000 - 1000 - - - - - InitializeTransform - -u - --noinitializetransform - Calculate initial transform - - false - - - - Iterations - -i - --iterations - Comma separated list of iterations. Must have the same number of elements as learning rate - - 100,100,100,200 - - - - LearningRate - -l - --learningrate - Comma separated list of learning rates must have the same number of elements as iterations - - 0.005,0.001,0.0005,0.0002 - - - - TranslationScale - --translationscale - -t - Relative scale of translations to rotations, i.e. a value of 100 means 10mm = 1 degree - - 100.0 - - 10.0 - 500.0 - 50.0 - - - - - - - Input/output parameters - - FixedImage - - input - 0 - Fixed image to register to - - - MovingImage - - input - 1 - Moving image - - - OutputImage - - output - 2 - Resampled Moving Image - - - - diff --git a/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest2.xml b/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest2.xml deleted file mode 100644 index c0fb00eead..0000000000 --- a/Plugins/org.commontk.slicermodule/Testing/Cpp/TestData/ParserTest2.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - registration - NAMIC sample registration - Registers two images together using a rigid transform and MI - 1.0 - - - Daniel Blezek - - - - Parameters used for registration - - HistogramBins - -b - --histogrambins - Number of histogram bins to use for Mattes Mutual Information - - 30 - - 1 - 500 - 5 - - - - - SpatialSamples - -s - --spatialsamples - Number of spatial samples to use in estimating Mattes Mutual Information - - 10000 - - 1000 - 50000 - 1000 - - - - - InitializeTransform - -u - --noinitializetransform - Calculate initial transform - - false - - - - Iterations - -i - --iterations - Comma separated list of iterations. Must have the same number of elements as learning rate - - 100,100,100,200 - - - - LearningRate - -l - --learningrate - Comma separated list of learning rates must have the same number of elements as iterations - - 0.005,0.001,0.0005,0.0002 - - - - TranslationScale - --translationscale - -t - Relative scale of translations to rotations, i.e. a value of 100 means 10mm = 1 degree - - 100.0 - - 10.0 - 500.0 - 50.0 - - - - - - - Input/output parameters - - FixedImage - - input - 0 - Fixed image to register to - - - MovingImage - - input - 1 - Moving image - - - OutputImage - - output - 2 - Resampled Moving Image - - - - diff --git a/Plugins/org.commontk.slicermodule/Testing/Cpp/ctkSlicerModuleTest.cpp b/Plugins/org.commontk.slicermodule/Testing/Cpp/ctkSlicerModuleTest.cpp deleted file mode 100644 index 3279c560de..0000000000 --- a/Plugins/org.commontk.slicermodule/Testing/Cpp/ctkSlicerModuleTest.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/*========================================================================= - - Library: CTK - - Copyright (c) 2010 CISTIB - Universitat Pompeu Fabra - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -// Qt includes -#include -#include - -// CTK includes -#include "ctkModuleDescription.h" -#include "ctkPluginFrameworkFactory.h" -#include "ctkPluginFramework.h" -#include "ctkPluginException.h" -#include "ctkModuleDescriptionReader.h" -#include "ctkModuleDescriptionConverterInterface.h" -#include "ctkCommandLineParser.h" - -// STD includes -#include - -ctkModuleDescription ReadModuleDescription( ctkPluginContext* context, const QString &xmlFileName ) ; -void BuildCommandLine( ctkPluginContext* context, const ctkModuleDescription& module ) ; - - -//----------------------------------------------------------------------------- -int ctkSlicerModuleTest(int argc, char * argv [] ) -{ - QApplication app(argc, argv); - ctkCommandLineParser parser; - parser.addArgument("", "-F", QVariant::String); - bool ok = false; - QHash parsedArgs = parser.parseArguments(argc, argv, &ok); - if (!ok) - { - std::cerr << qPrintable(parser.errorString()) << std::endl; - return EXIT_FAILURE; - } - QString xmlFileName = parsedArgs["-F"].toString(); - - ctkPluginFrameworkFactory fwFactory; - ctkPluginFramework* framework = fwFactory.getFramework(); - - try { - framework->init(); - } - catch (const ctkPluginException& exc) - { - qCritical() << "Failed to initialize the plug-in framework:" << exc; - exit(1); - } - - -#ifdef CMAKE_INTDIR - QString pluginPath = "/../plugins/" CMAKE_INTDIR "/"; -#else - QString pluginPath = "/plugins/"; -#endif - - try - { - QString pluginLocation = "." + pluginPath + "liborg_commontk_slicermodule.dll"; - ctkPlugin* plugin = framework->getPluginContext()->installPlugin(QUrl::fromLocalFile(pluginLocation)); - plugin->start(ctkPlugin::START_TRANSIENT); - - framework->start(); - - ctkModuleDescription module; - module = ReadModuleDescription( framework->getPluginContext(), xmlFileName ); - - BuildCommandLine( framework->getPluginContext(), module ); - - } - catch (const ctkPluginException& e) - { - qCritical() << e.what(); - } - - return EXIT_SUCCESS; -} - - - -ctkModuleDescription ReadModuleDescription( - ctkPluginContext* context, const QString &xmlFileName ) -{ - - ctkServiceReference* serviceRef; - serviceRef = context->getServiceReference( - "ctkModuleDescriptionReaderInterface" ); - - ctkModuleDescriptionReader* reader; - reader = qobject_cast - (context->getService(serviceRef)); - - - // Read file - QFile file( xmlFileName ); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - std::cout << "XML file " << xmlFileName.toStdString( ) << " could not be opened." << std::endl; - exit(1); - } - - // Parse XML file - reader->setInput( &file ); - reader->update(); - QTextStream stream(stdout); - stream << reader->moduleDescription( ); - - return reader->moduleDescription( ); -} - -void BuildCommandLine( ctkPluginContext* context, const ctkModuleDescription& module ) -{ - ctkServiceReference* serviceRef; - serviceRef = context->getServiceReference( - "ctkModuleDescriptionConverterInterface" ); - - ctkModuleDescriptionConverterInterface* converter; - converter = qobject_cast - (context->getService(serviceRef)); - - QStringList commandLineString; - converter->setModuleDescription( module ); - converter->update(); - commandLineString = converter->GetOutput().toStringList(); - QTextStream stream(stdout); - stream << commandLineString; -} - diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin.cpp b/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin.cpp deleted file mode 100644 index ee9c55ff69..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) 2010 CISTIB - Universitat Pompeu Fabra - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - - -#include "ctkSlicerModulePlugin_p.h" -#include "ctkSlicerModuleReader.h" -#include "ctkSlicerModuleStringConverter.h" -#include -#include - -ctkSlicerModulePlugin* ctkSlicerModulePlugin::instance = 0; - -ctkSlicerModulePlugin::ctkSlicerModulePlugin() - : context(0) -{ -} - -ctkSlicerModulePlugin::~ctkSlicerModulePlugin() -{ - -} - -void ctkSlicerModulePlugin::start(ctkPluginContext* context) -{ - instance = this; - this->context = context; - - this->reader = new ctkSlicerModuleReader( ); - context->registerService(QStringList("ctkModuleDescriptionReaderInterface"), - this->reader); - - this->stringConverter = new ctkSlicerModuleStringConverter( ); - context->registerService(QStringList("ctkModuleDescriptionConverterInterface"), - this->stringConverter); - - qDebug() << "Registered Slicer Module Description"; -} - -void ctkSlicerModulePlugin::stop(ctkPluginContext* context) -{ - delete this->reader; - this->reader = NULL; - - delete this->stringConverter; - this->stringConverter = NULL; - - Q_UNUSED(context) -} - -ctkSlicerModulePlugin* ctkSlicerModulePlugin::getInstance() -{ - return instance; -} - -ctkPluginContext* ctkSlicerModulePlugin::getPluginContext() const -{ - return context; -} - -Q_EXPORT_PLUGIN2(org_commontk_slicermodule, ctkSlicerModulePlugin) - - diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin_p.h b/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin_p.h deleted file mode 100644 index fa0336706a..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModulePlugin_p.h +++ /dev/null @@ -1,59 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) 2010 CISTIB - Universitat Pompeu Fabra - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - - -#ifndef CTKSLICERMODULEPLUGIN_P_H -#define CTKSLICERMODULEPLUGIN_P_H - -#include - -class ctkModuleDescriptionReaderInterface; -class ctkSlicerModuleStringConverter; - -class ctkSlicerModulePlugin : - public QObject, public ctkPluginActivator -{ - Q_OBJECT - Q_INTERFACES(ctkPluginActivator) - -public: - - ctkSlicerModulePlugin(); - ~ctkSlicerModulePlugin(); - - void start(ctkPluginContext* context); - void stop(ctkPluginContext* context); - - static ctkSlicerModulePlugin* getInstance(); - - ctkPluginContext* getPluginContext() const; - - -private: - - static ctkSlicerModulePlugin* instance; - ctkPluginContext* context; - ctkModuleDescriptionReaderInterface* reader; - ctkSlicerModuleStringConverter* stringConverter; - - -}; // ctkSlicerModulePlugin - -#endif // CTKSLICERMODULEPLUGIN_P_H diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.cpp b/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.cpp deleted file mode 100644 index c33ce9f1e5..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/*============================================================================= - -Library: CTK - -Copyright (c) 2010 Brigham and Women's Hospital (BWH) All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -=============================================================================*/ - -// Qt includes -#include -#include -#include -#include -#include - -// CTK includes -#include "ctkSlicerModuleReader.h" - -// STD includes -#include - -class ctkDefaultMessageHandler : public QAbstractMessageHandler -{ -public: - ctkDefaultMessageHandler(): QAbstractMessageHandler(0) - { - } - - QString statusMessage() const - { - return m_description; - } - - int line() const - { - return m_sourceLocation.line(); - } - - int column() const - { - return m_sourceLocation.column(); - } - -protected: - virtual void handleMessage(QtMsgType type, const QString &description, - const QUrl &identifier, const QSourceLocation &sourceLocation) - { - Q_UNUSED(type); - Q_UNUSED(identifier); - - m_messageType = type; - m_description = description; - m_sourceLocation = sourceLocation; - } - -private: - QtMsgType m_messageType; - QString m_description; - QSourceLocation m_sourceLocation; - }; - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleReader::validate()const -{ - ctkDefaultMessageHandler errorHandler; - - QXmlSchema schema; - schema.setMessageHandler(&errorHandler); - schema.load(QUrl::fromLocalFile(":slicerModuleDescription.xsd")); - - bool res = schema.isValid(); - if (!res) - { - QString error = errorHandler.statusMessage(); - throw ctkRuntimeException( tr("Invalid Schema %1") - .arg(errorHandler.statusMessage()).toStdString() ); - } - - QXmlSchemaValidator validator(schema); - res = validator.validate(this->Device); - - if (!res) - { - throw ctkRuntimeException( tr("Invalid XML(%1,%2):\n %3") - .arg(errorHandler.line()) - .arg(errorHandler.column()) - .arg(errorHandler.statusMessage()).toStdString()); - } - this->Device->reset(); - return res; -} - -// ---------------------------------------------------------------------------- -void ctkSlicerModuleReader::update() -{ - // Verify the xml is correct - this->validate(); - - QXmlSimpleReader xmlReader; - QXmlInputSource *source = new QXmlInputSource(this->Device); - - ctkSlicerModuleHandler handler; - handler.setModuleDescription(&this->ModuleDescription); - xmlReader.setContentHandler(&handler); - xmlReader.setErrorHandler(&handler); - - bool res = xmlReader.parse(source); - - if (!res) - { - throw ctkRuntimeException( tr("Parse error %1") - .arg(handler.errorString()).toStdString() ); - } -} - -// ---------------------------------------------------------------------------- -ctkSlicerModuleHandler::ctkSlicerModuleHandler() -{ - this->ModuleDescription = 0; - this->State.CurrentParameter = 0; - this->State.CurrentGroup = 0; - this->State.InExecutable = 0; - this->State.InGroup = 0; - this->State.InParameter = 0; - this->ParamValidator = QRegExp("\\W"); -} - -// ---------------------------------------------------------------------------- -void ctkSlicerModuleHandler::setModuleDescription(ctkModuleDescription* moduleDescription) -{ - this->ModuleDescription = moduleDescription; -} - -// ---------------------------------------------------------------------------- -ctkModuleDescription* ctkSlicerModuleHandler::moduleDescription()const -{ - return this->ModuleDescription; -} -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::characters(const QString& ch) -{ - this->State.CurrentText = ch.trimmed(); - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::startElement(const QString& namespaceURI, const QString& localName, - const QString& name, const QXmlAttributes& atts) -{ - if (this->State.CurrentGroup == 0) - { - return this->startExecutableElement(namespaceURI, localName, name, atts); - } - else if (this->State.CurrentParameter == 0) - { - return this->startGroupElement(namespaceURI, localName, name, atts); - } - else - { - return this->startParameterElement(namespaceURI, localName, name, atts); - } - return false; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::endElement(const QString& namespaceURI, - const QString& localName, - const QString& qName) -{ - if (this->State.InParameter) - { - return this->endParameterElement(namespaceURI, localName, qName); - } - else if (this->State.InGroup) - { - return this->endGroupElement(namespaceURI, localName, qName); - } - else if (this->State.InExecutable) - { - return this->endExecutableElement(namespaceURI, localName, qName); - } - return false; -} -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::startExecutableElement(const QString& namespaceURI, - const QString& localName, - const QString& name, - const QXmlAttributes& atts) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - ++this->State.InExecutable; - ctkModuleParameterGroup group; - if (name == "parameters") - { - if (!atts.value("advanced").isEmpty()) - { - group["Advanced"] = QVariant(atts.value("advanced")).toBool() ? "true" : "false"; - } - this->State.CurrentGroup = new ctkModuleParameterGroup(group); - } - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::startGroupElement(const QString& namespaceURI, - const QString& localName, - const QString& name, - const QXmlAttributes& atts) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - - ++this->State.InGroup; - if (name == "label" || name == "description") - {// not a parameter - return true; - } - ctkModuleParameter param; - param["Tag"] = name; - bool multiple = QVariant(atts.value("multiple")).toBool(); //empty (not found) is false - bool hidden = QVariant(atts.value("hidden")).toBool(); //empty (not found) is false - - if (name == "integer" || name == "integer-vector") - { - if (name == "integer-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "int"; - param["ArgType"] = "int"; - param["StringToType"] = "atoi"; - } - else if (name == "float" || name == "float-vector") - { - if (name == "integer-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "float"; - param["ArgType"] = "float"; - param["StringToType"] = "atof"; - } - else if (name == "double" || name == "double-vector") - { - if (name == "double-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "double"; - param["ArgType"] = "double"; - param["StringToType"] = "atof"; - } - else if (name == "string") - { - if (name == "string-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["StringToType"] = ""; - } - else if (name == "boolean") - { - if (name == "boolean-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "bool"; - param["ArgType"] = "bool"; - param["StringToType"] = ""; - param["Hidden"] = hidden ? "true" : "false"; - } - else if (name == "point" || name == "point-vector" || - name == "region" || name == "region-vector") - { - if (name == "point-vector" || name == "region-vector") - { - multiple = true; - } - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector >" : "std::vector"; - param["ArgType"] = "float"; - param["StringToType"] = "atof"; - if (!atts.value("coordinateSystem").isEmpty()) - { - param["CoordinateSystem"] = atts.value("coordinateSystem"); - } - } - else if (name == "string-enumeration") - { - param["CPPStyle"] = "std::string"; - } - else if (name == "integer-enumeration") - { - param["CPPStyle"] = "int"; - } - else if (name == "float-enumeration") - { - param["CPPStyle"] = "float"; - } - else if (name == "double-enumeration") - { - param["CPPStyle"] = "double"; - } - else if (name == "file") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPType"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = "scalar"; - if (!atts.value("fileExtensions").isEmpty()) - { - param["FileExtensionsAsString"] = atts.value("fileExtensions"); - } - } - else if (name == "directory") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - } - else if (name == "transform") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = atts.value("type").isEmpty() ? atts.value("type") : "unknown"; - if (!atts.value("fileExtensions").isEmpty()) - { - param["FileExtensionsAsString"] = atts.value("fileExtensions"); - } - if (!atts.value("reference").isEmpty()) - { - param["Reference"] = atts.value("reference"); - } - } - else if (name == "image") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPStyle"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = (!atts.value("type").isEmpty()) ? atts.value("type") : "scalar"; - if (!atts.value("fileExtensions").isEmpty()) - { - param["FileExtensionsAsString"] = atts.value("fileExtensions"); - } - param["Hidden"] = hidden ? "true" : "false"; - if (!atts.value("reference").isEmpty()) - { - param["Reference"] = atts.value("reference"); - } - } - else if (name == "geometry") - { - bool aggregate = QVariant(atts.value("aggregate")).toBool(); - param["Multiple"] = multiple ? "true" : "false"; - param["Aggregate"] = aggregate ? "true" : "false"; - param["CPPType"] = (multiple && !aggregate) ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar"; - } - else if (name == "table") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPType"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar"; - if (!atts.value("reference").isEmpty()) - { - param["Reference"] = atts.value("reference"); - } - if (!atts.value("fileExtensions").isEmpty()) - { - param["FileExtensionsAsString"] = atts.value("fileExtensions"); - } - } - else if (name == "measurement") - { - param["Multiple"] = multiple ? "true" : "false"; - param["CPPType"] = multiple ? "std::vector" : "std::string"; - param["ArgType"] = "std::string"; - param["Type"] = (!atts.value("type").isEmpty())? atts.value("type") : "scalar"; - param["Hidden"] = hidden ? "true" : "false"; - if (!atts.value("reference").isEmpty()) - { - param["Reference"] = atts.value("reference"); - } - if (!atts.value("fileExtensions").isEmpty()) - { - param["FileExtensionsAsString"] = atts.value("fileExtensions"); - } - } - this->State.CurrentParameter = new ctkModuleParameter(param); - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::startParameterElement(const QString& namespaceURI, const QString& localName, - const QString& name, const QXmlAttributes& atts) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - - ++this->State.InParameter; - ctkModuleParameter& param = *this->State.CurrentParameter; - if (name == "flag") - { - if (!atts.value("alias").isEmpty()) - { - param["FlagAliasesAsString"] = atts.value("alias"); - } - if (!atts.value("deprecatedalias").isEmpty()) - { - param["DeprecatedFlagAliasesAsString"] = atts.value("deprecatedalias"); - } - } - else if (name == "longflag") - { - if (!atts.value("alias").isEmpty()) - { - param["LongFlagAliasesAsString"] = atts.value("alias"); - } - if (!atts.value("deprecatedalias").isEmpty()) - { - param["DeprecatedLongFlagAliasesAsString"] = atts.value("deprecatedalias"); - } - } - else if (name == "constraints") - { - param["Constraints"] = "true"; - } - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::endExecutableElement(const QString& namespaceURI, - const QString& localName, - const QString& name) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - - --this->State.InExecutable; - ctkModuleDescription& module= *this->ModuleDescription; - if (name == "parameters") - { - Q_ASSERT(this->State.CurrentGroup); - this->ModuleDescription->addParameterGroup(this->State.CurrentGroup); - this->State.CurrentGroup = 0; - } - else if (name == "category") - { - module["Category"] = this->State.CurrentText; - } - else if (name == "index") - { - module["Index"] = this->State.CurrentText; - } - else if (name == "title") - { - module["Title"] = this->State.CurrentText; - } - else if (name == "version") - { - module["Version"] = this->State.CurrentText; - } - else if (name == "documentation-url") - { - module["DocumentationUrl"] = this->State.CurrentText; - } - else if (name == "license") - { - module["License"] = this->State.CurrentText; - } - else if (name == "acknowledgements") - { - module["License"] = this->State.CurrentText; - } - else if (name == "contributor") - { - module["Contributor"] = this->State.CurrentText; - } - else if (name == "location") - { - module["Location"] = this->State.CurrentText; - } - else if (name == "description") - { - module["Description"] = this->State.CurrentText.replace('\"','\''); - } - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::endGroupElement(const QString& namespaceURI, - const QString& localName, - const QString& name) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - - --this->State.InGroup; - Q_ASSERT(this->State.CurrentGroup); - ctkModuleParameterGroup& group = *this->State.CurrentGroup; - if (name == "label") - { - group["Label"] = this->State.CurrentText; - return true; - } - else if (name == "description") - { - group["Description"] = this->State.CurrentText.replace('\"', '\''); - return true; - } - Q_ASSERT(this->State.CurrentParameter); - this->State.CurrentGroup->addParameter(this->State.CurrentParameter); - this->State.CurrentParameter = 0; - return true; -} - -// ---------------------------------------------------------------------------- -bool ctkSlicerModuleHandler::endParameterElement(const QString& namespaceURI, const QString& localName, - const QString& name) -{ - Q_UNUSED(namespaceURI); - Q_UNUSED(localName); - - --this->State.InParameter; - bool res = true; - ctkModuleParameter& parameter = *this->State.CurrentParameter; - if (name == "flag") - { - QString flag = this->State.CurrentText; - flag.remove(0, flag.indexOf('-') + 1); - res = (flag.size() == 1); - if (!res) - { - qWarning() << "Invalid flag" << flag; - } - parameter["Flag"] = flag; - } - else if (name == "longflag") - { - QString flag = this->State.CurrentText; - flag.remove(0, flag.indexOf("--") + 2); - res = (flag.size() != 0 && this->ParamValidator.indexIn(flag) == -1); - if (!res) - { - qWarning() << "Invalid flag" << flag; - } - parameter["LongFlag"] = flag; - parameter["Name"] = parameter.value("Name", flag); - } - else if (name == "name") - { - QString paramName = this->State.CurrentText; - if (this->ParamValidator.indexIn(paramName) != -1) - { - res = false; - qWarning() << "Invalid name" << paramName; - } - parameter["Name"] = paramName; - } - else if (name == "label") - { - parameter["Label"] = this->State.CurrentText; - } - else if (name == "element") - { - parameter.insertMulti("Element", this->State.CurrentText); - } - else if (name == "default") - { - parameter["Default"] = this->State.CurrentText; - } - else if (name == "channel") - { - parameter["Channel"] = this->State.CurrentText; - } - else if (name == "index") - { - // TODO make sure Flag and LongFlag are empty - parameter["Index"] = this->State.CurrentText; - } - else if (name == "minimum") - { - parameter["Minimum"] = this->State.CurrentText; - } - else if (name == "maximum") - { - parameter["Maximum"] = this->State.CurrentText; - } - else if (name == "step") - { - parameter["Step"] = this->State.CurrentText; - } - return res; -} diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.h b/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.h deleted file mode 100644 index 3cf49b942d..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModuleReader.h +++ /dev/null @@ -1,96 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) 2010 Brigham and Women's Hospital (BWH) All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#ifndef __ctkSlicerModuleReader_h -#define __ctkSlicerModuleReader_h - -// Qt includes -#include -#include -#include -#include - -// CTK includes -#include "ctkModuleDescriptionReader.h" - -#include - -/** - * Reader of Slicer Module XML description - * Freely inspired from - * Slicer/Libs/SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx - */ -class org_commontk_slicermodule_EXPORT ctkSlicerModuleReader : public ctkModuleDescriptionReader -{ - Q_OBJECT -public: - virtual void update(); - bool validate()const; -}; - -/** - * Utility class to parse the Slicer Module XML descriptions using the - * SAX interface. - */ -class ctkSlicerModuleHandler: public QXmlDefaultHandler -{ -public: - ctkSlicerModuleHandler(); - bool characters(const QString& ch); - virtual bool endElement(const QString& namespaceURI, const QString& localName, const QString& qName); - virtual bool startElement(const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts ); - - void setModuleDescription(ctkModuleDescription* moduleDescription); - ctkModuleDescription* moduleDescription()const; - -protected: - - bool startExecutableElement(const QString& namespaceURI, const QString& localName, - const QString& name, const QXmlAttributes& atts); - - bool startGroupElement(const QString& namespaceURI, const QString& localName, - const QString& name, const QXmlAttributes& atts); - - bool startParameterElement(const QString& namespaceURI, const QString& localName, - const QString& name, const QXmlAttributes& atts); - - bool endExecutableElement(const QString& namespaceURI, const QString& localName, - const QString& name); - - bool endGroupElement(const QString& namespaceURI, const QString& localName, - const QString& name); - - bool endParameterElement(const QString& namespaceURI, const QString& localName, - const QString& name); - - ctkModuleDescription* ModuleDescription; - struct ParserState{ - ctkModuleParameter* CurrentParameter; - ctkModuleParameterGroup* CurrentGroup; - QString CurrentText; - int InExecutable; - int InGroup; - int InParameter; - }; - ParserState State; - QRegExp ParamValidator; -}; - -#endif diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.cpp b/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.cpp deleted file mode 100644 index b3fec39f24..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/*============================================================================= - -Library: CTK - -Copyright (c) 2010 CISTIB - Universtitat Pompeu Fabra - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -=============================================================================*/ - -#include "ctkSlicerModuleStringConverter.h" -#include - - -void ctkSlicerModuleStringConverter::update() -{ - SetTarget( ); - - SetAllParameters( ); -} - -const QVariant ctkSlicerModuleStringConverter::GetOutput() -{ - return this->CommandLineAsString; -} - -void ctkSlicerModuleStringConverter::SetAllParameters() -{ - foreach( const ctkModuleParameterGroup* itGroup, this->ModuleDescription.parameterGroups()) - { - foreach( const ctkModuleParameter* itParam, itGroup->parameters()) - { - SetParameterValue( *itParam ); - } - } -} - -void ctkSlicerModuleStringConverter::SetParameterValue( const ctkModuleParameter ¶m ) -{ - QString prefix; - QString flag; - bool hasFlag = false; - - if ( param["LongFlag"] != "") - { - prefix = "--"; - flag = param["LongFlag"]; - hasFlag = true; - } - else if (param["Flag"] != "") - { - prefix = "-"; - flag = param["Flag"]; - hasFlag = true; - } - - - if (hasFlag) - { - if ( param["Tag"] != "boolean" - && param["Tag"] != "file" - && param["Tag"] != "directory" - && param["Tag"] != "string" - && param["Tag"] != "integer-vector" - && param["Tag"] != "float-vector" - && param["Tag"] != "double-vector" - && param["Tag"] != "string-vector" - && param["Tag"] != "image" - && param["Tag"] != "point" - && param["Tag"] != "region" - && param["Tag"] != "transform" - && param["Tag"] != "geometry" - && param["Tag"] != "table" - && param["Tag"] != "measurement") - { - // simple parameter, write flag and value - this->CommandLineAsString.push_back(prefix + flag); - this->CommandLineAsString.push_back(param["Default"]); - } - else if (param["Tag"] == "boolean" && param["Default"] == "true") - { - this->CommandLineAsString.push_back(prefix + flag); - } - else if (param["Tag"] == "file" - || param["Tag"] == "directory" - || param["Tag"] == "string" - || param["Tag"] == "integer-vector" - || param["Tag"] == "float-vector" - || param["Tag"] == "double-vector" - || param["Tag"] == "string-vector") - { - // Only write out the flag if value is not empty - if ( param["Default"] != "") - { - this->CommandLineAsString.push_back(prefix + flag); - this->CommandLineAsString.push_back( param["Default"] ); - } - } - // data passed as parameter - else if ( param["Tag"] == "image" - || param["Tag"] == "geometry" - || param["Tag"] == "transform" - || param["Tag"] == "table" - || param["Tag"] == "measurement" ) - { - if ( param["Default"] != "") - { - this->CommandLineAsString.push_back(prefix + flag); - this->CommandLineAsString.push_back(param["Default"]); - } - } - else if ( param["Tag"] == "region" ) - { - this->CommandLineAsString.push_back(prefix + flag); - this->CommandLineAsString.push_back( param["Default"] ); - } - else if ( param["Tag"] == "point" ) - { - QStringList points = param["Default"].split( ";"); - foreach ( const QString &it, points ) - { - this->CommandLineAsString.push_back(prefix + flag); - this->CommandLineAsString.push_back( it ); - } - } - - } - - // If index is not empty -> It's a command line argument arg0, arg1, ... without flag prefix - if ( param["Index"] != "") - { - this->CommandLineAsString.push_back( param["Default"] ); - } -} - -void ctkSlicerModuleStringConverter::SetTarget() -{ - this->CommandLineAsString.clear(); - - if (!this->ModuleDescription["Location"].isEmpty() && - this->ModuleDescription["Location"] != this->ModuleDescription["Target"]) - { - this->CommandLineAsString.push_back(this->ModuleDescription["Location"]); - } - this->CommandLineAsString.push_back( this->ModuleDescription["Target"] ); -} - - diff --git a/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.h b/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.h deleted file mode 100644 index 0a6083721e..0000000000 --- a/Plugins/org.commontk.slicermodule/ctkSlicerModuleStringConverter.h +++ /dev/null @@ -1,65 +0,0 @@ -/*============================================================================= - - Library: CTK - - Copyright (c) 2010 CISTIB - Universtitat Pompeu Fabra - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#ifndef __ctkSlicerModuleStringConverter_h -#define __ctkSlicerModuleStringConverter_h - -#include - -#include "ctkModuleDescriptionConverter.h" - -class ModuleDescription; -class ModuleParameterGroup; - - -/** - * Convert Slicer Module description to command line string QStringList - * - */ -class ctkSlicerModuleStringConverter : public ctkModuleDescriptionConverter -{ - Q_OBJECT -public: - ctkSlicerModuleStringConverter() {}; - ~ctkSlicerModuleStringConverter() {}; - - /// - void update( ); - - /// - virtual const QVariant GetOutput( ); - -protected: - - //! - void SetAllParameters(); - - //! - void SetParameterValue( const ctkModuleParameter ¶m ); - - /// - void SetTarget(); - -protected: - - QStringList CommandLineAsString; -}; - -#endif diff --git a/SuperBuild.cmake b/SuperBuild.cmake index df3a8db698..a23e8aaf10 100644 --- a/SuperBuild.cmake +++ b/SuperBuild.cmake @@ -72,6 +72,10 @@ endforeach() set(ctk_cmake_boolean_args BUILD_TESTING + CTK_BUILD_ALL + CTK_BUILD_ALL_APPS + CTK_BUILD_ALL_LIBRARIES + CTK_BUILD_ALL_PLUGINS CTK_BUILD_QTDESIGNER_PLUGINS CTK_USE_QTTESTING CTK_USE_KWSTYLE @@ -80,9 +84,6 @@ set(ctk_cmake_boolean_args CTEST_USE_LAUNCHERS CTK_WRAP_PYTHONQT_FULL CTK_ENABLE_Python_Wrapping - ${ctk_libs_bool_vars} - ${ctk_plugins_bool_vars} - ${ctk_applications_bool_vars} ${ctk_lib_options_list} ) @@ -91,6 +92,13 @@ foreach(ctk_cmake_arg ${ctk_cmake_boolean_args}) list(APPEND ctk_superbuild_boolean_args -D${ctk_cmake_arg}:BOOL=${${ctk_cmake_arg}}) endforeach() +foreach(ctk_cmake_arg ${ctk_libs_bool_vars} ${ctk_plugins_bool_vars} ${ctk_applications_bool_vars}) + # Use the cached value of the option in case the current value has been + # overridden by a "CTK_BUILD_ALL" option. + get_property(arg_value CACHE ${ctk_cmake_arg} PROPERTY VALUE) + list(APPEND ctk_superbuild_boolean_args -D${ctk_cmake_arg}:BOOL=${arg_value}) +endforeach() + # message("CMake boolean args:") # foreach(arg ${ctk_superbuild_boolean_args}) # message(" ${arg}") diff --git a/Utilities/CMake/FindDCMTK.cmake b/Utilities/CMake/FindDCMTK.cmake index 9869ec03be..ea8eb1e05a 100644 --- a/Utilities/CMake/FindDCMTK.cmake +++ b/Utilities/CMake/FindDCMTK.cmake @@ -1,7 +1,9 @@ +# adapted version of FindDCMTK, better suited for super-builds + # - find DCMTK libraries and applications # -# DCMTK_INCLUDE_DIR - Directories to include to use DCMTK +# DCMTK_INCLUDE_DIRS - Directories to include to use DCMTK # DCMTK_LIBRARIES - Files to link against to use DCMTK # DCMTK_FOUND - If false, don't try to use DCMTK # DCMTK_DIR - (optional) Source directory for DCMTK @@ -13,7 +15,8 @@ #============================================================================= # Copyright 2004-2009 Kitware, Inc. -# Copyright 2009 Mathieu Malaterre +# Copyright 2009-2010 Mathieu Malaterre +# Copyright 2010 Thomas Sondergaard # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. @@ -28,338 +31,151 @@ # # Written for VXL by Amitha Perera. # Upgraded for GDCM by Mathieu Malaterre. -# - -if( NOT DCMTK_FOUND ) - set( DCMTK_DIR "/usr/include/dcmtk/" - CACHE PATH "Root of DCMTK source tree (optional)." ) - mark_as_advanced( DCMTK_DIR ) -endif() - -find_path( DCMTK_config_INCLUDE_DIR osconfig.h - PATHS - ${DCMTK_DIR}/config/include - ${DCMTK_DIR}/config - ${DCMTK_DIR}/include/dcmtk/config - ${DCMTK_DIR}/include - NO_DEFAULT_PATH - -) - -find_path( DCMTK_ofstd_INCLUDE_DIR ofstdinc.h - PATHS - ${DCMTK_DIR}/ofstd/include - ${DCMTK_DIR}/ofstd - ${DCMTK_DIR}/include/ofstd - ${DCMTK_DIR}/include/dcmtk/ofstd - NO_DEFAULT_PATH -) - -find_library( DCMTK_ofstd_LIBRARY ofstd - PATHS - ${DCMTK_DIR}/ofstd/libsrc - ${DCMTK_DIR}/ofstd/libsrc/Release - ${DCMTK_DIR}/ofstd/libsrc/Debug - ${DCMTK_DIR}/ofstd/Release - ${DCMTK_DIR}/ofstd/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_path( DCMTK_oflog_INCLUDE_DIR logger.h - PATHS - ${DCMTK_DIR}/oflog/include - ${DCMTK_DIR}/oflog - ${DCMTK_DIR}/include/oflog - ${DCMTK_DIR}/include/dcmtk/oflog - NO_DEFAULT_PATH -) - -find_library( DCMTK_oflog_LIBRARY oflog - PATHS - ${DCMTK_DIR}/oflog/libsrc - ${DCMTK_DIR}/oflog/libsrc/Release - ${DCMTK_DIR}/oflog/libsrc/Debug - ${DCMTK_DIR}/oflog/Release - ${DCMTK_DIR}/oflog/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - - - -find_path( DCMTK_dcmdata_INCLUDE_DIR dctypes.h - PATHS - ${DCMTK_DIR}/include/dcmdata - ${DCMTK_DIR}/include/dcmtk/dcmdata - ${DCMTK_DIR}/dcmdata - ${DCMTK_DIR}/dcmdata/include - NO_DEFAULT_PATH -) - -find_library( DCMTK_dcmdata_LIBRARY dcmdata - PATHS - ${DCMTK_DIR}/dcmdata/libsrc - ${DCMTK_DIR}/dcmdata/libsrc/Release - ${DCMTK_DIR}/dcmdata/libsrc/Debug - ${DCMTK_DIR}/dcmdata/Release - ${DCMTK_DIR}/dcmdata/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_path( DCMTK_dcmjpeg_INCLUDE_DIR djdecode.h - PATHS - ${DCMTK_DIR}/include/dcmjpeg - ${DCMTK_DIR}/include/dcmtk/dcmjpeg - ${DCMTK_DIR}/dcmjpeg - ${DCMTK_DIR}/dcmjpeg/include - NO_DEFAULT_PATH -) - -find_library( DCMTK_dcmjpeg_LIBRARY dcmjpeg - PATHS - ${DCMTK_DIR}/dcmjpeg/libsrc - ${DCMTK_DIR}/dcmjpeg/libsrc/Release - ${DCMTK_DIR}/dcmjpeg/libsrc/Debug - ${DCMTK_DIR}/dcmjpeg/Release - ${DCMTK_DIR}/dcmjpeg/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_library( DCMTK_ijg12_LIBRARY ijg12 - PATHS - ${DCMTK_DIR}/dcmjpeg/libsrc - ${DCMTK_DIR}/dcmjpeg/libsrc/Release - ${DCMTK_DIR}/dcmjpeg/libsrc/Debug - ${DCMTK_DIR}/dcmjpeg/Release - ${DCMTK_DIR}/dcmjpeg/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_library( DCMTK_ijg16_LIBRARY ijg16 - PATHS - ${DCMTK_DIR}/dcmjpeg/libsrc - ${DCMTK_DIR}/dcmjpeg/libsrc/Release - ${DCMTK_DIR}/dcmjpeg/libsrc/Debug - ${DCMTK_DIR}/dcmjpeg/Release - ${DCMTK_DIR}/dcmjpeg/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_library( DCMTK_ijg8_LIBRARY ijg8 - PATHS - ${DCMTK_DIR}/dcmjpeg/libsrc - ${DCMTK_DIR}/dcmjpeg/libsrc/Release - ${DCMTK_DIR}/dcmjpeg/libsrc/Debug - ${DCMTK_DIR}/dcmjpeg/Release - ${DCMTK_DIR}/dcmjpeg/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_path( DCMTK_dcmnet_INCLUDE_DIR dimse.h - PATHS - ${DCMTK_DIR}/include/dcmnet - ${DCMTK_DIR}/include/dcmtk/dcmnet - ${DCMTK_DIR}/dcmnet - ${DCMTK_DIR}/dcmnet/include - NO_DEFAULT_PATH -) +# Modified for EasyViz by Thomas Sondergaard. +# -find_library( DCMTK_dcmnet_LIBRARY dcmnet - PATHS - ${DCMTK_DIR}/dcmnet/libsrc - ${DCMTK_DIR}/dcmnet/libsrc/Release - ${DCMTK_DIR}/dcmnet/libsrc/Debug - ${DCMTK_DIR}/dcmnet/Release - ${DCMTK_DIR}/dcmnet/Debug - ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) +# prefer DCMTK_DIR over default system paths like /usr/lib +set(CMAKE_PREFIX_PATH ${DCMTK_DIR}/lib ${CMAKE_PREFIX_PATH}) # this is given to FIND_LIBRARY or FIND_PATH -find_path( DCMTK_dcmimgle_INCLUDE_DIR dcmimage.h - PATHS - ${DCMTK_DIR}/dcmimgle/include - ${DCMTK_DIR}/dcmimgle - ${DCMTK_DIR}/include/dcmimgle - ${DCMTK_DIR}/include/dcmtk/dcmimgle - NO_DEFAULT_PATH -) +if(NOT DCMTK_FOUND AND NOT DCMTK_DIR) + set(DCMTK_DIR + "/usr/include/dcmtk/" + CACHE + PATH + "Root of DCMTK source tree (optional).") + mark_as_advanced(DCMTK_DIR) +endif() -find_library( DCMTK_dcmimgle_LIBRARY dcmimgle - PATHS - ${DCMTK_DIR}/dcmimgle/libsrc - ${DCMTK_DIR}/dcmimgle/libsrc/Release - ${DCMTK_DIR}/dcmimgle/libsrc/Debug - ${DCMTK_DIR}/dcmimgle/Release - ${DCMTK_DIR}/dcmimgle/Debug +# Find all libraries, store debug and release separately +foreach(lib + dcmpstat + dcmsr + dcmsign + dcmtls + dcmqrdb + dcmnet + dcmjpeg + dcmimage + dcmimgle + dcmdata + oflog + ofstd + ijg12 + ijg16 + ijg8 + ) + + # Find Release libraries + find_library(DCMTK_${lib}_LIBRARY_RELEASE + ${lib} + PATHS + ${DCMTK_DIR}/${lib}/libsrc + ${DCMTK_DIR}/${lib}/libsrc/Release + ${DCMTK_DIR}/${lib}/Release ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -find_path( DCMTK_dcmimage_INCLUDE_DIR diregist.h - PATHS - ${DCMTK_DIR}/dcmimage/include - ${DCMTK_DIR}/dcmimage - ${DCMTK_DIR}/include/dcmimage - ${DCMTK_DIR}/include/dcmtk/dcmimage - NO_DEFAULT_PATH -) - -find_library( DCMTK_dcmimage_LIBRARY dcmimage - PATHS - ${DCMTK_DIR}/dcmimage/libsrc - ${DCMTK_DIR}/dcmimage/libsrc/Release - ${DCMTK_DIR}/dcmimage/libsrc/Debug - ${DCMTK_DIR}/dcmimage/Release - ${DCMTK_DIR}/dcmimage/Debug + ${DCMTK_DIR}/lib/Release + ${DCMTK_DIR}/dcmjpeg/lib${lib}/Release + NO_DEFAULT_PATH + ) + + # Find Debug libraries + find_library(DCMTK_${lib}_LIBRARY_DEBUG + ${lib} + PATHS + ${DCMTK_DIR}/${lib}/libsrc + ${DCMTK_DIR}/${lib}/libsrc/Debug + ${DCMTK_DIR}/${lib}/Debug ${DCMTK_DIR}/lib - NO_DEFAULT_PATH -) - -# MM: I could not find this library on debian system / dcmtk 3.5.4 -# Michael Onken: this module is now called dcmqrdb. I will re-work that script soon... -find_library(DCMTK_imagedb_LIBRARY imagedb - PATHS - ${DCMTK_DIR}/imagectn/libsrc/Release - ${DCMTK_DIR}/imagectn/libsrc/ - ${DCMTK_DIR}/imagectn/libsrc/Debug - NO_DEFAULT_PATH - ) - -if( DCMTK_config_INCLUDE_DIR - AND DCMTK_ofstd_INCLUDE_DIR - AND DCMTK_ofstd_LIBRARY - AND DCMTK_oflog_INCLUDE_DIR - AND DCMTK_oflog_LIBRARY - AND DCMTK_dcmdata_INCLUDE_DIR - AND DCMTK_dcmdata_LIBRARY - AND DCMTK_dcmjpeg_INCLUDE_DIR - AND DCMTK_dcmjpeg_LIBRARY - AND DCMTK_dcmnet_INCLUDE_DIR - AND DCMTK_dcmnet_LIBRARY - AND DCMTK_dcmimgle_INCLUDE_DIR - AND DCMTK_dcmimgle_LIBRARY - AND DCMTK_dcmimage_INCLUDE_DIR - AND DCMTK_dcmimage_LIBRARY) - -# # Wrap library is required on Linux -# if(NOT WIN32) -# find_library(DCMTK_wrap_LIBRARY wrap) -# message(DCMTK_wrap_LIBRARY:${DCMTK_wrap_LIBRARY}) -# if(NOT DCMTK_wrap_LIBRARY) -# message(FATAL_ERROR "error: Wrap library is required to use DCMTK. " -# "On Ubuntu, you could install it using 'sudo apt-get libwrap0'") -# endif() -# endif() - - set(CMAKE_THREAD_LIBS_INIT) - if(DCMTK_oflog_LIBRARY) - # Hack - Not having a DCMTKConfig.cmake file to read the settings from, we will attempt to - # find the library in all cases. - # Ideally, pthread library should be discovered only if DCMTK_WITH_THREADS is enabled. - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads) + ${DCMTK_DIR}/lib/Debug + ${DCMTK_DIR}/dcmjpeg/lib${lib}/Debug + NO_DEFAULT_PATH + ) + + mark_as_advanced(DCMTK_${lib}_LIBRARY_RELEASE) + mark_as_advanced(DCMTK_${lib}_LIBRARY_DEBUG) + + # Add libraries to variable according to build type + if(DCMTK_${lib}_LIBRARY_RELEASE) + list(APPEND DCMTK_LIBRARIES optimized ${DCMTK_${lib}_LIBRARY_RELEASE}) endif() - set( DCMTK_FOUND "YES" ) - set( DCMTK_INCLUDE_DIR - ${DCMTK_DIR}/include - ${DCMTK_config_INCLUDE_DIR} - ${DCMTK_ofstd_INCLUDE_DIR} - ${DCMTK_oflog_INCLUDE_DIR} - ${DCMTK_dcmdata_INCLUDE_DIR} - ${DCMTK_dcmjpeg_INCLUDE_DIR} - ${DCMTK_dcmnet_INCLUDE_DIR} - ${DCMTK_dcmimgle_INCLUDE_DIR} - ${DCMTK_dcmimage_INCLUDE_DIR} - ) - - set( DCMTK_LIBRARIES - ${DCMTK_dcmimage_LIBRARY} - ${DCMTK_dcmimgle_LIBRARY} - ${DCMTK_dcmnet_LIBRARY} - ${DCMTK_dcmjpeg_LIBRARY} - ${DCMTK_dcmdata_LIBRARY} - ${DCMTK_ijg8_LIBRARY} - ${DCMTK_ijg12_LIBRARY} - ${DCMTK_ijg16_LIBRARY} - ${DCMTK_oflog_LIBRARY} - ${DCMTK_ofstd_LIBRARY} - ${DCMTK_config_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ) - - if(DCMTK_imagedb_LIBRARY) - set( DCMTK_LIBRARIES - ${DCMTK_LIBRARIES} - ${DCMTK_imagedb_LIBRARY} - ) + if(DCMTK_${lib}_LIBRARY_DEBUG) + list(APPEND DCMTK_LIBRARIES debug ${DCMTK_${lib}_LIBRARY_DEBUG}) endif() - if( WIN32 ) - set( DCMTK_LIBRARIES ${DCMTK_LIBRARIES} ws2_32 netapi32 wsock32) - endif() - -# IF (NOT WIN32) -# set( DCMTK_LIBRARIES ${DCMTK_LIBRARIES} ${DCMTK_wrap_LIBRARY} ) -# endif() +endforeach() +set(CMAKE_THREAD_LIBS_INIT) +if(DCMTK_oflog_LIBRARY_RELEASE OR DCMTK_oflog_LIBRARY_DEBUG) + # Hack - Not having a DCMTKConfig.cmake file to read the settings from, we will attempt to + # find the library in all cases. + # Ideally, pthread library should be discovered only if DCMTK_WITH_THREADS is enabled. + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads) endif() -find_program(DCMTK_DCMDUMP_EXECUTABLE dcmdump - PATHS - ${DCMTK_DIR}/bin - NO_DEFAULT_PATH - ) - -find_program(DCMTK_DCMDJPEG_EXECUTABLE dcmdjpeg - PATHS - ${DCMTK_DIR}/bin - NO_DEFAULT_PATH - ) +if(CMAKE_THREAD_LIBS_INIT) + list(APPEND DCMTK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +endif() -find_program(DCMTK_DCMDRLE_EXECUTABLE dcmdrle - PATHS - ${DCMTK_DIR}/bin - NO_DEFAULT_PATH - ) +set(DCMTK_config_TEST_HEADER osconfig.h) +set(DCMTK_dcmdata_TEST_HEADER dctypes.h) +set(DCMTK_dcmimage_TEST_HEADER dicoimg.h) +set(DCMTK_dcmimgle_TEST_HEADER dcmimage.h) +set(DCMTK_dcmjpeg_TEST_HEADER djdecode.h) +set(DCMTK_dcmnet_TEST_HEADER assoc.h) +set(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h) +set(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h) +set(DCMTK_dcmsign_TEST_HEADER sicert.h) +set(DCMTK_dcmsr_TEST_HEADER dsrtree.h) +set(DCMTK_dcmtls_TEST_HEADER tlslayer.h) +set(DCMTK_ofstd_TEST_HEADER ofstdinc.h) + +foreach(dir + config + dcmdata + dcmimage + dcmimgle + dcmjpeg + dcmnet + dcmpstat + dcmqrdb + dcmsign + dcmsr + dcmtls + ofstd) + find_path(DCMTK_${dir}_INCLUDE_DIR + ${DCMTK_${dir}_TEST_HEADER} + PATHS + ${DCMTK_DIR}/${dir}/include + ${DCMTK_DIR}/${dir} + ${DCMTK_DIR}/include/dcmtk/${dir} + ${DCMTK_DIR}/include/${dir}) + + mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR) + #message("** DCMTKs ${dir} found at ${DCMTK_${dir}_INCLUDE_DIR}") + + if(DCMTK_${dir}_INCLUDE_DIR) + list(APPEND + DCMTK_INCLUDE_DIRS + ${DCMTK_${dir}_INCLUDE_DIR}) + endif() +endforeach() -find_program(DCMTK_DCMQRSCP_EXECUTABLE dcmqrscp - PATHS - ${DCMTK_DIR}/bin - NO_DEFAULT_PATH - ) +list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_DIR}/include) -find_program(DCMTK_STORESCU_EXECUTABLE storescu - PATHS - ${DCMTK_DIR}/bin - NO_DEFAULT_PATH - ) +if(WIN32) + list(APPEND DCMTK_LIBRARIES netapi32 wsock32) +endif() -mark_as_advanced( - DCMTK_DCMDUMP_EXECUTABLE - DCMTK_DCMDJPEG_EXECUTABLE - DCMTK_DCMDRLE_EXECUTABLE - DCMTK_DCMQRSCP_EXECUTABLE - DCMTK_STORESCU_EXECUTABLE - DCMTK_config_INCLUDE_DIR - DCMTK_dcmdata_INCLUDE_DIR - DCMTK_dcmdata_LIBRARY - DCMTK_dcmnet_INCLUDE_DIR - DCMTK_dcmnet_LIBRARY - DCMTK_dcmimgle_INCLUDE_DIR - DCMTK_dcmimgle_LIBRARY - DCMTK_dcmimage_INCLUDE_DIR - DCMTK_dcmimage_LIBRARY - DCMTK_imagedb_LIBRARY - DCMTK_ofstd_INCLUDE_DIR - DCMTK_ofstd_LIBRARY - DCMTK_oflog_INCLUDE_DIR - DCMTK_oflog_LIBRARY - ) +if(DCMTK_ofstd_INCLUDE_DIR) + get_filename_component(DCMTK_dcmtk_INCLUDE_DIR + ${DCMTK_ofstd_INCLUDE_DIR} + PATH + CACHE) + list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR}) + mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR) +endif() +# Compatibility: This variable is deprecated +set(DCMTK_INCLUDE_DIR ${DCMTK_INCLUDE_DIRS}) diff --git a/Utilities/CMake/FindPythonQt.cmake b/Utilities/CMake/FindPythonQt.cmake index 82c38ecf81..8abc05b419 100644 --- a/Utilities/CMake/FindPythonQt.cmake +++ b/Utilities/CMake/FindPythonQt.cmake @@ -25,6 +25,9 @@ endif() set(PYTHONQT_FOUND 0) if(PYTHONQT_INCLUDE_DIR AND PYTHONQT_LIBRARY) + # Currently CMake'ified PythonQt only supports building against a python Release build. + # This applies independently of CTK build type (Release, Debug, ...) + add_definitions(-DPYTHONQT_USE_RELEASE_PYTHON_FALLBACK) set(PYTHONQT_FOUND 1) set(PYTHONQT_LIBRARIES ${PYTHONQT_LIBRARY} ${PYTHONQT_LIBUTIL}) endif()