diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index c5b3252693..7036418a58 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -1,853 +1,853 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { void outputQtMessage(QtMsgType type, const QMessageLogContext&, const QString& msg) { auto message = msg.toStdString(); switch (type) { case QtDebugMsg: MITK_DEBUG << message; break; case QtInfoMsg: MITK_INFO << message; break; case QtWarningMsg: MITK_WARN << message; break; case QtCriticalMsg: MITK_ERROR << message; break; case QtFatalMsg: MITK_ERROR << message; abort(); default: MITK_INFO << message; break; } } } namespace mitk { const QString BaseApplication::ARG_APPLICATION = "BlueBerry.application"; const QString BaseApplication::ARG_CLEAN = "BlueBerry.clean"; const QString BaseApplication::ARG_CONSOLELOG = "BlueBerry.consoleLog"; const QString BaseApplication::ARG_DEBUG = "BlueBerry.debug"; const QString BaseApplication::ARG_FORCE_PLUGIN_INSTALL = "BlueBerry.forcePlugins"; const QString BaseApplication::ARG_HOME = "BlueBerry.home"; const QString BaseApplication::ARG_NEWINSTANCE = "BlueBerry.newInstance"; const QString BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING = "BlueBerry.noLazyRegistryCacheLoading"; const QString BaseApplication::ARG_NO_REGISTRY_CACHE = "BlueBerry.noRegistryCache"; const QString BaseApplication::ARG_PLUGIN_CACHE = "BlueBerry.plugin_cache_dir"; const QString BaseApplication::ARG_PLUGIN_DIRS = "BlueBerry.plugin_dirs"; const QString BaseApplication::ARG_PRELOAD_LIBRARY = "BlueBerry.preloadLibrary"; const QString BaseApplication::ARG_PRODUCT = "BlueBerry.product"; const QString BaseApplication::ARG_PROVISIONING = "BlueBerry.provisioning"; const QString BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE = "BlueBerry.registryMultiLanguage"; const QString BaseApplication::ARG_SPLASH_IMAGE = "BlueBerry.splashscreen"; const QString BaseApplication::ARG_STORAGE_DIR = "BlueBerry.storageDir"; const QString BaseApplication::ARG_XARGS = "xargs"; const QString BaseApplication::PROP_APPLICATION = "blueberry.application"; const QString BaseApplication::PROP_FORCE_PLUGIN_INSTALL = BaseApplication::ARG_FORCE_PLUGIN_INSTALL; const QString BaseApplication::PROP_NEWINSTANCE = BaseApplication::ARG_NEWINSTANCE; const QString BaseApplication::PROP_NO_LAZY_REGISTRY_CACHE_LOADING = BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING; const QString BaseApplication::PROP_NO_REGISTRY_CACHE = BaseApplication::ARG_NO_REGISTRY_CACHE; const QString BaseApplication::PROP_PRODUCT = "blueberry.product"; const QString BaseApplication::PROP_REGISTRY_MULTI_LANGUAGE = BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE; class SplashCloserCallback : public QRunnable { public: SplashCloserCallback(QSplashScreen* splashscreen) : m_Splashscreen(splashscreen) { } void run() override { this->m_Splashscreen->close(); } private: QSplashScreen *m_Splashscreen; // Owned by BaseApplication::Impl }; struct BaseApplication::Impl { ctkProperties m_FWProps; QCoreApplication *m_QApp; int m_Argc; char **m_Argv; #ifdef Q_OS_MAC std::vector m_Argv_macOS; #endif QString m_AppName; QString m_OrgaName; QString m_OrgaDomain; bool m_SingleMode; bool m_SafeMode; QSplashScreen *m_Splashscreen; SplashCloserCallback *m_SplashscreenClosingCallback; QStringList m_PreloadLibs; QString m_ProvFile; Impl(int argc, char **argv) : m_Argc(argc), m_Argv(argv), #ifdef Q_OS_MAC m_Argv_macOS(), #endif m_SingleMode(false), m_SafeMode(true), m_Splashscreen(nullptr), m_SplashscreenClosingCallback(nullptr) { #ifdef Q_OS_MAC /* On macOS the process serial number is passed as an command line argument (-psn_) in certain circumstances. This option causes a Poco exception. We remove it, if present. */ m_Argv_macOS.reserve(argc); const char psn[] = "-psn"; for (decltype(argc) i = 0; i < argc; ++i) { if (0 == strncmp(argv[i], psn, sizeof(psn))) continue; m_Argv_macOS.push_back(argv[i]); } m_Argc = static_cast(m_Argv_macOS.size()); m_Argv = m_Argv_macOS.data(); #endif } ~Impl() { delete m_SplashscreenClosingCallback; delete m_Splashscreen; delete m_QApp; } QVariant getProperty(const QString &property) const { auto iter = m_FWProps.find(property); return m_FWProps.end() != iter ? iter.value() : QVariant(); } void handleBooleanOption(const std::string &name, const std::string &) { auto fwKey = QString::fromStdString(name); // Translate some keys to proper framework properties if (ARG_CONSOLELOG == fwKey) fwKey = ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG; // For all other options we use the command line option name as the // framework property key. m_FWProps[fwKey] = true; } void handlePreloadLibraryOption(const std::string &, const std::string &value) { m_PreloadLibs.push_back(QString::fromStdString(value)); } void handleClean(const std::string &, const std::string &) { m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN] = ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; } void initializeCTKPluginFrameworkProperties(Poco::Util::LayeredConfiguration &configuration) { // Add all configuration key/value pairs as framework properties Poco::Util::LayeredConfiguration::Keys keys; Poco::Util::LayeredConfiguration::Keys keyStack; configuration.keys(keyStack); std::vector keyChain; while (!keyStack.empty()) { const auto currSubKey = keyStack.back(); if (!keyChain.empty() && keyChain.back() == currSubKey) { keyChain.pop_back(); keyStack.pop_back(); continue; } Poco::Util::LayeredConfiguration::Keys subKeys; configuration.keys(currSubKey, subKeys); if (subKeys.empty()) { std::string finalKey; keyStack.pop_back(); - for (const auto key : keyChain) + for (const auto& key : keyChain) finalKey += key + '.'; finalKey += currSubKey; keys.push_back(finalKey); } else { keyChain.push_back(currSubKey); - for (const auto key : subKeys) + for (const auto& key : subKeys) keyStack.push_back(key); } } - for (const auto key : keys) + for (const auto& key : keys) { if (configuration.hasProperty(key)) { // .ini and command line options overwrite already inserted keys auto qKey = QString::fromStdString(key); m_FWProps[qKey] = QString::fromStdString(configuration.getString(key)); } } } void parseProvisioningFile(const QString &filePath) { // Skip parsing if the file path is empty if (filePath.isEmpty()) return; auto consoleLog = this->getProperty(ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG).toBool(); // Read initial plugins from a provisioning file QFileInfo provFile(filePath); QStringList pluginsToStart; if (provFile.exists()) { MITK_INFO(consoleLog) << "Using provisioning file: " << qPrintable(provFile.absoluteFilePath()); ProvisioningInfo provInfo(provFile.absoluteFilePath()); // It can still happen that the encoding is not compatible with the fromUtf8 function (i.e. when // manipulating the LANG variable). The QStringList in provInfo is empty then. if (provInfo.getPluginDirs().empty()) { MITK_ERROR << "Cannot search for provisioning file, the retrieved directory list is empty.\n" << "This can happen if there are some special non-ASCII characters in the install path."; } else { - for(const auto pluginPath : provInfo.getPluginDirs()) + for(const auto& pluginPath : provInfo.getPluginDirs()) ctkPluginFrameworkLauncher::addSearchPath(pluginPath); auto pluginUrlsToStart = provInfo.getPluginsToStart(); - for (const auto url : pluginUrlsToStart) + for (const auto& url : pluginUrlsToStart) pluginsToStart.push_back(url.toString()); } } else { MITK_INFO(consoleLog) << "Provisionig file does not exist."; } if (!pluginsToStart.isEmpty()) { m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS] = pluginsToStart; // Use transient start with declared activation policy (this helps when the provisioning file // changes and some plug-ins should not be installed in the application any more). ctkPlugin::StartOptions startOptions(ctkPlugin::START_TRANSIENT | ctkPlugin::START_ACTIVATION_POLICY); m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS_START_OPTIONS] = static_cast(startOptions); } } }; BaseApplication::BaseApplication(int argc, char **argv) : Application(), d(new Impl(argc, argv)) { } BaseApplication::~BaseApplication() { delete d; } void BaseApplication::printHelp(const std::string &, const std::string &) { Poco::Util::HelpFormatter help(this->options()); help.setAutoIndent(); help.setCommand(this->commandName()); help.format(std::cout); exit(EXIT_OK); } void BaseApplication::setApplicationName(const QString &name) { if (nullptr != qApp) qApp->setApplicationName(name); d->m_AppName = name; } QString BaseApplication::getApplicationName() const { return nullptr != qApp ? qApp->applicationName() : d->m_AppName; } void BaseApplication::setOrganizationName(const QString &name) { if (nullptr != qApp) qApp->setOrganizationName(name); d->m_OrgaName = name; } QString BaseApplication::getOrganizationName() const { return nullptr != qApp ? qApp->organizationName() : d->m_OrgaName; } void BaseApplication::setOrganizationDomain(const QString &domain) { if (nullptr != qApp) qApp->setOrganizationDomain(domain); d->m_OrgaDomain = domain; } QString BaseApplication::getOrganizationDomain() const { return nullptr != qApp ? qApp->organizationDomain() : d->m_OrgaDomain; } void BaseApplication::setSingleMode(bool singleMode) { if (nullptr != qApp) return; d->m_SingleMode = singleMode; } bool BaseApplication::getSingleMode() const { return d->m_SingleMode; } void BaseApplication::setSafeMode(bool safeMode) { if (nullptr != qApp && nullptr == d->m_QApp) return; d->m_SafeMode = safeMode; nullptr == d->m_QApp && getSingleMode() ? static_cast(d->m_QApp)->setSafeMode(safeMode) : static_cast(d->m_QApp)->setSafeMode(safeMode); } bool BaseApplication::getSafeMode() const { return d->m_SafeMode; } void BaseApplication::setPreloadLibraries(const QStringList &libraryBaseNames) { d->m_PreloadLibs = libraryBaseNames; } QStringList BaseApplication::getPreloadLibraries() const { return d->m_PreloadLibs; } void BaseApplication::setProvisioningFilePath(const QString &filePath) { d->m_ProvFile = filePath; } QString BaseApplication::getProvisioningFilePath() const { auto provFilePath = d->m_ProvFile; // A null QString means look up a default provisioning file if (provFilePath.isNull() && nullptr != qApp) { QFileInfo appFilePath(QCoreApplication::applicationFilePath()); QDir basePath(QCoreApplication::applicationDirPath()); auto provFileName = appFilePath.baseName() + ".provisioning"; QFileInfo provFile(basePath.absoluteFilePath(provFileName)); #ifdef Q_OS_MAC /* * On macOS, if started from the build directory, the .provisioning file is located at: * * The executable path is: * * In this case we have to cdUp threetimes. * * During packaging the MitkWorkbench.provisioning file is placed at the same * level like the executable. Nothing has to be done. */ if (!provFile.exists()) { basePath.cdUp(); basePath.cdUp(); basePath.cdUp(); provFile = basePath.absoluteFilePath(provFileName); } #endif if (provFile.exists()) { provFilePath = provFile.absoluteFilePath(); } #ifdef CMAKE_INTDIR else { basePath.cdUp(); provFile.setFile(basePath.absoluteFilePath(provFileName)); if (provFile.exists()) provFilePath = provFile.absoluteFilePath(); } #endif } return provFilePath; } void BaseApplication::initializeQt() { if (nullptr != qApp) return; // If parameters have been set before, we have to store them to hand them // through to the application auto appName = this->getApplicationName(); auto orgName = this->getOrganizationName(); auto orgDomain = this->getOrganizationDomain(); // Create a QCoreApplication instance this->getQApplication(); // Provide parameters to QCoreApplication this->setApplicationName(appName); this->setOrganizationName(orgName); this->setOrganizationDomain(orgDomain); qInstallMessageHandler(outputQtMessage); } void BaseApplication::initialize(Poco::Util::Application &self) { // 1. Call the super-class method Poco::Util::Application::initialize(self); // 2. Initialize the Qt framework (by creating a QCoreApplication) this->initializeQt(); // 3. Seed the random number generator, once at startup. QTime time = QTime::currentTime(); qsrand((uint)time.msec()); // 4. Load the "default" configuration, which involves parsing // an optional .ini file and parsing any // command line arguments this->loadConfiguration(); // 5. Add configuration data from the command line and the // optional .ini file as CTK plugin // framework properties. d->initializeCTKPluginFrameworkProperties(this->config()); // 6. Initialize splash screen if an image path is provided // in the .ini file this->initializeSplashScreen(qApp); // 7. Set the custom CTK Plugin Framework storage directory QString storageDir = this->getCTKFrameworkStorageDir(); if (!storageDir.isEmpty()) d->m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE] = storageDir; // 8. Set the library search paths and the pre-load library property this->initializeLibraryPaths(); auto preloadLibs = this->getPreloadLibraries(); if (!preloadLibs.isEmpty()) d->m_FWProps[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES] = preloadLibs; // 9. Initialize the CppMicroServices library. // The initializeCppMicroServices() method reuses the // FRAMEWORK_STORAGE property, so we call it after the // getCTKFrameworkStorageDir method. this->initializeCppMicroServices(); // 10. Parse the (optional) provisioning file and set the // correct framework properties. d->parseProvisioningFile(this->getProvisioningFilePath()); // 11. Set the CTK Plugin Framework properties ctkPluginFrameworkLauncher::setFrameworkProperties(d->m_FWProps); } void BaseApplication::uninitialize() { auto pfw = this->getFramework(); if (pfw) { pfw->stop(); // Wait for up to 10 seconds for the CTK plugin framework to stop pfw->waitForStop(10000); } Poco::Util::Application::uninitialize(); } int BaseApplication::getArgc() const { return d->m_Argc; } char **BaseApplication::getArgv() const { return d->m_Argv; } QString BaseApplication::getCTKFrameworkStorageDir() const { QString storageDir; if (this->getSingleMode()) { // This function checks if an instance is already running and either sends a message to // it containing the command line arguments or checks if a new instance was forced by // providing the BlueBerry.newInstance command line argument. In the latter case, a path // to a temporary directory for the new application's storage directory is returned. storageDir = handleNewAppInstance(static_cast(d->m_QApp), d->m_Argc, d->m_Argv, ARG_NEWINSTANCE); } if (storageDir.isEmpty()) { // This is a new instance and no other instance is already running. We specify the // storage directory here (this is the same code as in berryInternalPlatform.cpp) // so that we can re-use the location for the persistent data location of the // the CppMicroServices library. // Append a hash value of the absolute path of the executable to the data location. // This allows to start the same application from different build or install trees. storageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + this->getOrganizationName() + "/" + this->getApplicationName() + '_'; storageDir += QString::number(qHash(QCoreApplication::applicationDirPath())) + "/"; } return storageDir; } void BaseApplication::initializeCppMicroServices() { auto storageDir = this->getProperty(ctkPluginConstants::FRAMEWORK_STORAGE).toString(); if (!storageDir.isEmpty()) us::ModuleSettings::SetStoragePath((storageDir + "us" + QDir::separator()).toStdString()); } QCoreApplication *BaseApplication::getQApplication() const { if (nullptr == qApp) { vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0); auto defaultFormat = QVTKOpenGLWidget::defaultFormat(); defaultFormat.setSamples(0); QSurfaceFormat::setDefaultFormat(defaultFormat); #ifdef Q_OS_OSX QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #endif QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); d->m_QApp = this->getSingleMode() ? static_cast(new QmitkSingleApplication(d->m_Argc, d->m_Argv, this->getSafeMode())) : static_cast(new QmitkSafeApplication(d->m_Argc, d->m_Argv, this->getSafeMode())); } return qApp; } void BaseApplication::initializeLibraryPaths() { QStringList suffixes; suffixes << "plugins"; #ifdef Q_OS_WINDOWS suffixes << "bin/plugins"; #ifdef CMAKE_INTDIR suffixes << "bin/" CMAKE_INTDIR "/plugins"; #endif #else suffixes << "lib/plugins"; #ifdef CMAKE_INTDIR suffixes << "lib/" CMAKE_INTDIR "/plugins"; #endif #endif #ifdef Q_OS_MAC suffixes << "../../plugins"; #endif // We add a couple of standard library search paths for plug-ins QDir appDir(QCoreApplication::applicationDirPath()); // Walk one directory up and add bin and lib sub-dirs; this might be redundant appDir.cdUp(); - for (const auto suffix : suffixes) + for (const auto& suffix : suffixes) ctkPluginFrameworkLauncher::addSearchPath(appDir.absoluteFilePath(suffix)); } int BaseApplication::main(const std::vector &args) { // Start the plugin framework and all installed plug-ins according to their auto-start setting QStringList arguments; for (auto const &arg : args) arguments.push_back(QString::fromStdString(arg)); if (nullptr != d->m_Splashscreen) { // A splash screen is displayed. Create the closing callback. d->m_SplashscreenClosingCallback = new SplashCloserCallback(d->m_Splashscreen); } return ctkPluginFrameworkLauncher::run(d->m_SplashscreenClosingCallback, QVariant::fromValue(arguments)).toInt(); } void BaseApplication::defineOptions(Poco::Util::OptionSet &options) { Poco::Util::Option helpOption("help", "h", "print this help text"); helpOption.callback(Poco::Util::OptionCallback(this, &BaseApplication::printHelp)); options.addOption(helpOption); Poco::Util::Option newInstanceOption(ARG_NEWINSTANCE.toStdString(), "", "forces a new instance of this application"); newInstanceOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(newInstanceOption); Poco::Util::Option cleanOption(ARG_CLEAN.toStdString(), "", "cleans the plugin cache"); cleanOption.callback(Poco::Util::OptionCallback(d, &Impl::handleClean)); options.addOption(cleanOption); Poco::Util::Option productOption(ARG_PRODUCT.toStdString(), "", "the id of the product to be launched"); productOption.argument("").binding(PROP_PRODUCT.toStdString()); options.addOption(productOption); Poco::Util::Option appOption(ARG_APPLICATION.toStdString(), "", "the id of the application extension to be executed"); appOption.argument("").binding(PROP_APPLICATION.toStdString()); options.addOption(appOption); Poco::Util::Option provOption(ARG_PROVISIONING.toStdString(), "", "the location of a provisioning file"); provOption.argument("").binding(ARG_PROVISIONING.toStdString()); options.addOption(provOption); Poco::Util::Option storageDirOption(ARG_STORAGE_DIR.toStdString(), "", "the location for storing persistent application data"); storageDirOption.argument("").binding(ctkPluginConstants::FRAMEWORK_STORAGE.toStdString()); options.addOption(storageDirOption); Poco::Util::Option consoleLogOption(ARG_CONSOLELOG.toStdString(), "", "log messages to the console"); consoleLogOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(consoleLogOption); Poco::Util::Option debugOption(ARG_DEBUG.toStdString(), "", "enable debug mode"); debugOption.argument("", false).binding(ctkPluginFrameworkLauncher::PROP_DEBUG.toStdString()); options.addOption(debugOption); Poco::Util::Option forcePluginOption(ARG_FORCE_PLUGIN_INSTALL.toStdString(), "", "force installing plug-ins with same symbolic name"); forcePluginOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(forcePluginOption); Poco::Util::Option preloadLibsOption(ARG_PRELOAD_LIBRARY.toStdString(), "", "preload a library"); preloadLibsOption.argument("") .repeatable(true) .callback(Poco::Util::OptionCallback(d, &Impl::handlePreloadLibraryOption)); options.addOption(preloadLibsOption); Poco::Util::Option noRegistryCacheOption(ARG_NO_REGISTRY_CACHE.toStdString(), "", "do not use a cache for the registry"); noRegistryCacheOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(noRegistryCacheOption); Poco::Util::Option noLazyRegistryCacheLoadingOption(ARG_NO_LAZY_REGISTRY_CACHE_LOADING.toStdString(), "", "do not use lazy cache loading for the registry"); noLazyRegistryCacheLoadingOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(noLazyRegistryCacheLoadingOption); Poco::Util::Option registryMultiLanguageOption(ARG_REGISTRY_MULTI_LANGUAGE.toStdString(), "", "enable multi-language support for the registry"); registryMultiLanguageOption.callback(Poco::Util::OptionCallback(d, &Impl::handleBooleanOption)); options.addOption(registryMultiLanguageOption); Poco::Util::Option splashScreenOption(ARG_SPLASH_IMAGE.toStdString(), "", "optional picture to use as a splash screen"); splashScreenOption.argument("").binding(ARG_SPLASH_IMAGE.toStdString()); options.addOption(splashScreenOption); Poco::Util::Option xargsOption(ARG_XARGS.toStdString(), "", "Extended argument list"); xargsOption.argument("").binding(ARG_XARGS.toStdString()); options.addOption(xargsOption); Poco::Util::Application::defineOptions(options); } QSharedPointer BaseApplication::getFramework() const { return ctkPluginFrameworkLauncher::getPluginFramework(); } ctkPluginContext *BaseApplication::getFrameworkContext() const { auto framework = getFramework(); return framework ? framework->getPluginContext() : nullptr; } void BaseApplication::initializeSplashScreen(QCoreApplication * application) const { auto pixmapFileNameProp = d->getProperty(ARG_SPLASH_IMAGE); if (!pixmapFileNameProp.isNull()) { auto pixmapFileName = pixmapFileNameProp.toString(); QFileInfo checkFile(pixmapFileName); if (checkFile.exists() && checkFile.isFile()) { QPixmap pixmap(checkFile.absoluteFilePath()); d->m_Splashscreen = new QSplashScreen(pixmap, Qt::WindowStaysOnTopHint); d->m_Splashscreen->show(); application->processEvents(); } } } QHash BaseApplication::getFrameworkProperties() const { return d->m_FWProps; } int BaseApplication::run() { this->init(d->m_Argc, d->m_Argv); return Application::run(); } void BaseApplication::setProperty(const QString &property, const QVariant &value) { d->m_FWProps[property] = value; } QVariant BaseApplication::getProperty(const QString &property) const { return d->getProperty(property); } void BaseApplication::installTranslator(QTranslator* translator) { this->getQApplication()->installTranslator(translator); } bool BaseApplication::isRunning() { auto app = dynamic_cast(this->getQApplication()); if (nullptr != app) app->isRunning(); mitkThrow() << "Method not implemented."; } void BaseApplication::sendMessage(const QByteArray msg) { auto app = dynamic_cast(this->getQApplication()); if (nullptr != app) app->sendMessage(msg); mitkThrow() << "Method not implemented."; } } diff --git a/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h index 2918b325c8..d173fbc3bc 100644 --- a/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h +++ b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h @@ -1,54 +1,54 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCESTGenericDICOMReaderService_H #define MITKCESTGenericDICOMReaderService_H #include namespace mitk { /** Service wrapper that auto selects (using the mitk::DICOMFileReaderSelector) the best DICOMFileReader from the DICOMReader module and loads the CEST relevant meta data from a provided cest_meta.json file or provided from the user as reader options. */ class CESTDICOMManualReaderService : public BaseDICOMReaderService { public: CESTDICOMManualReaderService(const CustomMimeType& mimeType, const std::string& description); /** Uses the AbstractFileReader Read function and add extra steps for CEST meta data */ using AbstractFileReader::Read; std::vector > Read() override; Options GetOptions() const override; us::Any GetOption(const std::string& name) const override; protected: CESTDICOMManualReaderService(const CESTDICOMManualReaderService&) = default; - CESTDICOMManualReaderService& operator=(const CESTDICOMManualReaderService&) = default; + CESTDICOMManualReaderService& operator=(const CESTDICOMManualReaderService&) = delete; std::string GetCESTMetaFilePath() const; std::string GetTRECFilePath() const; std::string GetLISTFilePath() const; mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const override; private: CESTDICOMManualReaderService* Clone() const override; }; DICOMTagPath DICOM_IMAGING_FREQUENCY_PATH(); } #endif // MITKCESTGenericDICOMReaderService_H diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp index 304c76fc12..d9d082671b 100644 --- a/Modules/CEST/src/mitkCustomTagParser.cpp +++ b/Modules/CEST/src/mitkCustomTagParser.cpp @@ -1,846 +1,846 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCustomTagParser.h" #include #include #include "mitkCESTPropertyHelper.h" #include "mitkIPropertyPersistence.h" #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include #include #include #include #include #include #include #include #include #include #include namespace { mitk::IPropertyPersistence *GetPersistenceService() { mitk::IPropertyPersistence *result = nullptr; std::vector> persRegisters = us::GetModuleContext()->GetServiceReferences(); if (!persRegisters.empty()) { if (persRegisters.size() > 1) { MITK_WARN << "Multiple property description services found. Using just one."; } result = us::GetModuleContext()->GetService(persRegisters.front()); } return result; }; } const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST."; const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision"; const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json"; const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = "\n" " \"sProtConsistencyInfo.tSystemType\" : \"SysType\",\n" " \"sProtConsistencyInfo.flNominalB0\" : \"NominalB0\",\n" " \"sTXSPEC.asNucleusInfo[0].lFrequency\" : \"FREQ\",\n" " \"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude\" : \"RefAmp\",\n" " \"alTR[0]\" : \"TR\",\n" " \"alTE[0]\" : \"TE\",\n" " \"lAverages\" : \"averages\",\n" " \"lRepetitions\" : \"repetitions\",\n" " \"adFlipAngleDegree[0]\" : \"ImageFlipAngle\",\n" " \"lTotalScanTimeSec\" : \"TotalScanTime\","; const std::string mitk::CustomTagParser::m_DefaultJsonString = "{\n" " \"default mapping, corresponds to revision 1416\" : \"revision_json\",\n" " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\",\n" " \"sWiPMemBlock.alFree[2]\" : \"RecoveryMode\",\n" " \"sWiPMemBlock.alFree[3]\" : \"DoubleIrrMode\",\n" " \"sWiPMemBlock.alFree[4]\" : \"BinomMode\",\n" " \"sWiPMemBlock.alFree[5]\" : \"MtMode\",\n" " \"sWiPMemBlock.alFree[6]\" : \"PreparationType\",\n" " \"sWiPMemBlock.alFree[7]\" : \"PulseType\",\n" " \"sWiPMemBlock.alFree[8]\" : \"SamplingType\",\n" " \"sWiPMemBlock.alFree[9]\" : \"SpoilingType\",\n" " \"sWiPMemBlock.alFree[10]\" : \"measurements\",\n" " \"sWiPMemBlock.alFree[11]\" : \"NumberOfPulses\",\n" " \"sWiPMemBlock.alFree[12]\" : \"NumberOfLockingPulses\",\n" " \"sWiPMemBlock.alFree[13]\" : \"PulseDuration\",\n" " \"sWiPMemBlock.alFree[14]\" : \"DutyCycle\",\n" " \"sWiPMemBlock.alFree[15]\" : \"RecoveryTime\",\n" " \"sWiPMemBlock.alFree[16]\" : \"RecoveryTimeM0\",\n" " \"sWiPMemBlock.alFree[17]\" : \"ReadoutDelay\",\n" " \"sWiPMemBlock.alFree[18]\" : \"BinomDuration\",\n" " \"sWiPMemBlock.alFree[19]\" : \"BinomDistance\",\n" " \"sWiPMemBlock.alFree[20]\" : \"BinomNumberofPulses\",\n" " \"sWiPMemBlock.alFree[21]\" : \"BinomPreRepetions\",\n" " \"sWiPMemBlock.alFree[22]\" : \"BinomType\",\n" " \"sWiPMemBlock.adFree[1]\" : \"Offset\",\n" " \"sWiPMemBlock.adFree[2]\" : \"B1Amplitude\",\n" " \"sWiPMemBlock.adFree[3]\" : \"AdiabaticPulseMu\",\n" " \"sWiPMemBlock.adFree[4]\" : \"AdiabaticPulseBW\",\n" " \"sWiPMemBlock.adFree[5]\" : \"AdiabaticPulseLength\",\n" " \"sWiPMemBlock.adFree[6]\" : \"AdiabaticPulseAmp\",\n" " \"sWiPMemBlock.adFree[7]\" : \"FermiSlope\",\n" " \"sWiPMemBlock.adFree[8]\" : \"FermiFWHM\",\n" " \"sWiPMemBlock.adFree[9]\" : \"DoubleIrrDuration\",\n" " \"sWiPMemBlock.adFree[10]\" : \"DoubleIrrAmplitude\",\n" " \"sWiPMemBlock.adFree[11]\" : \"DoubleIrrRepetitions\",\n" " \"sWiPMemBlock.adFree[12]\" : \"DoubleIrrPreRepetitions\"\n" "}"; mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("") { std::string pathToDirectory; std::string fileName; itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName); m_DicomDataPath = pathToDirectory; m_ParseStrategy = "Automatic"; m_RevisionMappingStrategy = "Fuzzy"; } std::string mitk::CustomTagParser::ExtractRevision(std::string sequenceFileName) { //all rules are case insesitive. Thus we convert everything to lower case //in order to check everything only once. std::string cestPrefix = "cest"; std::string cestPrefix2 = "_cest"; std::string cestPrefix3 = "\\cest"; //this version covers the fact that the strings extracted //from the SIEMENS tag has an additional prefix that is seperated by backslash. std::string revisionPrefix = "_rev"; std::transform(sequenceFileName.begin(), sequenceFileName.end(), sequenceFileName.begin(), ::tolower); bool isCEST = sequenceFileName.compare(0, cestPrefix.length(), cestPrefix) == 0; std::size_t foundPosition = 0; if (!isCEST) { foundPosition = sequenceFileName.find(cestPrefix2); isCEST = foundPosition != std::string::npos; } if (!isCEST) { foundPosition = sequenceFileName.find(cestPrefix3); isCEST = foundPosition != std::string::npos; } if (!isCEST) { mitkThrow() << "Invalid CEST sequence file name. No CEST prefix found. Could not extract revision."; } foundPosition = sequenceFileName.find(revisionPrefix, foundPosition); if (foundPosition == std::string::npos) { mitkThrow() << "Invalid CEST sequence file name. No revision prefix was found in CEST sequence file name. Could not extract revision."; } std::string revisionString = sequenceFileName.substr(foundPosition + revisionPrefix.length(), std::string::npos); std::size_t firstNoneNumber = revisionString.find_first_not_of("0123456789"); if (firstNoneNumber != std::string::npos) { revisionString.erase(firstNoneNumber, std::string::npos); } return revisionString; } bool mitk::CustomTagParser::IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString) { bool isT1 = false; // if a forced parse strategy is set, use that one if ("T1" == m_ParseStrategy) { return true; } if ("CEST/WASABI" == m_ParseStrategy) { return false; } if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType)) { isT1 = true; } // How to interpret the recoveryMode depends on the age of the sequence // older sequences use 0 = false and 1 = true, newer ones 1 = false and 2 = true. // A rough rule of thumb is to assume that if the SpoilingType is 0, then the first // convention is chosen, if it is 1, then the second applies. Otherwise // we assume revision 1485 and newer to follow the new convention. // This unfortunate heuristic is due to somewhat arbitrary CEST sequence implementations. if (!isT1) { std::string thisIsTrue = "1"; std::string thisIsFalse = "0"; if ("0" == spoilingType) { thisIsFalse = "0"; thisIsTrue = "1"; } else if ("1" == spoilingType) { thisIsFalse = "1"; thisIsTrue = "2"; } else { int revisionNrWeAssumeToBeDifferenciating = 1485; if (std::stoi(revisionString) - revisionNrWeAssumeToBeDifferenciating < 0) { thisIsFalse = "0"; thisIsTrue = "1"; } else { thisIsFalse = "1"; thisIsTrue = "2"; } } if (thisIsFalse == recoveryMode) { isT1 = false; } else if (thisIsTrue == recoveryMode) { isT1 = true; } } return isT1; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString) { auto results = mitk::PropertyList::New(); if ("" == dicomPropertyString) { //MITK_ERROR << "Could not parse empty custom dicom string"; return results; } std::map privateParameters; // The Siemens private tag contains information like "43\52\23\34". // We jump over each "\" and convert the number; std::string bytes; { const std::size_t SUBSTR_LENGTH = 2; const std::size_t INPUT_LENGTH = dicomPropertyString.length(); if (INPUT_LENGTH < SUBSTR_LENGTH) return results; const std::size_t MAX_INPUT_OFFSET = INPUT_LENGTH - SUBSTR_LENGTH; bytes.reserve(INPUT_LENGTH / 3 + 1); try { for (std::size_t i = 0; i <= MAX_INPUT_OFFSET; i += 3) { std::string byte_string = dicomPropertyString.substr(i, SUBSTR_LENGTH); int byte = static_cast(std::stoi(byte_string.c_str(), nullptr, 16)); bytes.push_back(byte); } } catch (const std::invalid_argument&) // std::stoi() could not perform conversion { return results; } } // extract parameter list std::string parameterListString; { const std::string ASCCONV_BEGIN = "### ASCCONV BEGIN ###"; const std::string ASCCONV_END = "### ASCCONV END ###"; auto offset = bytes.find(ASCCONV_BEGIN); if (std::string::npos == offset) return results; offset += ASCCONV_BEGIN.length(); auto count = bytes.find(ASCCONV_END, offset); if (std::string::npos == count) return results; count -= offset; parameterListString = bytes.substr(offset, count); } boost::replace_all(parameterListString, "\r\n", "\n"); boost::char_separator newlineSeparator("\n"); boost::tokenizer> parameters(parameterListString, newlineSeparator); for (const auto ¶meter : parameters) { std::vector parts; boost::split(parts, parameter, boost::is_any_of("=")); if (parts.size() == 2) { parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end()); parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space privateParameters[parts[0]] = parts[1]; } } std::string revisionString = ""; try { revisionString = ExtractRevision(privateParameters["tSequenceFileName"]); } catch (const std::exception &e) { MITK_ERROR << "Cannot deduce revision information. Reason: "<< e.what(); return results; } results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString)); std::string jsonString = GetRevisionAppropriateJSONString(revisionString); boost::property_tree::ptree root; std::istringstream jsonStream(jsonString); try { boost::property_tree::read_json(jsonStream, root); } catch (const boost::property_tree::json_parser_error &e) { mitkThrow() << "Could not parse json file. Error was:\n" << e.what(); } for (auto it : root) { if (it.second.empty()) { std::string propertyName = m_CESTPropertyPrefix + it.second.data(); if (m_JSONRevisionPropertyName == propertyName) { results->SetProperty(propertyName, mitk::StringProperty::New(it.first)); } else { results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.first])); } } else { MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file."; } } std::string offset = ""; std::string measurements = ""; results->GetStringProperty("CEST.Offset", offset); results->GetStringProperty("CEST.measurements", measurements); if (measurements.empty()) { std::string stringRepetitions = ""; results->GetStringProperty("CEST.repetitions", stringRepetitions); std::string stringAverages = ""; results->GetStringProperty("CEST.averages", stringAverages); const auto ERROR_STRING = "Could not find measurements, fallback assumption of repetitions + averages could not be determined either."; if (!stringRepetitions.empty() && !stringAverages.empty()) { std::stringstream measurementStream; try { measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages); measurements = measurementStream.str(); MITK_INFO << "Could not find measurements, assuming repetitions + averages. That is: " << measurements; } catch (const std::invalid_argument&) { MITK_ERROR << ERROR_STRING; } } else { MITK_WARN << ERROR_STRING; } } std::string preparationType = ""; std::string recoveryMode = ""; std::string spoilingType = ""; results->GetStringProperty(CEST_PROPERTY_NAME_PREPERATIONTYPE().c_str(), preparationType); results->GetStringProperty(CEST_PROPERTY_NAME_RECOVERYMODE().c_str(), recoveryMode); results->GetStringProperty(CEST_PROPERTY_NAME_SPOILINGTYPE().c_str(), spoilingType); if (this->IsT1Sequence(preparationType, recoveryMode, spoilingType, revisionString)) { MITK_INFO << "Parsed as T1 image"; std::stringstream trecStream; std::string trecPath = m_DicomDataPath + "/TREC.txt"; auto trec = ReadListFromFile(trecPath); if(trec.empty()) { MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath; } results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trec.c_str()); } else { MITK_INFO << "Parsed as CEST or WASABI image"; std::string sampling = ""; bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling); if (hasSamplingInformation) { std::string offsets = GetOffsetString(sampling, offset, measurements); results->SetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets.c_str()); } else { MITK_WARN << "Could not determine sampling type."; } } //persist all properties mitk::IPropertyPersistence *persSrv = GetPersistenceService(); if (persSrv) { auto propertyMap = results->GetMap(); for (auto const &prop : *propertyMap) { PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New(); std::string key = prop.first; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(prop.first, key); persSrv->AddInfo(info); } } return results; } std::string mitk::CustomTagParser::ReadListFromFile(const std::string& filePath) { std::stringstream listStream; std::ifstream list(filePath.c_str()); list.imbue(std::locale("C")); if (list.good()) { std::string currentValue; while (std::getline(list, currentValue)) { listStream << currentValue << " "; } } return listStream.str(); } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty) { if (!dicomProperty) { MITK_ERROR << "DICOM property empty"; } auto results = mitk::PropertyList::New(); if (dicomProperty) { results = ParseDicomPropertyString(dicomProperty->GetValue()); } return results; } std::vector mitk::CustomTagParser::GetInternalRevisions() { const std::vector configs = us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false); std::vector availableRevisionsVector; - for (auto const resource : configs) + for (const auto& resource : configs) { availableRevisionsVector.push_back(std::stoi(resource.GetBaseName())); } return availableRevisionsVector; } std::vector mitk::CustomTagParser::GetExternalRevisions() { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json"; std::set JsonFiles; Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS); std::vector availableRevisionsVector; - for (auto const jsonpath : JsonFiles) + for (const auto& jsonpath : JsonFiles) { std::string jsonDir; std::string jsonName; itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName); std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName); // disregard jsons which contain letters in their name bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos); if(onlyNumbers) { availableRevisionsVector.push_back(std::stoi(revision)); } } return availableRevisionsVector; } std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector) { // descending order std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>()); int revision = std::stoi(revisionString); int index = 0; int numberOfRevisions = availableRevisionsVector.size(); while (index < numberOfRevisions) { // current mapping still has a higher revision number if ((availableRevisionsVector[index] - revision) > 0) { ++index; } else { break; } } if (index < numberOfRevisions) { std::stringstream foundRevisionStream; foundRevisionStream << availableRevisionsVector[index]; return foundRevisionStream.str(); } return ""; } void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString) { m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions()); m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions()); if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) || (0 == m_ClosestExternalRevision.compare(revisionString)))) { // strict revision mapping and neither revision does match the dicom meta data std::stringstream errorMessageStream; errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString << " has no known matching parameter mapping. To use the closest known older parameter mapping select the " << "\"Fuzzy\" revision mapping option when loading the data.\n" << "\nCurrently known revision mappings are:\n Precompiled:"; for (const auto revision : GetInternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n External:"; for (const auto revision : GetExternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n"; std::string stringToJSONDirectory = GetExternalJSONDirectory(); errorMessageStream << stringToJSONDirectory; errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString << ".json there. You might need to create the directory first."; mitkThrow() << errorMessageStream.str(); } } std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString) { std::string returnValue = ""; if ("" == revisionString) { MITK_WARN << "Could not extract revision"; } else { GetClosestLowerRevision(revisionString); bool useExternal = false; bool useInternal = false; if ("" != m_ClosestExternalRevision) { useExternal = true; } if ("" != m_ClosestInternalRevision) { useInternal = true; } if (useExternal && useInternal) { if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision)) { useExternal = false; } } if (useExternal) { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json"; std::ifstream externalJSON(prospectiveJsonPath.c_str()); if (externalJSON.good()) { MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath; std::stringstream buffer; buffer << externalJSON.rdbuf(); returnValue = buffer.str(); useInternal = false; } } if (useInternal) { std::string filename = m_ClosestInternalRevision + ".json"; us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename); if (jsonResource.IsValid() && jsonResource.IsFile()) { MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision " << m_ClosestInternalRevision; us::ModuleResourceStream jsonStream(jsonResource); std::stringstream buffer; buffer << jsonStream.rdbuf(); returnValue = buffer.str(); } } } if ("" == returnValue) { MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString << ", using default mapping."; returnValue = m_DefaultJsonString; } // inject the revision independent mapping before the first newline { returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping); } return returnValue; } std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements) { std::stringstream results; results.imbue(std::locale("C")); std::string normalizationIndicatingOffset = "-300"; double offsetDouble = 0.0; int measurementsInt = 0; bool validOffset = false; bool validMeasurements = false; if ("" != offset) { validOffset = true; offsetDouble = std::stod(offset); } if ("" != measurements) { validMeasurements = true; measurementsInt = std::stoi(measurements); } std::vector offsetVector; if (validOffset && validMeasurements) { for (int step = 0; step < measurementsInt -1; ++step) { double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0); offsetVector.push_back(currentOffset); } } else { MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type."; } if (samplingType == "1" || samplingType == "Regular") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (const auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "2" || samplingType == "Alternating") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (auto& entry : offsetVector) { entry = std::abs(entry); } std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>()); for (unsigned int index = 0; index < offsetVector.size(); ++index) { offsetVector[index] = std::pow(-1, index) * offsetVector[index]; } for (auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "3" || samplingType == "List") { std::string listPath = m_DicomDataPath + "/LIST.txt"; auto values = ReadListFromFile(listPath); if (!values.empty()) { results << values; } else { MITK_ERROR << "Could not load list at " << listPath; } } else if (samplingType == "4" || samplingType == "SingleOffset") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (int step = 0; step < measurementsInt - 1; ++step) { results << offsetDouble << " "; } } } else { MITK_WARN << "Encountered unknown sampling type."; } std::string resultString = results.str(); // replace multiple spaces by a single space std::string::iterator newEnditerator = std::unique(resultString.begin(), resultString.end(), [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } ); resultString.erase(newEnditerator, resultString.end()); if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' ')) { resultString.erase(resultString.end() - 1, resultString.end()); } if ((resultString.length() > 0) && (resultString.at(0) == ' ')) { resultString.erase(resultString.begin(), ++(resultString.begin())); } return resultString; } void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy) { m_ParseStrategy = parseStrategy; } void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy) { m_RevisionMappingStrategy = revisionMappingStrategy; } std::string mitk::CustomTagParser::GetExternalJSONDirectory() { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string stringToModule; std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName); std::stringstream jsonDirectory; jsonDirectory << stringToModule << "/CESTRevisionMapping"; return jsonDirectory.str(); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index e05a314b21..5e4e81c3d6 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,852 +1,852 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include #include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; } bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; } mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); } bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); } std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const { auto path = this->GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } return PropertyKeyPathToPropertyRegEx(path); } //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; } //end workaround for T24729 bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { return !this->GetExistingRelations(owner).empty(); } bool mitk::PropertyRelationRuleBase::HasRelation( const IPropertyProvider* source, const IPropertyProvider* destination, RelationType requiredRelation) const { auto relTypes = this->GetRelationTypes(source, destination); if (requiredRelation == RelationType::None) { return !relTypes.empty(); } RelationVectorType allowedTypes = { RelationType::Complete }; if (requiredRelation == RelationType::Data) { allowedTypes.emplace_back(RelationType::Data); } else if (requiredRelation == RelationType::ID) { allowedTypes.emplace_back(RelationType::ID); } return relTypes.end() != std::find_first_of(relTypes.begin(), relTypes.end(), allowedTypes.begin(), allowedTypes.end()); } mitk::PropertyRelationRuleBase::RelationVectorType mitk::PropertyRelationRuleBase::GetRelationTypes( const IPropertyProvider* source, const IPropertyProvider* destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } auto instanceIDs_IDLayer = this->GetInstanceID_IDLayer(source, destination); auto relIDs_dataLayer = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relIDs_dataLayer.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Relation UID: " << relIDs_dataLayer.front().first; } bool hasComplete = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasID = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relIDs_dataLayer.end() == std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); bool hasData = relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { return instanceIDs_IDLayer.end() == std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; }); }); RelationVectorType result; if (hasData) { result.emplace_back(RelationType::Data); } if (hasID) { result.emplace_back(RelationType::ID); } if (hasComplete) { result.emplace_back(RelationType::Complete); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( const IPropertyProvider *source, RelationType layer) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } RelationUIDVectorType relationUIDs; InstanceIDVectorType instanceIDs; if (layer != RelationType::Data) { auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); auto regEx = std::regex(ruleIDRegExStr); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto& key : keys) { if (std::regex_match(key, regEx)) { auto idProp = source->GetConstProperty(key); auto ruleID = idProp->GetValueAsString(); if (this->IsSupportedRuleID(ruleID)) { auto instanceID = this->GetInstanceIDByPropertyName(key); instanceIDs.emplace_back(instanceID); relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } } } } if (layer == RelationType::ID) { return relationUIDs; } DataRelationUIDVectorType relationUIDandRuleID_Data; if (layer != RelationType::ID) { relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source, nullptr, instanceIDs); } RelationUIDVectorType relationUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relationUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); if (layer == RelationType::Data) { return relationUIDs_Data; } std::sort(relationUIDs.begin(), relationUIDs.end()); std::sort(relationUIDs_Data.begin(), relationUIDs_Data.end()); RelationUIDVectorType result; if (layer == RelationType::Complete) { std::set_intersection(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } else { std::set_union(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); } return result; } mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } RelationUIDVectorType relUIDs_id; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); - for (const auto instanceID : instanceIDs) + for (const auto& instanceID : instanceIDs) { relUIDs_id.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } DataRelationUIDVectorType relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source,destination,instanceIDs); RelationUIDVectorType relUIDs_Data; std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relUIDs_Data), [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); std::sort(relUIDs_id.begin(), relUIDs_id.end()); std::sort(relUIDs_Data.begin(), relUIDs_Data.end()); RelationUIDVectorType result; std::set_union(relUIDs_id.begin(), relUIDs_id.end(), relUIDs_Data.begin(), relUIDs_Data.end(), std::back_inserter(result)); return result; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(instanceID).AddElement("relationUID"))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; } mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto identifiable = CastProviderAsIdentifiable(destination); InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; } const mitk::Identifiable* mitk::PropertyRelationRuleBase::CastProviderAsIdentifiable(const mitk::IPropertyProvider* destination) const { auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } return identifiable; } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); auto relUIDs_data = this->GetRelationUIDs_DataLayer(source, destination, {}); if (relUIDs_data.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. RelationUID ID: " << relUIDs_data.front().first; } bool hasDatalayer = !relUIDs_data.empty(); RelationUIDType relationUID = this->CreateRelationUID(); InstanceIDType instanceID = NULL_INSTANCE_ID(); if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { try { instanceID = this->GetInstanceIDByRelationUID(source, relUIDs_data.front().first); } catch(...) { } } if(instanceID == NULL_INSTANCE_ID()) { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("ruleID")); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { auto identifiable = this->CastProviderAsIdentifiable(destination); if (identifiable) { auto destUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } this->Connect_datalayer(source, destination, instanceID); return relationUID; } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (destination == nullptr) { mitkThrow() << "Error. Destination is invalid. Cannot disconnect."; } try { const auto relationUIDs = this->GetRelationUIDs(source, destination); - for (const auto relUID: relationUIDs) + for (const auto& relUID: relationUIDs) { this->Disconnect(source, relUID, layer); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } } void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer) const { if (source == nullptr) { mitkThrow() << "Error. Source is invalid. Cannot disconnect."; } if (layer == RelationType::Data || layer == RelationType::Complete) { this->Disconnect_datalayer(source, relationUID); } auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); if ((layer == RelationType::ID || layer == RelationType::Complete) && instanceID != NULL_INSTANCE_ID()) { auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } } mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); } /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = PropertyKeyPathToPropertyRegEx(this->GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(newID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; } itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); } mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; } mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRootKeyPath().AddElement(instanceID).AddElement("ruleID"); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; } std::string mitk::PropertyRelationRuleBase::GetDestinationUIDByInstanceID(const IPropertyProvider* source, const InstanceIDType& instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID"); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } return result; } namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( const IPropertyProvider *destination, RelationType exclusiveRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto check = [destination, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(node, destination, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( const IPropertyProvider *source, RelationType exclusiveRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto check = [source, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(source, node, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelations.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelations.cpp index 4d5f4fa489..2d9d397dc4 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelations.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelations.cpp @@ -1,152 +1,152 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include mitk::PropertyRelations::PropertyRelations() { } mitk::PropertyRelations::~PropertyRelations() { } bool mitk::PropertyRelations::AddRule(const PropertyRelationRuleBase *rule, bool overwrite) { if (!rule) { return false; } if (rule->GetRuleID().empty()) { return false; } auto ruleFinding = m_RuleMap.find(rule->GetRuleID()); bool result = false; if (ruleFinding == m_RuleMap.end()) { m_RuleMap.insert(std::make_pair(rule->GetRuleID(), rule)); result = true; } else if (overwrite) { m_RuleMap[rule->GetRuleID()] = rule; result = true; } return result; } mitk::PropertyRelations::RuleResultVectorType mitk::PropertyRelations::GetRulesForSource(const IPropertyProvider* source) const { RuleResultVectorType result; if (source) { - for (const auto rule : m_RuleMap) + for (const auto& rule : m_RuleMap) { if (rule.second->IsSource(source)) { result.push_back(rule.second); } } } return result; }; mitk::PropertyRelations::RuleResultVectorType mitk::PropertyRelations::GetRulesForSourceCandidate(const IPropertyProvider* sourceCandidate) const { RuleResultVectorType result; - for (const auto rule : m_RuleMap) + for (const auto& rule : m_RuleMap) { if (rule.second->IsSourceCandidate(sourceCandidate)) { result.push_back(rule.second); } } return result; }; mitk::PropertyRelations::RuleResultVectorType mitk::PropertyRelations::GetRulesForDestinationCandidate(const IPropertyProvider* destCandidate) const { RuleResultVectorType result; - for (const auto rule : m_RuleMap) + for (const auto& rule : m_RuleMap) { if (rule.second->IsDestinationCandidate(destCandidate)) { result.push_back(rule.second); } } return result; }; mitk::PropertyRelationRuleBase::ConstPointer mitk::PropertyRelations::GetRule(const RuleIDType &ruleID) const { mitk::PropertyRelationRuleBase::ConstPointer result; auto ruleFinding = m_RuleMap.find(ruleID); if (ruleFinding != m_RuleMap.end()) { result = ruleFinding->second; } return result; }; mitk::PropertyRelations::RuleIDVectorType mitk::PropertyRelations::GetRuleIDs() const { RuleIDVectorType result; for (const auto& rule : m_RuleMap) { result.push_back(rule.first); } return result; }; bool mitk::PropertyRelations::HasRuleForSource(const IPropertyProvider* source) const { return !this->GetRulesForSource(source).empty(); }; void mitk::PropertyRelations::RemoveAllRules() { m_RuleMap.clear(); } void mitk::PropertyRelations::RemoveRule(const RuleIDType &ruleID) { if (!ruleID.empty()) { m_RuleMap.erase(ruleID); } } mitk::IPropertyRelations *mitk::CreateTestInstancePropertyRelations() { return new PropertyRelations(); }; diff --git a/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp b/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp index 40fd836151..4805e6b3b6 100644 --- a/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp +++ b/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp @@ -1,510 +1,510 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include "mitkPlanarAngle.h" #include "mitkPlanarCircle.h" #include "mitkPlanarCross.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarLine.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlaneGeometry.h" #include "mitkGeometry3D.h" #include "mitkAbstractFileIO.h" #include "mitkFileReaderRegistry.h" #include "mitkFileWriterRegistry.h" #include "mitkIOUtil.h" #include /** \brief Helper class for testing PlanarFigure reader and writer classes. */ class PlanarFigureIOTestClass { public: typedef std::map PlanarFigureMap; typedef std::map PlanarFigureToStreamMap; static PlanarFigureMap CreatePlanarFigures() { PlanarFigureMap planarFigures; // Create PlaneGeometry on which to place the PlanarFigures mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(100.0, 100.0); // Create a few sample points for PlanarFigure placement mitk::Point2D p0; p0[0] = 20.0; p0[1] = 20.0; mitk::Point2D p1; p1[0] = 80.0; p1[1] = 80.0; mitk::Point2D p2; p2[0] = 90.0; p2[1] = 10.0; mitk::Point2D p3; p3[0] = 10.0; p3[1] = 90.0; // Create PlanarAngle mitk::PlanarAngle::Pointer planarAngle = mitk::PlanarAngle::New(); planarAngle->SetPlaneGeometry(planeGeometry); planarAngle->PlaceFigure(p0); planarAngle->SetCurrentControlPoint(p1); planarAngle->AddControlPoint(p2); planarAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarAngle",planarAngle.GetPointer()); // Create PlanarCircle mitk::PlanarCircle::Pointer planarCircle = mitk::PlanarCircle::New(); planarCircle->SetPlaneGeometry(planeGeometry); planarCircle->PlaceFigure(p0); planarCircle->SetCurrentControlPoint(p1); planarCircle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarCircle",planarCircle.GetPointer()); // Create PlanarCross mitk::PlanarCross::Pointer planarCross = mitk::PlanarCross::New(); planarCross->SetSingleLineMode(false); planarCross->SetPlaneGeometry(planeGeometry); planarCross->PlaceFigure(p0); planarCross->SetCurrentControlPoint(p1); planarCross->AddControlPoint(p2); planarCross->AddControlPoint(p3); planarCross->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarCross",planarCross.GetPointer()); // Create PlanarFourPointAngle mitk::PlanarFourPointAngle::Pointer planarFourPointAngle = mitk::PlanarFourPointAngle::New(); planarFourPointAngle->SetPlaneGeometry(planeGeometry); planarFourPointAngle->PlaceFigure(p0); planarFourPointAngle->SetCurrentControlPoint(p1); planarFourPointAngle->AddControlPoint(p2); planarFourPointAngle->AddControlPoint(p3); planarFourPointAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarFourPointAngle",planarFourPointAngle.GetPointer()); // Create PlanarLine mitk::PlanarLine::Pointer planarLine = mitk::PlanarLine::New(); planarLine->SetPlaneGeometry(planeGeometry); planarLine->PlaceFigure(p0); planarLine->SetCurrentControlPoint(p1); planarLine->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarLine",planarLine.GetPointer()); // Create PlanarPolygon mitk::PlanarPolygon::Pointer planarPolygon = mitk::PlanarPolygon::New(); planarPolygon->SetClosed(false); planarPolygon->SetPlaneGeometry(planeGeometry); planarPolygon->PlaceFigure(p0); planarPolygon->SetCurrentControlPoint(p1); planarPolygon->AddControlPoint(p2); planarPolygon->AddControlPoint(p3); planarPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarPolygon",planarPolygon.GetPointer()); // Create PlanarSubdivisionPolygon mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon = mitk::PlanarSubdivisionPolygon::New(); planarSubdivisionPolygon->SetClosed(false); planarSubdivisionPolygon->SetPlaneGeometry(planeGeometry); planarSubdivisionPolygon->PlaceFigure(p0); planarSubdivisionPolygon->SetCurrentControlPoint(p1); planarSubdivisionPolygon->AddControlPoint(p2); planarSubdivisionPolygon->AddControlPoint(p3); planarSubdivisionPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarSubdivisionPolygon",planarSubdivisionPolygon.GetPointer()); // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectangle = mitk::PlanarRectangle::New(); planarRectangle->SetPlaneGeometry(planeGeometry); planarRectangle->PlaceFigure(p0); planarRectangle->SetCurrentControlPoint(p1); planarRectangle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarRectangle",planarRectangle.GetPointer()); // create preciseGeometry which is using float coordinates mitk::PlaneGeometry::Pointer preciseGeometry = mitk::PlaneGeometry::New(); mitk::Vector3D right; right[0] = 0.0; right[1] = 1.23456; right[2] = 0.0; mitk::Vector3D down; down[0] = 1.23456; down[1] = 0.0; down[2] = 0.0; mitk::Vector3D spacing; spacing[0] = 0.0123456; spacing[1] = 0.0123456; spacing[2] = 1.123456; preciseGeometry->InitializeStandardPlane(right, down, &spacing); // convert points into the precise coordinates mitk::Point2D p0precise; p0precise[0] = p0[0] * spacing[0]; p0precise[1] = p0[1] * spacing[1]; mitk::Point2D p1precise; p1precise[0] = p1[0] * spacing[0]; p1precise[1] = p1[1] * spacing[1]; mitk::Point2D p2precise; p2precise[0] = p2[0] * spacing[0]; p2precise[1] = p2[1] * spacing[1]; mitk::Point2D p3precise; p3precise[0] = p3[0] * spacing[0]; p3precise[1] = p3[1] * spacing[1]; // Now all PlanarFigures are create using the precise Geometry // Create PlanarCross mitk::PlanarCross::Pointer nochncross = mitk::PlanarCross::New(); nochncross->SetSingleLineMode(false); nochncross->SetPlaneGeometry(preciseGeometry); nochncross->PlaceFigure(p0precise); nochncross->SetCurrentControlPoint(p1precise); nochncross->AddControlPoint(p2precise); nochncross->AddControlPoint(p3precise); nochncross->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("nochncross", nochncross.GetPointer()); // Create PlanarAngle mitk::PlanarAngle::Pointer planarAnglePrecise = mitk::PlanarAngle::New(); planarAnglePrecise->SetPlaneGeometry(preciseGeometry); planarAnglePrecise->PlaceFigure(p0precise); planarAnglePrecise->SetCurrentControlPoint(p1precise); planarAnglePrecise->AddControlPoint(p2precise); planarAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarAnglePrecise",planarAnglePrecise.GetPointer()); // Create PlanarCircle mitk::PlanarCircle::Pointer planarCirclePrecise = mitk::PlanarCircle::New(); planarCirclePrecise->SetPlaneGeometry(preciseGeometry); planarCirclePrecise->PlaceFigure(p0precise); planarCirclePrecise->SetCurrentControlPoint(p1precise); planarCirclePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarCirclePrecise",planarCirclePrecise.GetPointer()); // Create PlanarFourPointAngle mitk::PlanarFourPointAngle::Pointer planarFourPointAnglePrecise = mitk::PlanarFourPointAngle::New(); planarFourPointAnglePrecise->SetPlaneGeometry(preciseGeometry); planarFourPointAnglePrecise->PlaceFigure(p0precise); planarFourPointAnglePrecise->SetCurrentControlPoint(p1precise); planarFourPointAnglePrecise->AddControlPoint(p2precise); planarFourPointAnglePrecise->AddControlPoint(p3precise); planarFourPointAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarFourPointAnglePrecise",planarFourPointAnglePrecise.GetPointer()); // Create PlanarLine mitk::PlanarLine::Pointer planarLinePrecise = mitk::PlanarLine::New(); planarLinePrecise->SetPlaneGeometry(preciseGeometry); planarLinePrecise->PlaceFigure(p0precise); planarLinePrecise->SetCurrentControlPoint(p1precise); planarLinePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarLinePrecise",planarLinePrecise.GetPointer()); // Create PlanarPolygon mitk::PlanarPolygon::Pointer planarPolygonPrecise = mitk::PlanarPolygon::New(); planarPolygonPrecise->SetClosed(false); planarPolygonPrecise->SetPlaneGeometry(preciseGeometry); planarPolygonPrecise->PlaceFigure(p0precise); planarPolygonPrecise->SetCurrentControlPoint(p1precise); planarPolygonPrecise->AddControlPoint(p2precise); planarPolygonPrecise->AddControlPoint(p3precise); planarPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarPolygonPrecise",planarPolygonPrecise.GetPointer()); // Create PlanarSubdivisionPolygon mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygonPrecise = mitk::PlanarSubdivisionPolygon::New(); planarSubdivisionPolygonPrecise->SetClosed(false); planarSubdivisionPolygonPrecise->SetPlaneGeometry(preciseGeometry); planarSubdivisionPolygonPrecise->PlaceFigure(p0precise); planarSubdivisionPolygonPrecise->SetCurrentControlPoint(p1precise); planarSubdivisionPolygonPrecise->AddControlPoint(p2precise); planarSubdivisionPolygonPrecise->AddControlPoint(p3precise); planarSubdivisionPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarSubdivisionPolygonPrecise",planarSubdivisionPolygonPrecise.GetPointer()); // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectanglePrecise = mitk::PlanarRectangle::New(); planarRectanglePrecise->SetPlaneGeometry(preciseGeometry); planarRectanglePrecise->PlaceFigure(p0precise); planarRectanglePrecise->SetCurrentControlPoint(p1precise); planarRectanglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); planarFigures.emplace("planarRectanglePrecise",planarRectanglePrecise.GetPointer()); return planarFigures; } static PlanarFigureMap CreateClonedPlanarFigures(PlanarFigureMap original) { PlanarFigureMap copiedPlanarFigures; for (const auto& pf : original) { mitk::PlanarFigure::Pointer copiedFigure = pf.second->Clone(); copiedPlanarFigures[pf.first] = copiedFigure; } return copiedPlanarFigures; } static void VerifyPlanarFigures(PlanarFigureMap &referencePfs, PlanarFigureMap &testPfs) { PlanarFigureMap::iterator it1, it2; int i = 0; for (it1 = referencePfs.begin(); it1 != referencePfs.end(); ++it1) { bool planarFigureFound = false; int j = 0; for (it2 = testPfs.begin(); it2 != testPfs.end(); ++it2) { // Compare PlanarFigures (returns false if different types) if (ComparePlanarFigures(it1->second, it2->second)) { planarFigureFound = true; } ++j; } // Test if (at least) on PlanarFigure of the first type was found in the second list MITK_TEST_CONDITION_REQUIRED(planarFigureFound, "Testing if " << it1->second->GetNameOfClass() << " has a counterpart " << i); ++i; } } static bool ComparePlanarFigures(const mitk::PlanarFigure *referencePf, const mitk::PlanarFigure *testPf) { // Test if PlanarFigures are of same type; otherwise return if (strcmp(referencePf->GetNameOfClass(), testPf->GetNameOfClass()) != 0) { return false; } if (strcmp(referencePf->GetNameOfClass(), "PlanarCross") == 0) { std::cout << "Planar Cross Found" << std::endl; } // Test for equal number of control points if (referencePf->GetNumberOfControlPoints() != testPf->GetNumberOfControlPoints()) { return false; } // Test if all control points are equal for (unsigned int i = 0; i < referencePf->GetNumberOfControlPoints(); ++i) { mitk::Point2D point1 = referencePf->GetControlPoint(i); mitk::Point2D point2 = testPf->GetControlPoint(i); if (point1.EuclideanDistanceTo(point2) >= mitk::eps) { return false; } } // Test for equal number of properties typedef mitk::PropertyList::PropertyMap PropertyMap; const PropertyMap *refProperties = referencePf->GetPropertyList()->GetMap(); const PropertyMap *testProperties = testPf->GetPropertyList()->GetMap(); MITK_INFO << "List 1:"; for (auto i1 = refProperties->begin(); i1 != refProperties->end(); ++i1) { std::cout << i1->first << std::endl; } MITK_INFO << "List 2:"; for (auto i2 = testProperties->begin(); i2 != testProperties->end(); ++i2) { std::cout << i2->first << std::endl; } MITK_INFO << "-------"; //remark test planar figures may have additional properties //(e.g. reader meta information), but they are not relevant //for the test. Only check of all properties of the reference //are present and correct. - for (const auto prop : *refProperties) + for (const auto& prop : *refProperties) { auto finding = testProperties->find(prop.first); if (finding == testProperties->end()) { return false; } MITK_INFO << "Comparing " << prop.first << "(" << prop.second->GetValueAsString() << ") and " << finding->first << "(" << finding->second->GetValueAsString() << ")"; // Compare property objects contained in the map entries (see mitk::PropertyList) if (!(*(prop.second) == *(finding->second))) return false; } // Test if Geometry is equal const auto *planeGeometry1 = dynamic_cast(referencePf->GetPlaneGeometry()); const auto *planeGeometry2 = dynamic_cast(testPf->GetPlaneGeometry()); // Test Geometry transform parameters typedef mitk::Geometry3D::TransformType TransformType; const TransformType *affineGeometry1 = planeGeometry1->GetIndexToWorldTransform(); const TransformType::ParametersType ¶meters1 = affineGeometry1->GetParameters(); const TransformType::ParametersType ¶meters2 = planeGeometry2->GetIndexToWorldTransform()->GetParameters(); for (unsigned int i = 0; i < affineGeometry1->GetNumberOfParameters(); ++i) { if (fabs(parameters1.GetElement(i) - parameters2.GetElement(i)) >= mitk::eps) { return false; } } // Test Geometry bounds typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType; const BoundsArrayType &bounds1 = planeGeometry1->GetBounds(); const BoundsArrayType &bounds2 = planeGeometry2->GetBounds(); for (unsigned int i = 0; i < 6; ++i) { if (fabs(bounds1.GetElement(i) - bounds2.GetElement(i)) >= mitk::eps) { return false; }; } // Test Geometry spacing and origin mitk::Vector3D spacing1 = planeGeometry1->GetSpacing(); mitk::Vector3D spacing2 = planeGeometry2->GetSpacing(); if ((spacing1 - spacing2).GetNorm() >= mitk::eps) { return false; } mitk::Point3D origin1 = planeGeometry1->GetOrigin(); mitk::Point3D origin2 = planeGeometry2->GetOrigin(); if (origin1.EuclideanDistanceTo(origin2) >= mitk::eps) { return false; } return true; } static PlanarFigureToStreamMap SerializePlanarFiguresToMemoryBuffers(PlanarFigureMap &planarFigures) { PlanarFigureToStreamMap pfMemoryStreams; for (const auto& pf : planarFigures) { mitk::FileWriterRegistry writerRegistry; auto writers = writerRegistry.GetWriters(pf.second.GetPointer(), ""); std::ostringstream stream; writers[0]->SetOutputStream("",&stream); writers[0]->SetInput(pf.second); writers[0]->Write(); pfMemoryStreams.emplace(pf.first, stream.str()); } return pfMemoryStreams; } static PlanarFigureMap DeserializePlanarFiguresFromMemoryBuffers(PlanarFigureToStreamMap pfMemoryStreams) { // Store them in the list and return it PlanarFigureMap planarFigures; mitk::FileReaderRegistry readerRegistry; std::vector readers = readerRegistry.GetReaders(mitk::FileReaderRegistry::GetMimeTypeForFile("pf")); for (const auto& pfStream : pfMemoryStreams) { std::istringstream stream; stream.str(pfStream.second); readers[0]->SetInput("", &stream); auto pfRead = readers[0]->Read(); MITK_TEST_CONDITION(pfRead.size() == 1, "One planar figure should be read from stream."); auto pf = dynamic_cast(pfRead.front().GetPointer()); MITK_TEST_CONDITION(pf != nullptr, "Loaded data should be a planar figure."); planarFigures.emplace(pfStream.first, pf); } return planarFigures; } }; // end test helper class /** \brief Test for PlanarFigure reader and writer classes. * * The test works as follows: * * First, a number of PlanarFigure objects of different types are created and placed with * various control points. These objects are the serialized to file, read again from file, and * the retrieved objects are compared with their control points, properties, and geometry * information to the original PlanarFigure objects. */ int mitkPlanarFigureIOTest(int /* argc */, char * /*argv*/ []) { MITK_TEST_BEGIN("PlanarFigureIO"); // Create a number of PlanarFigure objects PlanarFigureIOTestClass::PlanarFigureMap originalPlanarFigures = PlanarFigureIOTestClass::CreatePlanarFigures(); // Create a number of cloned planar figures to test the Clone function PlanarFigureIOTestClass::PlanarFigureMap clonedPlanarFigures = PlanarFigureIOTestClass::CreateClonedPlanarFigures(originalPlanarFigures); PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, clonedPlanarFigures); std::map pfFileNameMap; for (const auto& pf : originalPlanarFigures) { std::string filename = mitk::IOUtil::CreateTemporaryFile(pf.first+"_XXXXXX.pf", itksys::SystemTools::GetCurrentWorkingDirectory()); mitk::IOUtil::Save(pf.second, filename); pfFileNameMap.emplace(pf.first, filename); } // Write PlanarFigure objects to memory buffers PlanarFigureIOTestClass::PlanarFigureToStreamMap writersStreams = PlanarFigureIOTestClass::SerializePlanarFiguresToMemoryBuffers(originalPlanarFigures); // Read PlanarFigure objects from temp file PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFigures; for (const auto& files : pfFileNameMap) { auto pf = mitk::IOUtil::Load(files.second); retrievedPlanarFigures.emplace(files.first, pf); } // Read PlanarFigure objects from memory buffers PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFiguresFromMemory = PlanarFigureIOTestClass::DeserializePlanarFiguresFromMemoryBuffers(writersStreams); // Test if original and retrieved PlanarFigure objects are the same PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFigures); // Test if original and memory retrieved PlanarFigure objects are the same PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFiguresFromMemory); // empty the originalPlanarFigures originalPlanarFigures.clear(); // Test if cloned and retrieved PlanarFigure objects are the same PlanarFigureIOTestClass::VerifyPlanarFigures(clonedPlanarFigures, retrievedPlanarFigures); MITK_TEST_END() } diff --git a/Plugins/org.blueberry.core.runtime/src/internal/berryInternalPlatform.cpp b/Plugins/org.blueberry.core.runtime/src/internal/berryInternalPlatform.cpp index 1549ae9c6d..639d550046 100644 --- a/Plugins/org.blueberry.core.runtime/src/internal/berryInternalPlatform.cpp +++ b/Plugins/org.blueberry.core.runtime/src/internal/berryInternalPlatform.cpp @@ -1,499 +1,499 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef NOMINMAX #define NOMINMAX #endif #include "berryInternalPlatform.h" #include "berryLog.h" #include "berryLogImpl.h" #include "berryPlatform.h" #include "berryPlatformException.h" #include "berryDebugUtil.h" #include "berryPlatformException.h" #include "berryCTKPluginActivator.h" #include "berryPlatformException.h" #include "berryApplicationContainer.h" #include "berryProduct.h" #include "berryIBranding.h" //#include "event/berryPlatformEvents.h" //#include "berryPlatformLogChannel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace berry { QMutex InternalPlatform::m_Mutex; bool InternalPlatform::DEBUG = false; bool InternalPlatform::DEBUG_PLUGIN_PREFERENCES = false; InternalPlatform::InternalPlatform() : m_Initialized(false) , m_ConsoleLog(false) , m_Context(nullptr) { } InternalPlatform::~InternalPlatform() { } InternalPlatform* InternalPlatform::GetInstance() { QMutexLocker lock(&m_Mutex); static InternalPlatform instance; return &instance; } bool InternalPlatform::ConsoleLog() const { return m_ConsoleLog; } QVariant InternalPlatform::GetOption(const QString& option, const QVariant& defaultValue) const { ctkDebugOptions* options = GetDebugOptions(); if (options != nullptr) { return options->getOption(option, defaultValue); } return QVariant(); } IAdapterManager* InternalPlatform::GetAdapterManager() const { AssertInitialized(); return nullptr; } SmartPointer InternalPlatform::GetProduct() const { if (product.IsNotNull()) return product; ApplicationContainer* container = org_blueberry_core_runtime_Activator::GetContainer(); IBranding* branding = container == nullptr ? nullptr : container->GetBranding(); if (branding == nullptr) return IProduct::Pointer(); IProduct::Pointer brandingProduct = branding->GetProduct(); if (!brandingProduct) { brandingProduct = new Product(branding); } product = brandingProduct; return product; } void InternalPlatform::InitializePluginPaths() { QMutexLocker lock(&m_Mutex); // Add search paths for Qt plugins - for(const auto qtPluginPath : m_Context->getProperty(Platform::PROP_QTPLUGIN_PATH).toStringList()) + for(const auto& qtPluginPath : m_Context->getProperty(Platform::PROP_QTPLUGIN_PATH).toStringList()) { if (qtPluginPath.isEmpty()) continue; if (QFile::exists(qtPluginPath)) { QCoreApplication::addLibraryPath(qtPluginPath); } else if (m_ConsoleLog) { BERRY_WARN << "Qt plugin path does not exist: " << qtPluginPath.toStdString(); } } // Add a default search path. It is assumed that installed applications // provide their Qt plugins in that path. static const QString defaultQtPluginPath = QCoreApplication::applicationDirPath() + "/plugins"; if (QFile::exists(defaultQtPluginPath)) { QCoreApplication::addLibraryPath(defaultQtPluginPath); } if (m_ConsoleLog) { std::string pathList; foreach(QString libPath, QCoreApplication::libraryPaths()) { pathList += (pathList.empty() ? "" : ", ") + libPath.toStdString(); } BERRY_INFO << "Qt library search paths: " << pathList; } /* m_ConfigPath.setPath(m_Context->getProperty("application.configDir").toString()); m_InstancePath.setPath(m_Context->getProperty("application.dir").toString()); QString installPath = m_Context->getProperty(Platform::PROP_HOME).toString(); if (installPath.isEmpty()) { m_InstallPath = m_InstancePath; } else { m_InstallPath.setPath(installPath); } QString dataLocation = m_Context->getProperty(Platform::PROP_STORAGE_DIR).toString(); if (!storageDir.isEmpty()) { if (dataLocation.at(dataLocation.size()-1) != '/') { dataLocation += '/'; } m_UserPath.setPath(dataLocation); } else { // Append a hash value of the absolute path of the executable to the data location. // This allows to start the same application from different build or install trees. dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + this->getOrganizationName() + "/" + this->getApplicationName() + '_'; dataLocation += QString::number(qHash(QCoreApplication::applicationDirPath())) + "/"; m_UserPath.setPath(dataLocation); } BERRY_INFO(m_ConsoleLog) << "Framework storage dir: " << m_UserPath.absolutePath(); QFileInfo userFile(m_UserPath.absolutePath()); if (!QDir().mkpath(userFile.absoluteFilePath()) || !userFile.isWritable()) { QString tmpPath = QDir::temp().absoluteFilePath(QString::fromStdString(this->commandName())); BERRY_WARN << "Storage dir " << userFile.absoluteFilePath() << " is not writable. Falling back to temporary path " << tmpPath; QDir().mkpath(tmpPath); userFile.setFile(tmpPath); } m_BaseStatePath.setPath(m_UserPath.absolutePath() + "/bb-metadata/bb-plugins"); QString logPath(m_UserPath.absoluteFilePath(QString::fromStdString(this->commandName()) + ".log")); m_PlatformLogChannel = new Poco::SimpleFileChannel(logPath.toStdString()); */ } ctkDebugOptions* InternalPlatform::GetDebugOptions() const { return m_DebugTracker.isNull() ? nullptr : m_DebugTracker->getService(); } IApplicationContext* InternalPlatform::GetApplicationContext() const { QList refs; try { refs = m_Context->getServiceReferences("(blueberry.application.type=main.thread)"); } catch (const std::invalid_argument&) { return nullptr; } if (refs.isEmpty()) return nullptr; // assumes the application context is available as a service IApplicationContext* result = m_Context->getService(refs.front()); if (result != nullptr) { m_Context->ungetService(refs.front()); return result; } return nullptr; } void InternalPlatform::Start(ctkPluginContext* context) { this->m_Context = context; m_ConsoleLog = m_Context->getProperty(ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG).toBool(); OpenServiceTrackers(); this->InitializePluginPaths(); #ifdef BLUEBERRY_DEBUG_SMARTPOINTER DebugUtil::RestoreState(m_UserPath); #endif InitializeDebugFlags(); this->m_Initialized = true; } void InternalPlatform::Stop(ctkPluginContext* /*context*/) { AssertInitialized(); this->m_Initialized = false; CloseServiceTrackers(); #ifdef BLUEBERRY_DEBUG_SMARTPOINTER DebugUtil::SaveState(m_UserPath); #endif this->m_Context = nullptr; } void InternalPlatform::AssertInitialized() const { if (!m_Initialized) { throw PlatformException("The Platform has not been initialized yet!"); } } void InternalPlatform::OpenServiceTrackers() { ctkPluginContext* context = this->m_Context; instanceLocation.reset(new ctkServiceTracker(context, ctkLDAPSearchFilter(ctkLocation::INSTANCE_FILTER))); instanceLocation->open(); userLocation.reset(new ctkServiceTracker(context, ctkLDAPSearchFilter(ctkLocation::USER_FILTER))); userLocation->open(); configurationLocation.reset(new ctkServiceTracker(context, ctkLDAPSearchFilter(ctkLocation::CONFIGURATION_FILTER))); configurationLocation->open(); installLocation.reset(new ctkServiceTracker(context, ctkLDAPSearchFilter(ctkLocation::INSTALL_FILTER))); installLocation->open(); m_PreferencesTracker.reset(new ctkServiceTracker(context)); m_PreferencesTracker->open(); m_RegistryTracker.reset(new ctkServiceTracker(context)); m_RegistryTracker->open(); m_DebugTracker.reset(new ctkServiceTracker(context)); m_DebugTracker->open(); } void InternalPlatform::CloseServiceTrackers() { if (!m_PreferencesTracker.isNull()) { m_PreferencesTracker->close(); m_PreferencesTracker.reset(); } if (!m_RegistryTracker.isNull()) { m_RegistryTracker->close(); m_RegistryTracker.reset(); } if (!m_DebugTracker.isNull()) { m_DebugTracker->close(); m_DebugTracker.reset(); } if (!configurationLocation.isNull()) { configurationLocation->close(); configurationLocation.reset(); } if (!installLocation.isNull()) { installLocation->close(); installLocation.reset(); } if (!instanceLocation.isNull()) { instanceLocation->close(); instanceLocation.reset(); } if (!userLocation.isNull()) { userLocation->close(); userLocation.reset(); } } void InternalPlatform::InitializeDebugFlags() { DEBUG = this->GetOption(Platform::PI_RUNTIME + "/debug", false).toBool(); if (DEBUG) { DEBUG_PLUGIN_PREFERENCES = GetOption(Platform::PI_RUNTIME + "/preferences/plugin", false).toBool(); } } IExtensionRegistry* InternalPlatform::GetExtensionRegistry() { return m_RegistryTracker.isNull() ? nullptr : m_RegistryTracker->getService(); } IPreferencesService *InternalPlatform::GetPreferencesService() { return m_PreferencesTracker.isNull() ? nullptr : m_PreferencesTracker->getService(); } ctkLocation* InternalPlatform::GetConfigurationLocation() { this->AssertInitialized(); return configurationLocation->getService(); } ctkLocation* InternalPlatform::GetInstallLocation() { this->AssertInitialized(); return installLocation->getService(); } ctkLocation* InternalPlatform::GetInstanceLocation() { this->AssertInitialized(); return instanceLocation->getService(); } QDir InternalPlatform::GetStateLocation(const QSharedPointer& plugin) { ctkLocation* service = GetInstanceLocation(); if (service == nullptr) { throw ctkIllegalStateException("No instance data can be specified."); } QUrl url = GetInstanceLocation()->getDataArea(plugin->getSymbolicName()); if (!url.isValid()) { throw ctkIllegalStateException("The instance data location has not been specified yet."); } QDir location(url.toLocalFile()); if (!location.exists()) { if (!location.mkpath(location.absolutePath())) { throw PlatformException(QString("Could not create plugin state location \"%1\"").arg(location.absolutePath())); } } return location; } //PlatformEvents& InternalPlatform::GetEvents() //{ // return m_Events; //} ctkLocation* InternalPlatform::GetUserLocation() { this->AssertInitialized(); return userLocation->getService(); } ILog *InternalPlatform::GetLog(const QSharedPointer &plugin) const { LogImpl* result = m_Logs.value(plugin->getPluginId()); if (result != nullptr) return result; // ExtendedLogService logService = (ExtendedLogService) extendedLogTracker.getService(); // Logger logger = logService == null ? null : logService.getLogger(bundle, PlatformLogWriter.EQUINOX_LOGGER_NAME); // result = new Log(bundle, logger); // ExtendedLogReaderService logReader = (ExtendedLogReaderService) logReaderTracker.getService(); // logReader.addLogListener(result, result); // logs.put(bundle, result); // return result; result = new LogImpl(plugin); m_Logs.insert(plugin->getPluginId(), result); return result; } bool InternalPlatform::IsRunning() const { QMutexLocker lock(&m_Mutex); try { return m_Initialized && m_Context && m_Context->getPlugin()->getState() == ctkPlugin::ACTIVE; } catch (const ctkIllegalStateException&) { return false; } } QSharedPointer InternalPlatform::GetPlugin(const QString &symbolicName) { QList > plugins = m_Context->getPlugins(); QSharedPointer res(nullptr); foreach(QSharedPointer plugin, plugins) { if ((plugin->getState() & (ctkPlugin::INSTALLED | ctkPlugin::UNINSTALLED)) == 0 && plugin->getSymbolicName() == symbolicName) { if (res.isNull()) { res = plugin; } else if (res->getVersion().compare(plugin->getVersion()) < 0) { res = plugin; } } } return res; } QList > InternalPlatform::GetPlugins(const QString &symbolicName, const QString &version) { QList > plugins = m_Context->getPlugins(); QMap > selected; ctkVersion versionObj(version); foreach(QSharedPointer plugin, plugins) { if ((plugin->getState() & (ctkPlugin::INSTALLED | ctkPlugin::UNINSTALLED)) == 0 && plugin->getSymbolicName() == symbolicName) { if (plugin->getVersion().compare(versionObj) > -1) { selected.insert(plugin->getVersion(), plugin); } } } QList > sortedPlugins = selected.values(); QList > reversePlugins; qCopyBackward(sortedPlugins.begin(), sortedPlugins.end(), reversePlugins.end()); return reversePlugins; } QStringList InternalPlatform::GetApplicationArgs() const { QStringList result; IApplicationContext* appContext = this->GetApplicationContext(); if (appContext) { QHash args = appContext->GetArguments(); result = args[IApplicationContext::APPLICATION_ARGS].toStringList(); } return result; } } diff --git a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionPreferencePage.cpp b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionPreferencePage.cpp index 62e9815bbd..9ccc92dc20 100644 --- a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionPreferencePage.cpp @@ -1,215 +1,215 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkNodeSelectionPreferencePage.h" #include "QmitkNodeSelectionPreferenceHelper.h" #include #include //----------------------------------------------------------------------------- QmitkNodeSelectionPreferencePage::QmitkNodeSelectionPreferencePage() : m_MainControl(nullptr), m_Controls(nullptr) { } //----------------------------------------------------------------------------- QmitkNodeSelectionPreferencePage::~QmitkNodeSelectionPreferencePage() { delete m_Controls; } //----------------------------------------------------------------------------- void QmitkNodeSelectionPreferencePage::Init(berry::IWorkbench::Pointer ) { } //----------------------------------------------------------------------------- void QmitkNodeSelectionPreferencePage::CreateQtControl(QWidget* parent) { m_MainControl = new QWidget(parent); m_Controls = new Ui::QmitkNodeSelectionPreferencePage; m_Controls->setupUi( m_MainControl ); connect(m_Controls->comboPreferred, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateWidgets())); connect(m_Controls->btnUp, SIGNAL(clicked(bool)), this, SLOT(MoveUp())); connect(m_Controls->btnDown, SIGNAL(clicked(bool)), this, SLOT(MoveDown())); connect(m_Controls->listInspectors, &QListWidget::itemSelectionChanged, this, &QmitkNodeSelectionPreferencePage::UpdateWidgets); this->Update(); } //----------------------------------------------------------------------------- QWidget* QmitkNodeSelectionPreferencePage::GetQtControl() const { return m_MainControl; } //----------------------------------------------------------------------------- bool QmitkNodeSelectionPreferencePage::PerformOk() { //store preferred auto id = m_Controls->comboPreferred->currentData().toString(); mitk::PutPreferredDataStorageInspector(id.toStdString()); //store visible mitk::VisibleDataStorageInspectorMapType visibles; unsigned int visiblePos = 0; for (int i = 0; i < m_Controls->listInspectors->count(); ++i) { auto item = m_Controls->listInspectors->item(i); if (item->checkState() == Qt::Checked) { visibles.insert(std::make_pair(visiblePos++, item->data(Qt::UserRole).toString().toStdString())); } } mitk::PutVisibleDataStorageInspectors(visibles); mitk::PutShowFavoritesInspector(m_Controls->checkShowFav->isChecked()); mitk::PutShowHistoryInspector(m_Controls->checkShowHistory->isChecked()); return true; } //----------------------------------------------------------------------------- void QmitkNodeSelectionPreferencePage::PerformCancel() { } //----------------------------------------------------------------------------- void QmitkNodeSelectionPreferencePage::Update() { m_Providers = mitk::DataStorageInspectorGenerator::GetProviders(); auto visibleProviders = mitk::GetVisibleDataStorageInspectors(); auto allProviders = mitk::DataStorageInspectorGenerator::GetProviders(); auto preferredInspectorID = mitk::GetPreferredDataStorageInspector(); //fill preferred combo int index = 0; int currentIndex = 0; m_Controls->comboPreferred->clear(); for (auto iter : m_Providers) { m_Controls->comboPreferred->addItem(QString::fromStdString(iter.second->GetInspectorDisplayName()), QVariant::fromValue(QString::fromStdString(iter.first))); if (iter.first == preferredInspectorID) { currentIndex = index; }; ++index; } m_Controls->comboPreferred->setCurrentIndex(currentIndex); //fill inspector list m_Controls->listInspectors->clear(); - for (const auto iter : allProviders) + for (const auto& iter : allProviders) { if (iter.first != QmitkDataStorageFavoriteNodesInspector::INSPECTOR_ID() && iter.first != QmitkDataStorageSelectionHistoryInspector::INSPECTOR_ID()) { auto currentID = iter.first; QListWidgetItem* item = new QListWidgetItem; item->setText(QString::fromStdString(iter.second->GetInspectorDisplayName())); item->setData(Qt::UserRole, QVariant::fromValue(QString::fromStdString(currentID))); item->setToolTip(QString::fromStdString(iter.second->GetInspectorDescription())); auto finding = std::find_if(visibleProviders.cbegin(), visibleProviders.cend(), [¤tID](auto v) {return v.second == currentID; }); if (finding == visibleProviders.cend()) { item->setCheckState(Qt::Unchecked); m_Controls->listInspectors->addItem(item); } else { item->setCheckState(Qt::Checked); m_Controls->listInspectors->insertItem(finding->first, item); } } } m_Controls->checkShowFav->setChecked(mitk::GetShowFavoritesInspector()); m_Controls->checkShowHistory->setChecked(mitk::GetShowHistoryInspector()); this->UpdateWidgets(); } void QmitkNodeSelectionPreferencePage::UpdateWidgets() { int currentIndex = m_Controls->listInspectors->currentRow(); m_Controls->btnUp->setEnabled(!m_Controls->listInspectors->selectedItems().empty() && currentIndex > 0); m_Controls->btnDown->setEnabled(!m_Controls->listInspectors->selectedItems().empty() && currentIndex + 1 < m_Controls->listInspectors->count()); for (int i = 0; i < m_Controls->listInspectors->count(); ++i) { auto item = m_Controls->listInspectors->item(i); if (item->data(Qt::UserRole).toString() == m_Controls->comboPreferred->currentData().toString()) { //preferred inspector is always visible. item->setCheckState(Qt::Checked); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); } else { item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable); } } auto isFavSelected = QmitkDataStorageFavoriteNodesInspector::INSPECTOR_ID() == m_Controls->comboPreferred->currentData().toString(); if (isFavSelected) { m_Controls->checkShowFav->setChecked(true); } m_Controls->checkShowFav->setEnabled(!isFavSelected); auto isHistorySelected = QmitkDataStorageSelectionHistoryInspector::INSPECTOR_ID() == m_Controls->comboPreferred->currentData().toString(); if (isHistorySelected) { m_Controls->checkShowHistory->setChecked(true); } m_Controls->checkShowHistory->setEnabled(!isHistorySelected); } void QmitkNodeSelectionPreferencePage::MoveDown() { int currentIndex = m_Controls->listInspectors->currentRow(); if (currentIndex+1 < m_Controls->listInspectors->count()) { QListWidgetItem *currentItem = m_Controls->listInspectors->takeItem(currentIndex); m_Controls->listInspectors->insertItem(currentIndex + 1, currentItem); m_Controls->listInspectors->setCurrentRow(currentIndex + 1); } this->UpdateWidgets(); } void QmitkNodeSelectionPreferencePage::MoveUp() { int currentIndex = m_Controls->listInspectors->currentRow(); if (currentIndex > 0) { QListWidgetItem *currentItem = m_Controls->listInspectors->takeItem(currentIndex); m_Controls->listInspectors->insertItem(currentIndex - 1, currentItem); m_Controls->listInspectors->setCurrentRow(currentIndex - 1); } this->UpdateWidgets(); } diff --git a/Plugins/org.mitk.gui.qt.ext/src/internal/QmitkCommonExtPlugin.cpp b/Plugins/org.mitk.gui.qt.ext/src/internal/QmitkCommonExtPlugin.cpp index e8cce81954..d4307e29a1 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/internal/QmitkCommonExtPlugin.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/internal/QmitkCommonExtPlugin.cpp @@ -1,223 +1,223 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkCommonExtPlugin.h" #include #include "QmitkAboutHandler.h" #include "QmitkAppInstancesPreferencePage.h" #include "QmitkExternalProgramsPreferencePage.h" #include "QmitkInputDevicesPrefPage.h" #include "QmitkModuleView.h" #include #include #include #include #include #include #include #include #include #include #include #include ctkPluginContext* QmitkCommonExtPlugin::_context = nullptr; void QmitkCommonExtPlugin::start(ctkPluginContext* context) { this->_context = context; QtWidgetsExtRegisterClasses(); BERRY_REGISTER_EXTENSION_CLASS(QmitkAboutHandler, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkAppInstancesPreferencePage, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkExternalProgramsPreferencePage, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkInputDevicesPrefPage, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkModuleView, context) if (qApp->metaObject()->indexOfSignal("messageReceived(QByteArray)") > -1) { connect(qApp, SIGNAL(messageReceived(QByteArray)), this, SLOT(handleIPCMessage(QByteArray))); } // This is a potentially long running operation. loadDataFromDisk(berry::Platform::GetApplicationArgs(), true); } void QmitkCommonExtPlugin::stop(ctkPluginContext* context) { Q_UNUSED(context) this->_context = nullptr; } ctkPluginContext* QmitkCommonExtPlugin::getContext() { return _context; } void QmitkCommonExtPlugin::loadDataFromDisk(const QStringList &arguments, bool globalReinit) { if (!arguments.empty()) { ctkServiceReference serviceRef = _context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService* dataStorageService = _context->getService(serviceRef); mitk::DataStorage::Pointer dataStorage = dataStorageService->GetDefaultDataStorage()->GetDataStorage(); int argumentsAdded = 0; for (int i = 0; i < arguments.size(); ++i) { if (arguments[i].right(5) == ".mitk") { mitk::SceneIO::Pointer sceneIO = mitk::SceneIO::New(); bool clearDataStorageFirst(false); mitk::ProgressBar::GetInstance()->AddStepsToDo(2); dataStorage = sceneIO->LoadScene( arguments[i].toLocal8Bit().constData(), dataStorage, clearDataStorageFirst ); mitk::ProgressBar::GetInstance()->Progress(2); argumentsAdded++; } else { try { const std::string path(arguments[i].toStdString()); auto addedNodes = mitk::IOUtil::Load(path, *dataStorage); - for (auto const node : *addedNodes ) + for (const auto& node : *addedNodes ) { node->SetIntProperty("layer", argumentsAdded); } argumentsAdded++; } catch(...) { MITK_WARN << "Failed to load command line argument: " << arguments[i].toStdString(); } } } // end for each command line argument if (argumentsAdded > 0 && globalReinit) { // calculate bounding geometry mitk::RenderingManager::GetInstance()->InitializeViews(dataStorage->ComputeBoundingGeometry3D()); } } else { MITK_ERROR << "A service reference for mitk::IDataStorageService does not exist"; } } } void QmitkCommonExtPlugin::startNewInstance(const QStringList &args, const QStringList& files) { QStringList newArgs(args); #ifdef Q_OS_UNIX newArgs << QString("--") + mitk::BaseApplication::ARG_NEWINSTANCE; #else newArgs << QString("/") + mitk::BaseApplication::ARG_NEWINSTANCE; #endif newArgs << files; QProcess::startDetached(qApp->applicationFilePath(), newArgs); } void QmitkCommonExtPlugin::handleIPCMessage(const QByteArray& msg) { QDataStream ds(msg); QString msgType; ds >> msgType; // we only handle messages containing command line arguments if (msgType != "$cmdLineArgs") return; // activate the current workbench window berry::IWorkbenchWindow::Pointer window = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); mainWindow->setWindowState(mainWindow->windowState() & ~Qt::WindowMinimized); mainWindow->raise(); mainWindow->activateWindow(); // Get the preferences for the instantiation behavior berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer prefs = prefService->GetSystemPreferences()->Node("/General"); bool newInstanceAlways = prefs->GetBool("newInstance.always", false); bool newInstanceScene = prefs->GetBool("newInstance.scene", true); QStringList args; ds >> args; QStringList fileArgs; QStringList sceneArgs; foreach (QString arg, args) { if (arg.endsWith(".mitk")) { sceneArgs << arg; } else { fileArgs << arg; } } if (newInstanceAlways) { if (newInstanceScene) { startNewInstance(args, fileArgs); foreach(QString sceneFile, sceneArgs) { startNewInstance(args, QStringList(sceneFile)); } } else { fileArgs.append(sceneArgs); startNewInstance(args, fileArgs); } } else { loadDataFromDisk(fileArgs, false); if (newInstanceScene) { foreach(QString sceneFile, sceneArgs) { startNewInstance(args, QStringList(sceneFile)); } } else { loadDataFromDisk(sceneArgs, false); } } }