From cd67ade205d426d8b17e68373c30eaec15d17ece Mon Sep 17 00:00:00 2001 From: Florent Collot Date: Fri, 21 Oct 2022 13:34:43 +0200 Subject: [PATCH 01/98] Add PYNCPP external project --- src/CMakeLists.txt | 11 ++++ superbuild/CMakeLists.txt | 8 +++ superbuild/projects_modules/PYNCPP.cmake | 66 ++++++++++++++++++++++ superbuild/projects_modules/medInria.cmake | 1 + 4 files changed, 86 insertions(+) create mode 100644 superbuild/projects_modules/PYNCPP.cmake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1fff689964..bb40c13a5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,11 @@ option(USE_FFmpeg ) endif() +option(USE_Python + "Build with Python support" + ON + ) + ## ############################################################################# ## Find package ## ############################################################################# @@ -80,6 +85,10 @@ find_package(Qt5 REQUIRED COMPONENTS find_package(dtk REQUIRED) include_directories(${dtk_INCLUDE_DIRS}) +if(USE_Python) + find_package(PYNCPP REQUIRED COMPONENTS Qt5) +endif() + ## ############################################################################# ## enable c++ 17 ## ############################################################################# @@ -157,6 +166,8 @@ if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") add_definitions(-DQT_NO_DEBUG) endif() +add_compile_definitions("USE_PYTHON=$") + ## ############################################################################# ## Windows specificity ## ############################################################################# diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index f96f908834..626294e5e7 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -47,6 +47,8 @@ option(USE_DTKIMAGING "Use DTK imaging layer" OFF ) option(USE_OSPRay "Use OSPRay for CPU VR" OFF ) +option(USE_Python "Build with Python support" ON) + if (NOT WIN32) option(USE_FFmpeg "Build with FFmpeg video export support" OFF) if (${USE_FFmpeg}) @@ -71,6 +73,12 @@ if (USE_DTKIMAGING) ) endif() +if (USE_Python) + list(APPEND external_projects + PYNCPP + ) +endif() + list(APPEND external_projects medInria ) diff --git a/superbuild/projects_modules/PYNCPP.cmake b/superbuild/projects_modules/PYNCPP.cmake new file mode 100644 index 0000000000..ca9d2c9a4a --- /dev/null +++ b/superbuild/projects_modules/PYNCPP.cmake @@ -0,0 +1,66 @@ +################################################################################ +# +# medInria +# +# Copyright (c) INRIA 2022. All rights reserved. +# See LICENSE.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even +# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. +# +################################################################################ + +function(PYNCPP_project) + + set(ep PYNCPP) + + EP_Initialisation(${ep} + USE_SYSTEM OFF + BUILD_SHARED_LIBS ON + REQUIRED_FOR_PLUGINS OFF + ) + + if(USE_SYSTEM_${ep}) + find_package(PYNCPP ${version_major}.${version_minor}.${version_patch} + REQUIRED COMPONENTS Qt5 + ) + else() + epComputPath(${ep}) + + set(source_dir ${EP_PATH_SOURCE}/${ep}) + set(binary_dir ${build_path}) + + set(project_args + GIT_REPOSITORY ${GITHUB_PREFIX}LIRYC-IHU/pyncpp.git + GIT_TAG origin/working + GIT_SHALLOW True + GIT_PROGRESS True + ) + + ## ##################################################################### + ## Add external project + ## ##################################################################### + + ExternalProject_Add(${ep} + PREFIX ${EP_PATH_SOURCE} + SOURCE_DIR ${source_dir} + BINARY_DIR ${binary_dir} + TMP_DIR ${tmp_path} + STAMP_DIR ${stamp_path} + DEPENDS ${${ep}_dependencies} + INSTALL_COMMAND "" + "${project_args}" + ) + + ExternalProject_Get_Property(${ep} binary_dir) + + ## ##################################################################### + ## Export variables + ## ##################################################################### + + set(${ep}_DIR ${binary_dir} PARENT_SCOPE) + + endif() + +endfunction() diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index d13fa53d58..9ae5f0f2f7 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -93,6 +93,7 @@ set(cmake_cache_args -DTTK_DIR:PATH=${TTK_DIR} -DVTK_DIR:PATH=${VTK_DIR} -DQt5_DIR:PATH=${Qt5_DIR} + -DPYNCPP_DIR:PATH=${PYNCPP_DIR} -DLogDemons_DIR:PATH=${LogDemons_DIR} -DBoost_INCLUDE_DIR:PATH=${Boost_INCLUDE_DIR} ) From 7a2f79f7cf2ec5296a596a2eab54de720ef9aef5 Mon Sep 17 00:00:00 2001 From: Florent Collot Date: Thu, 1 Dec 2022 16:47:41 +0100 Subject: [PATCH 02/98] [add PYNCPP] Minor changes following review --- superbuild/projects_modules/PYNCPP.cmake | 2 +- superbuild/projects_modules/medInria.cmake | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/superbuild/projects_modules/PYNCPP.cmake b/superbuild/projects_modules/PYNCPP.cmake index ca9d2c9a4a..9ab6744882 100644 --- a/superbuild/projects_modules/PYNCPP.cmake +++ b/superbuild/projects_modules/PYNCPP.cmake @@ -22,7 +22,7 @@ function(PYNCPP_project) ) if(USE_SYSTEM_${ep}) - find_package(PYNCPP ${version_major}.${version_minor}.${version_patch} + find_package(PYNCPP REQUIRED COMPONENTS Qt5 ) else() diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index 9ae5f0f2f7..260531f59b 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -78,6 +78,7 @@ set(cmake_args -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS_${ep}} -DUSE_DTKIMAGING:BOOL=${USE_DTKIMAGING} -DUSE_OSPRay:BOOL=${USE_OSPRay} + -DUSE_Python:BOOL=${USE_Python} -DmedInria_VERSION:STRING=${${PROJECT_NAME}_VERSION} -DBUILD_ALL_PLUGINS=OFF -DBUILD_COMPOSITEDATASET_PLUGIN=OFF @@ -93,7 +94,6 @@ set(cmake_cache_args -DTTK_DIR:PATH=${TTK_DIR} -DVTK_DIR:PATH=${VTK_DIR} -DQt5_DIR:PATH=${Qt5_DIR} - -DPYNCPP_DIR:PATH=${PYNCPP_DIR} -DLogDemons_DIR:PATH=${LogDemons_DIR} -DBoost_INCLUDE_DIR:PATH=${Boost_INCLUDE_DIR} ) @@ -109,6 +109,12 @@ if (USE_DTKIMAGING) -DdtkImaging_DIR:PATH=${dtkImaging_DIR} ) endif() + +if (USE_Python) + list(APPEND cmake_cache_args + -DPYNCPP_DIR:PATH=${PYNCPP_DIR} + ) +endif() ## ############################################################################# ## Add external-project From 2951229b230cbef87b2769f3296714eecb1ffc29 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Fri, 2 Dec 2022 11:35:28 +0100 Subject: [PATCH 03/98] [Four-views] solve crash closing the views --- .../medCoreLegacy/parameters/medViewParameterGroupL.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp index a3f712efa3..2f3871d5ac 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp @@ -47,11 +47,6 @@ medViewParameterGroupL::~medViewParameterGroupL() { medParameterGroupManagerL::instance()->unregisterGroup(this); - for(medAbstractView *view : d->impactedViews) - { - removeImpactedView(view); - } - d->pool->clear(); delete d; From b62f990a456083d91a15a75532a8df89b48a2872 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Tue, 6 Dec 2022 14:56:38 +0100 Subject: [PATCH 04/98] [Zoom] proper zoom displaying data on xy and not xyz --- .../medVtkInria/vtkImageView/vtkImageView.cxx | 50 +++++++++---------- .../medVtkInria/vtkImageView/vtkImageView.h | 2 + 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.cxx b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.cxx index 075197fa65..8648e06fe2 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.cxx +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.cxx @@ -1178,15 +1178,7 @@ void vtkImageView::SetZoom (double arg) vtkCamera *cam = this->GetRenderer() ? this->GetRenderer()->GetActiveCamera() : nullptr; if (cam) { - int* extent = this->GetMedVtkImageInfo()->extent; - double* spacing = this->GetMedVtkImageInfo()->spacing; - double xyz[3] = {0,0,0}; - - for (unsigned int i=0; i<3; i++) - { - xyz[i] = (extent [2*i +1] - extent [2*i]) * spacing[i] / 2.0; - } - double val = std::max (std::max (xyz[0], xyz[1]), xyz[2]); + double val = getImageHalfMaximumSize(); // Just in case no data, avoid setting scale to zero. val = ( val == 0. ) ? 1. : val; @@ -1201,37 +1193,45 @@ void vtkImageView::SetZoom (double arg) //---------------------------------------------------------------------------- double vtkImageView::GetZoom() { + double res = 1.0; if (!this->GetMedVtkImageInfo() || !this->GetMedVtkImageInfo()->initialized) { - return 1.0; + return res; } if (!this->Get2DDisplayMapperInputAlgorithm() || !this->Get2DDisplayMapperInputAlgorithm()->GetOutputInformation(0)) { - return 1.0; + return res; } vtkCamera *cam = this->GetRenderer() ? this->GetRenderer()->GetActiveCamera() : nullptr; if (cam) { - // Ensure that the spacing and dimensions are up-to-date. - int* extent = this->GetMedVtkImageInfo()->extent; - double* spacing = this->GetMedVtkImageInfo()->spacing; - double xyz[3] = {0,0,0}; + res = getImageHalfMaximumSize() / cam->GetParallelScale(); + } - for (unsigned int i=0; i<3; i++) - { - xyz[i] = (extent [2*i +1] - extent [2*i]) * spacing[i] / 2.0; - } - //xyz[i] = dimensions[i] * spacing[i] / 2.0; + return res; +} - double val = std::max (std::max (xyz[0], xyz[1]), xyz[2]); +/** + * @brief Compute the maximum between both x and y size (divided by 2) of the image. + This is going to be used to compute a proper zoom for the camera. + * + * @return double + */ +double vtkImageView::getImageHalfMaximumSize() +{ + // Ensure that the spacing and dimensions are up-to-date. + int* extent = this->GetMedVtkImageInfo()->extent; + double* spacing = this->GetMedVtkImageInfo()->spacing; + double xy[2] = {0,0}; - return (val / cam->GetParallelScale()); - } - else + for (unsigned int i=0; i<2; i++) { - return 1.0; + xy[i] = (extent [2*i +1] - extent [2*i]) * spacing[i] / 2.0; } + //val = dimension * spacing / 2; + + return std::max (xy[0], xy[1]); } //---------------------------------------------------------------------------- diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.h b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.h index b1100ed9c2..9afdba56d4 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.h +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView.h @@ -505,6 +505,8 @@ class MEDVTKINRIA_EXPORT vtkImageView : public vtkObject virtual void GetWithinBoundsPosition (double* pos1, double* dos2); + double getImageHalfMaximumSize(); + protected: /** Takes a vtkScalarsToColors pointer From 7644d308081f8ee42a6da38543ad9173fc2344de Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Wed, 7 Dec 2022 12:26:48 +0100 Subject: [PATCH 05/98] [Tabs] handling of tabs: removal, switch --- .../gui/medTabbedViewContainers.cpp | 44 +++++++++++++++++-- .../gui/medTabbedViewContainers.h | 1 + 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp index 7c0a5fe184..3a943d4963 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp @@ -59,13 +59,12 @@ medTabbedViewContainers::medTabbedViewContainers(medAbstractWorkspaceLegacy* own d->closeShortcut = new QShortcut(this); d->closeShortcut->setKey(Qt::ControlModifier + Qt::Key_W); - // /////////////////////////////////////////////////////////////////////// - // Connect for creation and remove tab + // Connect for creation, removal and move of tabs connect(d->addTabButton, SIGNAL(clicked()), this, SLOT(addContainerInTabUnNamed())); connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); connect(d->closeShortcut, SIGNAL(activated()), this, SLOT(closeCurrentTab())); + connect(tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(movedTabs(int,int))); - // /////////////////////////////////////////////////////////////////////// // Connect group of view handling connect(medViewContainerManager::instance(), SIGNAL(containerAboutToBeDestroyed(QUuid)), this, SLOT(removeContainerFromSelection(QUuid))); connect(this, SIGNAL(containersSelectedChanged()), this, SLOT(buildTemporaryPool())); @@ -257,15 +256,36 @@ void medTabbedViewContainers::closeTab(int index) poMainSpliter->disconnect(this); } poTmp->deleteLater(); - + //Reset tab name if it follows the rule '###' is follows for (int i = 0; i < this->count(); ++i) + { if (this->tabText(i).isEmpty() || this->tabText(i).startsWith(d->owningWorkspace->name(), Qt::CaseSensitive)) + { this->setTabText(i, d->owningWorkspace->name() + " " + QString::number(i + 1)); + } + } // If medTabbedViewContainers is empty and empty is not allowed, we recreate one tab if (d->bKeepLeastOne && (this->count() < 1)) + { this->addContainerInTabUnNamed(); + } + + // Update the remaining tab indexes + d->containerSelectedForTabIndex.remove(index); + QHash > copyHash; + for(auto currentTabIndex : d->containerSelectedForTabIndex.keys()) + { + auto previousTab = d->containerSelectedForTabIndex.take(currentTabIndex); + copyHash.insert(copyHash.count(), previousTab); + } + d->containerSelectedForTabIndex.clear(); + d->containerSelectedForTabIndex = copyHash; + + // Update the current tab to the last one + setCurrentIndex(this->count()-1); + emit containersSelectedChanged(); } void medTabbedViewContainers::tabBarDoubleClickedHandler(int index) @@ -456,3 +476,19 @@ void medTabbedViewContainers::minimizeSplitterContainers(QUuid containerMaximize splitter->show(); } } + +/** + * @brief Launched when a tab from the tabs bar is moved to a new position + * + * @param from index tab before + * @param to index tab after + */ +void medTabbedViewContainers::movedTabs(int from, int to) +{ + auto previousFrom = d->containerSelectedForTabIndex.take(from); + auto previousTo = d->containerSelectedForTabIndex.take(to); + d->containerSelectedForTabIndex.insert(from, previousTo); + d->containerSelectedForTabIndex.insert(to, previousFrom); + + emit containersSelectedChanged(); +} \ No newline at end of file diff --git a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.h b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.h index a620a9e00a..36664ecd37 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.h +++ b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.h @@ -58,6 +58,7 @@ public slots: medViewContainer* insertNewTab(int index, const QString &name); void closeCurrentTab(); void closeTab(int index); + void movedTabs(int from, int to); private slots : void tabBarDoubleClickedHandler(int index); From 0adbb707b02d4d6850bd3b52e5a350f370ad59df Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Wed, 7 Dec 2022 12:50:30 +0100 Subject: [PATCH 06/98] [Tabs] manage the case of removing the last tab before opening new ones --- .../gui/medTabbedViewContainers.cpp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp index 3a943d4963..37384d952f 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp @@ -271,21 +271,23 @@ void medTabbedViewContainers::closeTab(int index) { this->addContainerInTabUnNamed(); } - - // Update the remaining tab indexes - d->containerSelectedForTabIndex.remove(index); - QHash > copyHash; - for(auto currentTabIndex : d->containerSelectedForTabIndex.keys()) + else { - auto previousTab = d->containerSelectedForTabIndex.take(currentTabIndex); - copyHash.insert(copyHash.count(), previousTab); - } - d->containerSelectedForTabIndex.clear(); - d->containerSelectedForTabIndex = copyHash; + // Update the remaining tab indexes + d->containerSelectedForTabIndex.remove(index); + QHash > copyHash; + for(auto currentTabIndex : d->containerSelectedForTabIndex.keys()) + { + auto previousTab = d->containerSelectedForTabIndex.take(currentTabIndex); + copyHash.insert(copyHash.count(), previousTab); + } + d->containerSelectedForTabIndex.clear(); + d->containerSelectedForTabIndex = copyHash; - // Update the current tab to the last one - setCurrentIndex(this->count()-1); - emit containersSelectedChanged(); + // Update the current tab to the last one + setCurrentIndex(this->count()-1); + emit containersSelectedChanged(); + } } void medTabbedViewContainers::tabBarDoubleClickedHandler(int index) From 749d690d1e26d2873125d17e2fe9fdd5c2928067 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Thu, 8 Dec 2022 09:27:01 +0100 Subject: [PATCH 07/98] [N4Bias] manage process cancel if the user wants to quit the app --- src/app/medInria/medMainWindow.cpp | 7 +- .../process/medRunnableProcess.cpp | 6 +- .../medN4BiasCorrection.cpp | 406 +++++++++++++++++- .../medN4BiasCorrection/medN4BiasCorrection.h | 10 +- .../medN4BiasCorrection_p.h | 334 -------------- 5 files changed, 414 insertions(+), 349 deletions(-) delete mode 100644 src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection_p.h diff --git a/src/app/medInria/medMainWindow.cpp b/src/app/medInria/medMainWindow.cpp index 0b445164b9..d2a28f13e3 100644 --- a/src/app/medInria/medMainWindow.cpp +++ b/src/app/medInria/medMainWindow.cpp @@ -873,13 +873,8 @@ void medMainWindow::closeEvent(QCloseEvent *event) // send cancel request to all running jobs, then wait for them // Note: most Jobs don't have the cancel method implemented, so this will be effectively the same as waitfordone. medJobManagerL::instance()->dispatchGlobalCancelEvent(); - QThreadPool::globalInstance()->waitForDone(); - } - else - { - // just hide the window and wait - QThreadPool::globalInstance()->waitForDone(); } + QThreadPool::globalInstance()->waitForDone(); } if(this->saveModifiedAndOrValidateClosing() != QDialog::Accepted) diff --git a/src/layers/legacy/medCoreLegacy/process/medRunnableProcess.cpp b/src/layers/legacy/medCoreLegacy/process/medRunnableProcess.cpp index 64d3a96084..6cfe951254 100644 --- a/src/layers/legacy/medCoreLegacy/process/medRunnableProcess.cpp +++ b/src/layers/legacy/medCoreLegacy/process/medRunnableProcess.cpp @@ -94,10 +94,10 @@ void medRunnableProcess::onProgressed (int value) } /** -* Contrarily to success() and failure(), the cancel() method is called +* Contrary to success() and failure(), the cancel() method is called * from outside this object (success and failure and emitted by the process -* itself. This slot implements the expected behaviour when a cancel request -* was made by calling the approrite onCanceled() slot of the running +* itself). This slot implements the expected behaviour when a cancel request +* was made by calling the appropriate onCanceled() slot of the running * dtkAbstractProcess */ void medRunnableProcess::onCancel (QObject *sender) diff --git a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp index 956ec13a14..e5a854d3bf 100644 --- a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp +++ b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp @@ -11,15 +11,39 @@ =========================================================================*/ +#include "medN4BiasCorrection.h" + #include #include -#include #include -#include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include + +class medN4BiasCorrectionPrivate +{ +public: + dtkSmartPointer input; + dtkSmartPointer mask; + dtkSmartPointer output; + + std::vector numberOfIterations, initialMeshResolution; + double convergenceThreshold, wienerFilterNoise, saveBias; + float bfFWHM; + int bsplineOrder, splineDistance, shrinkFactor, nbHistogramBins; + dtkSmartPointer biasField; + + bool cancelAsked; +}; // ///////////////////////////////////////////////////////////////// // medN4BiasCorrection // ///////////////////////////////////////////////////////////////// @@ -29,6 +53,7 @@ medN4BiasCorrection::medN4BiasCorrection() { d->output = nullptr; d->saveBias = false; + d->cancelAsked = false; } medN4BiasCorrection::~medN4BiasCorrection() @@ -102,7 +127,7 @@ int medN4BiasCorrection::update() if (d->input) { - res = DISPATCH_ON_3D_PIXEL_TYPE(&medN4BiasCorrectionPrivate::update, d, d->input); + res = DISPATCH_ON_3D_PIXEL_TYPE(&medN4BiasCorrection::update, this, d->input); } return res; @@ -113,6 +138,379 @@ medAbstractData * medN4BiasCorrection::output() return d->output; } +/** + * @brief Launch if the process is asked to be cancelled, by medRunnableProcess + for instance. We can't quit a medRunnableProcess naturally, but some + checkpoints can be added in the running process to quit when reached. + * + */ +void medN4BiasCorrection::onCanceled() +{ + d->cancelAsked = true; +} + +template +int medN4BiasCorrection::update(medAbstractData *inputData) +{ + typename InputImageType::Pointer inputImage = static_cast(inputData->data()); + + typedef itk::Image OutputImageType; + typedef itk::CastImageFilter CastType; + + typename CastType::Pointer caster = CastType::New(); + caster->SetInput(inputImage); + typename OutputImageType::Pointer image = caster->GetOutput(); + + d->output = medAbstractDataFactory::instance()->createSmartPointer("itkDataImageFloat3"); + d->biasField = medAbstractDataFactory::instance()->createSmartPointer("itkDataImageFloat3"); + + typedef itk::Image MaskImageType; + typename MaskImageType::Pointer maskImage; + if(d->mask) + { + maskImage = dynamic_cast((itk::Object*) (d->mask->data())); + } + else + { + maskImage = nullptr; + } + typedef itk::N4BiasFieldCorrectionImageFilter CorrecterType; + + typename CorrecterType::Pointer correcter = CorrecterType::New(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + // Handle the mask image + if( maskImage) + { + itk::ImageRegionConstIterator IM( + maskImage, maskImage->GetBufferedRegion() ); + MaskImageType::PixelType maskLabel = 0; + for( IM.GoToBegin(); !IM.IsAtEnd(); ++IM ) + { + if (IM.Get()) + { + maskLabel = IM.Get(); + break; + } + } + if (!maskLabel) + { + return medAbstractProcessLegacy::FAILURE; + } + correcter->SetMaskLabel(maskLabel); + } + else + { + qDebug() << "N4 Bias Correction: mask not read, creating Otsu mask." << endl; + typedef itk::OtsuThresholdImageFilter + ThresholderType; + ThresholderType::Pointer otsu = ThresholderType::New(); + otsu->SetInput(image); + otsu->SetNumberOfHistogramBins(200); + otsu->SetInsideValue(0); + otsu->SetOutsideValue(1); + otsu->Update(); + maskImage = otsu->GetOutput(); + } + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typename OutputImageType::Pointer weightImage = nullptr; + + // Convergence options + d->initialMeshResolution.push_back(1); // BSpline Grid Resolution + d->initialMeshResolution.push_back(1); + d->initialMeshResolution.push_back(1); + + if (d->numberOfIterations.size() > 1 && d->numberOfIterations[0]) + { + typename CorrecterType::VariableSizeArrayType + maximumNumberOfIterations( d->numberOfIterations.size() ); + for( unsigned int i = 0; i < d->numberOfIterations.size(); i++ ) + { + maximumNumberOfIterations[i] = d->numberOfIterations[i]; + } + correcter->SetMaximumNumberOfIterations(maximumNumberOfIterations); + + typename CorrecterType::ArrayType numberOfFittingLevels; + numberOfFittingLevels.Fill(d->numberOfIterations.size()); + correcter->SetNumberOfFittingLevels(numberOfFittingLevels); + } + + if(d->convergenceThreshold) + { + correcter->SetConvergenceThreshold(d->convergenceThreshold); + } + + typename OutputImageType::IndexType imageIndex = image->GetLargestPossibleRegion().GetIndex(); + typename OutputImageType::SizeType imageSize = image->GetLargestPossibleRegion().GetSize(); + typename OutputImageType::PointType newOrigin = image->GetOrigin(); + + // B-spline options -- we place this here to take care of the case where + // the user wants to specify things in terms of the spline distance. + if (d->bsplineOrder) + { + correcter->SetSplineOrder(d->bsplineOrder); + } + + typename CorrecterType::ArrayType numberOfControlPoints; + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + if (d->splineDistance) + { + itk::SizeValueType lowerBound[InputImageType::ImageDimension]; + itk::SizeValueType upperBound[InputImageType::ImageDimension]; + + for( unsigned int i = 0; i < 3; i++ ) + { + float domain = static_cast( + image->GetLargestPossibleRegion().GetSize()[i] - 1 ) + * image->GetSpacing()[i]; + unsigned int numberOfSpans = static_cast( + std::ceil(domain / d->splineDistance)); + unsigned long extraPadding = static_cast( ( numberOfSpans + * d->splineDistance + - domain ) / image->GetSpacing()[i] + 0.5 ); + lowerBound[i] = static_cast( 0.5 * extraPadding ); + upperBound[i] = extraPadding - lowerBound[i]; + newOrigin[i] -= static_cast( lowerBound[i] ) + * image->GetSpacing()[i]; + numberOfControlPoints[i] = numberOfSpans + correcter->GetSplineOrder(); + } + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typedef itk::ConstantPadImageFilter PadderType; + typename PadderType::Pointer padder = PadderType::New(); + padder->SetInput(image); + padder->SetPadLowerBound(lowerBound); + padder->SetPadUpperBound(upperBound); + padder->SetConstant(0); + padder->Update(); + image = padder->GetOutput(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typedef itk::ConstantPadImageFilter MaskPadderType; + typename MaskPadderType::Pointer maskPadder = MaskPadderType::New(); + maskPadder->SetInput(maskImage); + maskPadder->SetPadLowerBound(lowerBound); + maskPadder->SetPadUpperBound(upperBound); + maskPadder->SetConstant(0); + maskPadder->Update(); + maskImage = maskPadder->GetOutput(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + if(weightImage) + { + typename PadderType::Pointer weightPadder = PadderType::New(); + weightPadder->SetInput(weightImage); + weightPadder->SetPadLowerBound(lowerBound); + weightPadder->SetPadUpperBound(upperBound); + weightPadder->SetConstant(0); + weightPadder->Update(); + weightImage = weightPadder->GetOutput(); + } + correcter->SetNumberOfControlPoints(numberOfControlPoints); + } + else if( d->initialMeshResolution.size() == InputImageType::ImageDimension ) + { + for( unsigned int i = 0; i < InputImageType::ImageDimension; i++ ) + { + numberOfControlPoints[i] = static_cast( d->initialMeshResolution[i] ) + + correcter->GetSplineOrder(); + } + correcter->SetNumberOfControlPoints(numberOfControlPoints); + } + + typedef itk::ShrinkImageFilter ShrinkerType; + typename ShrinkerType::Pointer shrinker = ShrinkerType::New(); + shrinker->SetInput(image); + + typedef itk::ShrinkImageFilter MaskShrinkerType; + typename MaskShrinkerType::Pointer maskshrinker = MaskShrinkerType::New(); + maskshrinker->SetInput(maskImage); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + shrinker->SetShrinkFactors(d->shrinkFactor); + maskshrinker->SetShrinkFactors(d->shrinkFactor); + shrinker->Update(); + maskshrinker->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + correcter->SetInput(shrinker->GetOutput()); + correcter->SetMaskImage(maskshrinker->GetOutput()); + + // Histogram sharpening options + + if( d->bfFWHM ) + { + correcter->SetBiasFieldFullWidthAtHalfMaximum(d->bfFWHM); + } + + if( d->wienerFilterNoise ) + { + correcter->SetWienerFilterNoise(d->wienerFilterNoise); + } + + if( d->nbHistogramBins ) + { + correcter->SetNumberOfHistogramBins(d->nbHistogramBins); + } + + try + { + correcter->Update(); + } + catch( itk::ExceptionObject & err ) + { + std::cout << "Exception caught: " << err << std::endl; + return medAbstractProcessLegacy::FAILURE; + } + catch( ... ) + { + qDebug() << "Unknown exception caught." << endl; + return medAbstractProcessLegacy::FAILURE; + } + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + // Output + // Reconstruct the bias field at full image resolution. Divide + // the original input image by the bias field to get the final + // corrected image. + + typedef itk::BSplineControlPointImageFilter< + CorrecterType::BiasFieldControlPointLatticeType, + CorrecterType::ScalarImageType> BSplinerType; + typename BSplinerType::Pointer bspliner = BSplinerType::New(); + bspliner->SetInput(correcter->GetLogBiasFieldControlPointLattice()); + bspliner->SetSplineOrder(correcter->GetSplineOrder()); + bspliner->SetSize(image->GetLargestPossibleRegion().GetSize()); + bspliner->SetOrigin(newOrigin); + bspliner->SetDirection(image->GetDirection()); + bspliner->SetSpacing(image->GetSpacing()); + bspliner->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typename OutputImageType::Pointer logField = OutputImageType::New(); + logField->SetOrigin(image->GetOrigin()); + logField->SetSpacing(image->GetSpacing()); + logField->SetRegions(image->GetLargestPossibleRegion()); + logField->SetDirection(image->GetDirection()); + logField->Allocate(); + + itk::ImageRegionIterator IB( + bspliner->GetOutput(), + bspliner->GetOutput()->GetLargestPossibleRegion() ); + itk::ImageRegionIterator IF( logField, + logField->GetLargestPossibleRegion() ); + for( IB.GoToBegin(), IF.GoToBegin(); !IB.IsAtEnd(); ++IB, ++IF ) + { + IF.Set( IB.Get()[0] ); + } + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typedef itk::ExpImageFilter ExpFilterType; + typename ExpFilterType::Pointer expFilter = ExpFilterType::New(); + expFilter->SetInput(logField); + expFilter->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typedef itk::DivideImageFilter DividerType; + typename DividerType::Pointer divider = DividerType::New(); + divider->SetInput1(image); + divider->SetInput2(expFilter->GetOutput()); + divider->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typename OutputImageType::RegionType inputRegion; + inputRegion.SetIndex(imageIndex); + inputRegion.SetSize(imageSize); + + typedef itk::ExtractImageFilter CropperType; + typename CropperType::Pointer cropper = CropperType::New(); + cropper->SetInput(divider->GetOutput()); + cropper->SetExtractionRegion(inputRegion); + cropper->SetDirectionCollapseToSubmatrix(); + cropper->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + typename CropperType::Pointer biasFieldCropper = CropperType::New(); + biasFieldCropper->SetInput(expFilter->GetOutput()); + biasFieldCropper->SetExtractionRegion(inputRegion); + biasFieldCropper->SetDirectionCollapseToSubmatrix(); + biasFieldCropper->Update(); + + if (d->cancelAsked) + { + return medAbstractProcessLegacy::FAILURE; + } + + d->output->setData(cropper->GetOutput()); + medUtilities::setDerivedMetaData(d->output, d->input, "N4-corrected"); + + if(d->saveBias) + { + d->biasField->setData(biasFieldCropper->GetOutput()); + medUtilities::setDerivedMetaData(d->biasField, d->input, "bias"); + medDataManager::instance()->importData(d->biasField, false); + } + + return medAbstractProcessLegacy::SUCCESS; +} + // ///////////////////////////////////////////////////////////////// // Type instantiation // ///////////////////////////////////////////////////////////////// diff --git a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.h b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.h index ada545fd24..adb899bfde 100644 --- a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.h +++ b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.h @@ -39,10 +39,16 @@ public slots: //! Method to actually start the filter int update(); - + //! The output will be available through here medAbstractData *output(); - + + void onCanceled(); + +protected: + template + int update(medAbstractData *inputData); + private: medN4BiasCorrectionPrivate *d; }; diff --git a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection_p.h b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection_p.h deleted file mode 100644 index d656792c29..0000000000 --- a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection_p.h +++ /dev/null @@ -1,334 +0,0 @@ -#pragma once -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2020. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -class medN4BiasCorrectionPrivate : public dtkAbstractProcess -{ - -public: - dtkSmartPointer input; - dtkSmartPointer mask; - dtkSmartPointer output; - - std::vector numberOfIterations, initialMeshResolution; - double convergenceThreshold, wienerFilterNoise, saveBias; - float bfFWHM; - int bsplineOrder, splineDistance, shrinkFactor, nbHistogramBins; - dtkSmartPointer biasField; - - template - int update(medAbstractData *inputData) - { - typename InputImageType::Pointer inputImage = static_cast(inputData->data()); - - typedef itk::Image OutputImageType; - typedef itk::CastImageFilter CastType; - - typename CastType::Pointer caster = CastType::New(); - caster->SetInput(inputImage); - typename OutputImageType::Pointer image = caster->GetOutput(); - - output = medAbstractDataFactory::instance()->createSmartPointer("itkDataImageFloat3"); - biasField = medAbstractDataFactory::instance()->createSmartPointer("itkDataImageFloat3"); - - typedef itk::Image MaskImageType; - typename MaskImageType::Pointer maskImage; - if(mask) - { - maskImage = dynamic_cast ( ( itk::Object* ) ( mask->data() ) ); - } - else - { - maskImage = nullptr; - } - typedef itk::N4BiasFieldCorrectionImageFilter CorrecterType; - - typename CorrecterType::Pointer correcter = CorrecterType::New(); - - // Handle the mask image - - if( maskImage) - { - itk::ImageRegionConstIterator IM( - maskImage, maskImage->GetBufferedRegion() ); - MaskImageType::PixelType maskLabel = 0; - for( IM.GoToBegin(); !IM.IsAtEnd(); ++IM ) - { - if( IM.Get() ) - { - maskLabel = IM.Get(); - break; - } - } - if( !maskLabel ) - { - return medAbstractProcessLegacy::FAILURE; - } - correcter->SetMaskLabel(maskLabel); - } - else - { - qDebug() << "Mask not read. Creating Otsu mask." << endl; - typedef itk::OtsuThresholdImageFilter - ThresholderType; - ThresholderType::Pointer otsu = ThresholderType::New(); - otsu->SetInput(image); - otsu->SetNumberOfHistogramBins(200); - otsu->SetInsideValue(0); - otsu->SetOutsideValue(1); - otsu->Update(); - - maskImage = otsu->GetOutput(); - } - - typename OutputImageType::Pointer weightImage = nullptr; - - // Convergence options - - initialMeshResolution.push_back(1); // BSpline Grid Resolution - initialMeshResolution.push_back(1); - initialMeshResolution.push_back(1); - - if( numberOfIterations.size() > 1 && numberOfIterations[0] ) - { - typename CorrecterType::VariableSizeArrayType - maximumNumberOfIterations( numberOfIterations.size() ); - for( unsigned int i = 0; i < numberOfIterations.size(); i++ ) - { - maximumNumberOfIterations[i] = numberOfIterations[i]; - } - correcter->SetMaximumNumberOfIterations( maximumNumberOfIterations ); - - typename CorrecterType::ArrayType numberOfFittingLevels; - numberOfFittingLevels.Fill( numberOfIterations.size() ); - correcter->SetNumberOfFittingLevels( numberOfFittingLevels ); - } - - if(convergenceThreshold) - { - correcter->SetConvergenceThreshold(convergenceThreshold); - } - - // B-spline options -- we place this here to take care of the case where - // the user wants to specify things in terms of the spline distance. - - typename OutputImageType::IndexType imageIndex = image->GetLargestPossibleRegion().GetIndex(); - typename OutputImageType::SizeType imageSize = image->GetLargestPossibleRegion().GetSize(); - typename OutputImageType::PointType newOrigin = image->GetOrigin(); - - if( bsplineOrder ) - { - correcter->SetSplineOrder(bsplineOrder); - } - - typename CorrecterType::ArrayType numberOfControlPoints; - - if( splineDistance ) - { - itk::SizeValueType lowerBound[InputImageType::ImageDimension]; - itk::SizeValueType upperBound[InputImageType::ImageDimension]; - - for( unsigned int i = 0; i < 3; i++ ) - { - float domain = static_cast( image-> - GetLargestPossibleRegion().GetSize()[i] - 1 ) * image->GetSpacing()[i]; - unsigned int numberOfSpans = static_cast( std::ceil( domain / splineDistance ) ); - unsigned long extraPadding = static_cast( ( numberOfSpans - * splineDistance - - domain ) / image->GetSpacing()[i] + 0.5 ); - lowerBound[i] = static_cast( 0.5 * extraPadding ); - upperBound[i] = extraPadding - lowerBound[i]; - newOrigin[i] -= ( static_cast( lowerBound[i] ) - * image->GetSpacing()[i] ); - numberOfControlPoints[i] = numberOfSpans + correcter->GetSplineOrder(); - } - - typedef itk::ConstantPadImageFilter PadderType; - typename PadderType::Pointer padder = PadderType::New(); - padder->SetInput(image); - padder->SetPadLowerBound(lowerBound); - padder->SetPadUpperBound(upperBound); - padder->SetConstant(0); - padder->Update(); - image = padder->GetOutput(); - - typedef itk::ConstantPadImageFilter MaskPadderType; - typename MaskPadderType::Pointer maskPadder = MaskPadderType::New(); - maskPadder->SetInput(maskImage); - maskPadder->SetPadLowerBound(lowerBound); - maskPadder->SetPadUpperBound(upperBound); - maskPadder->SetConstant(0); - maskPadder->Update(); - maskImage = maskPadder->GetOutput(); - - if(weightImage) - { - typename PadderType::Pointer weightPadder = PadderType::New(); - weightPadder->SetInput(weightImage); - weightPadder->SetPadLowerBound(lowerBound); - weightPadder->SetPadUpperBound(upperBound); - weightPadder->SetConstant(0); - weightPadder->Update(); - weightImage = weightPadder->GetOutput(); - } - correcter->SetNumberOfControlPoints(numberOfControlPoints); - } - else if( initialMeshResolution.size() == InputImageType::ImageDimension ) - { - for( unsigned int d = 0; d < InputImageType::ImageDimension; d++ ) - { - numberOfControlPoints[d] = static_cast( initialMeshResolution[d] ) - + correcter->GetSplineOrder(); - } - correcter->SetNumberOfControlPoints(numberOfControlPoints); - } - - typedef itk::ShrinkImageFilter ShrinkerType; - typename ShrinkerType::Pointer shrinker = ShrinkerType::New(); - shrinker->SetInput(image); - - typedef itk::ShrinkImageFilter MaskShrinkerType; - typename MaskShrinkerType::Pointer maskshrinker = MaskShrinkerType::New(); - maskshrinker->SetInput(maskImage); - - shrinker->SetShrinkFactors(shrinkFactor); - maskshrinker->SetShrinkFactors(shrinkFactor); - shrinker->Update(); - maskshrinker->Update(); - - correcter->SetInput(shrinker->GetOutput()); - correcter->SetMaskImage(maskshrinker->GetOutput()); - - // Histogram sharpening options - - if( bfFWHM ) - { - correcter->SetBiasFieldFullWidthAtHalfMaximum(bfFWHM); - } - - if( wienerFilterNoise ) - { - correcter->SetWienerFilterNoise(wienerFilterNoise); - } - - if( nbHistogramBins ) - { - correcter->SetNumberOfHistogramBins(nbHistogramBins); - } - - try - { - correcter->Update(); - } - catch( itk::ExceptionObject & err ) - { - std::cout << "Exception caught: " << err << std::endl; - return medAbstractProcessLegacy::FAILURE; - } - catch( ... ) - { - qDebug() << "Unknown exception caught." << endl; - return medAbstractProcessLegacy::FAILURE; - } - - // Output - // Reconstruct the bias field at full image resolution. Divide - // the original input image by the bias field to get the final - // corrected image. - - typedef itk::BSplineControlPointImageFilter< - CorrecterType::BiasFieldControlPointLatticeType, - CorrecterType::ScalarImageType> BSplinerType; - typename BSplinerType::Pointer bspliner = BSplinerType::New(); - bspliner->SetInput(correcter->GetLogBiasFieldControlPointLattice()); - bspliner->SetSplineOrder(correcter->GetSplineOrder()); - bspliner->SetSize(image->GetLargestPossibleRegion().GetSize()); - bspliner->SetOrigin(newOrigin); - bspliner->SetDirection(image->GetDirection()); - bspliner->SetSpacing(image->GetSpacing()); - bspliner->Update(); - - typename OutputImageType::Pointer logField = OutputImageType::New(); - logField->SetOrigin(image->GetOrigin()); - logField->SetSpacing(image->GetSpacing()); - logField->SetRegions(image->GetLargestPossibleRegion()); - logField->SetDirection(image->GetDirection()); - logField->Allocate(); - - itk::ImageRegionIterator IB( - bspliner->GetOutput(), - bspliner->GetOutput()->GetLargestPossibleRegion() ); - itk::ImageRegionIterator IF( logField, - logField->GetLargestPossibleRegion() ); - for( IB.GoToBegin(), IF.GoToBegin(); !IB.IsAtEnd(); ++IB, ++IF ) - { - IF.Set( IB.Get()[0] ); - } - - typedef itk::ExpImageFilter ExpFilterType; - typename ExpFilterType::Pointer expFilter = ExpFilterType::New(); - expFilter->SetInput(logField); - expFilter->Update(); - - typedef itk::DivideImageFilter DividerType; - typename DividerType::Pointer divider = DividerType::New(); - divider->SetInput1(image); - divider->SetInput2(expFilter->GetOutput()); - divider->Update(); - - typename OutputImageType::RegionType inputRegion; - inputRegion.SetIndex(imageIndex); - inputRegion.SetSize(imageSize); - - typedef itk::ExtractImageFilter CropperType; - typename CropperType::Pointer cropper = CropperType::New(); - cropper->SetInput(divider->GetOutput()); - cropper->SetExtractionRegion(inputRegion); - cropper->SetDirectionCollapseToSubmatrix(); - cropper->Update(); - - typename CropperType::Pointer biasFieldCropper = CropperType::New(); - biasFieldCropper->SetInput(expFilter->GetOutput()); - biasFieldCropper->SetExtractionRegion(inputRegion); - biasFieldCropper->SetDirectionCollapseToSubmatrix(); - - biasFieldCropper->Update(); - - output->setData(cropper->GetOutput()); - medUtilities::setDerivedMetaData(output, input, "N4-corrected"); - - if(saveBias) - { - biasField->setData(biasFieldCropper->GetOutput()); - medUtilities::setDerivedMetaData(biasField, input, "bias"); - medDataManager::instance()->importData(biasField, false); - } - - return medAbstractProcessLegacy::SUCCESS; - } -}; From 3d2201ea0999e60424071940acd403ca666092fe Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Thu, 8 Dec 2022 14:43:50 +0100 Subject: [PATCH 08/98] [Reslice] fix zoom error + code clarity --- .../legacy/reformat/medResliceViewer.cpp | 64 ++++---- .../legacy/reformat/medResliceViewer.h | 4 +- .../legacy/reformat/resliceToolBox.cpp | 151 +++++++++--------- 3 files changed, 114 insertions(+), 105 deletions(-) diff --git a/src/plugins/legacy/reformat/medResliceViewer.cpp b/src/plugins/legacy/reformat/medResliceViewer.cpp index 199527c341..5d5fe92c7d 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.cpp +++ b/src/plugins/legacy/reformat/medResliceViewer.cpp @@ -140,7 +140,7 @@ medResliceViewer::medResliceViewer(medAbstractView *view, QWidget *parent): medA selectedView = 2; int *imageDims = view3d->GetMedVtkImageInfo()->dimensions; - outputSpacing = view3d->GetMedVtkImageInfo()->spacing; + outputSpacingOrSize = view3d->GetMedVtkImageInfo()->spacing; viewBody = new QWidget(parent); @@ -382,9 +382,9 @@ void medResliceViewer::saveImage() reslicerTop->SetInterpolationModeToLinear(); // Apply resampling in mm - if (reformaTlbx->findChild("bySpacingOrDimension")->currentText() == "Spacing") + if (reformaTlbx->findChild("bySpacingOrSize")->currentText() == "Spacing") { - reslicerTop->SetOutputSpacing(outputSpacing); + reslicerTop->SetOutputSpacing(outputSpacingOrSize); } reslicerTop->Update(); @@ -426,44 +426,48 @@ void medResliceViewer::saveImage() emit imageReformatedGenerated(); } -void medResliceViewer::thickSlabChanged(double val) +void medResliceViewer::askedSpacingOrSizeChange(double val) { medDoubleParameterL * doubleParam = qobject_cast(QObject::sender()); - auto spinBoxSender = doubleParam? doubleParam->getSpinBox() : nullptr; - if (spinBoxSender) { - double x, y, z; - riw[2]->GetResliceCursor()->GetThickness(x,y,z); + auto changeFormat = reformaTlbx->findChild("bySpacingOrSize")->currentText(); + auto accessibleName = spinBoxSender->accessibleName(); - if (spinBoxSender->accessibleName()=="SpacingX") + if (changeFormat == "Spacing") { - if (reformaTlbx->findChild("bySpacingOrDimension")->currentText() == "Spacing") + double x, y, z; + riw[2]->GetResliceCursor()->GetThickness(x,y,z); + + // The three windows share the same reslice cursor + if (accessibleName == "SpacingOrSizeX") { - riw[0]->GetResliceCursor()->SetThickness(val,y,z); //the three windows share the same reslice cursor + riw[0]->GetResliceCursor()->SetThickness(val,y,z); } - outputSpacing[0] = val; - } - - if (spinBoxSender->accessibleName()=="SpacingY") - { - if (reformaTlbx->findChild("bySpacingOrDimension")->currentText() == "Spacing") + else if (accessibleName == "SpacingOrSizeY") + { + riw[0]->GetResliceCursor()->SetThickness(x,val,z); + } + else if (accessibleName == "SpacingOrSizeZ") { - riw[0]->GetResliceCursor()->SetThickness(x,val,z); //the three windows share the same reslice cursor + riw[0]->GetResliceCursor()->SetThickness(x,y,val); } - outputSpacing[1] = val; } - if (spinBoxSender->accessibleName()=="SpacingZ") + // For spacing or size changes + if (accessibleName == "SpacingOrSizeX") { - if (reformaTlbx->findChild("bySpacingOrDimension")->currentText() == "Spacing") - { - riw[0]->GetResliceCursor()->SetThickness(x,y,val); //the three windows share the same reslice cursor - } - outputSpacing[2] = val; + outputSpacingOrSize[0] = val; + } + else if (accessibleName == "SpacingOrSizeY") + { + outputSpacingOrSize[1] = val; + } + else if (accessibleName == "SpacingOrSizeZ") + { + outputSpacingOrSize[2] = val; } - this->render(); } } @@ -732,7 +736,7 @@ void medResliceViewer::generateOutput(vtkImageReslice* reslicer, QString destTyp outputData->setData(filter->GetOutput()); // Apply resampling in pix - if (reformaTlbx->findChild("bySpacingOrDimension")->currentText() == "Dimension") + if (reformaTlbx->findChild("bySpacingOrSize")->currentText() == "Dimension") { applyResamplingPix(); } @@ -753,9 +757,9 @@ void medResliceViewer::applyResamplingPix() { resampleProcess *resamplePr = new resampleProcess(); resamplePr->setInput(outputData); - resamplePr->setParameter(outputSpacing[0], 0); - resamplePr->setParameter(outputSpacing[1], 1); - resamplePr->setParameter(outputSpacing[2], 2); + resamplePr->setParameter(outputSpacingOrSize[0], 0); + resamplePr->setParameter(outputSpacingOrSize[1], 1); + resamplePr->setParameter(outputSpacingOrSize[2], 2); resamplePr->update(); outputData = resamplePr->output(); diff --git a/src/plugins/legacy/reformat/medResliceViewer.h b/src/plugins/legacy/reformat/medResliceViewer.h index c5b07b09b7..4ad3386fed 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.h +++ b/src/plugins/legacy/reformat/medResliceViewer.h @@ -63,7 +63,7 @@ public slots: virtual void render(); void saveImage(); - void thickSlabChanged(double); + void askedSpacingOrSizeChange(double); void extentChanged(int); bool eventFilter(QObject *object, QEvent *event); @@ -85,7 +85,7 @@ public slots: QWidget *viewBody; QVTKOpenGLWidget *views[4]; dtkSmartPointer inputData; - double *outputSpacing; + double *outputSpacingOrSize; unsigned char selectedView; vtkImageView3D *view3d; vtkSmartPointer vtkViewData; diff --git a/src/plugins/legacy/reformat/resliceToolBox.cpp b/src/plugins/legacy/reformat/resliceToolBox.cpp index bef57fbf54..c1c91324ab 100644 --- a/src/plugins/legacy/reformat/resliceToolBox.cpp +++ b/src/plugins/legacy/reformat/resliceToolBox.cpp @@ -28,9 +28,9 @@ class resliceToolBoxPrivate { public: QPushButton *b_startReslice, *b_stopReslice, *b_saveImage, *b_reset; - QComboBox *bySpacingOrDimension; + QComboBox *bySpacingOrSize; QLabel *helpBegin; - medDoubleParameterL *spacingX, *spacingY, *spacingZ; + medDoubleParameterL *spacingOrSizeX, *spacingOrSizeY, *spacingOrSizeZ; medAbstractLayeredView *currentView; medResliceViewer *resliceViewer; dtkSmartPointer reformatedImage; @@ -54,14 +54,14 @@ resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox // User can choose pixel or millimeter resample QHBoxLayout * resampleLayout = new QHBoxLayout(); - QLabel *bySpacingOrDimensionLabel = new QLabel("Select your resample parameter ", resliceToolBoxBody); - d->bySpacingOrDimension = new QComboBox(resliceToolBoxBody); - d->bySpacingOrDimension->setObjectName("bySpacingOrDimension"); - d->bySpacingOrDimension->addItem("Spacing"); - d->bySpacingOrDimension->addItem("Dimension"); - resampleLayout->addWidget(bySpacingOrDimensionLabel); - resampleLayout->addWidget(d->bySpacingOrDimension); - connect(d->bySpacingOrDimension, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(switchSpacingAndDimension(const QString&))); + QLabel *bySpacingOrSizeLabel = new QLabel("Select your resample parameter ", resliceToolBoxBody); + d->bySpacingOrSize = new QComboBox(resliceToolBoxBody); + d->bySpacingOrSize->setObjectName("bySpacingOrSize"); + d->bySpacingOrSize->addItem("Spacing"); + d->bySpacingOrSize->addItem("Dimension"); + resampleLayout->addWidget(bySpacingOrSizeLabel); + resampleLayout->addWidget(d->bySpacingOrSize); + connect(d->bySpacingOrSize, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(switchSpacingAndDimension(const QString&))); // Spinboxes of resample values QWidget *spinBoxes = new QWidget(resliceToolBoxBody); @@ -69,38 +69,38 @@ resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox QHBoxLayout *spacingSpinBoxLayoutX = new QHBoxLayout(); spacingSpinBoxLayoutX->setAlignment(Qt::AlignCenter); - d->spacingX = new medDoubleParameterL("X", this); - d->spacingX->getSpinBox()->setAccessibleName("SpacingX"); - d->spacingX->setObjectName("SpacingX"); - d->spacingX->setRange(0,100); - d->spacingX->getSpinBox()->setSuffix(" mm"); - d->spacingX->setSingleStep(0.1f); - spacingSpinBoxLayoutX->addWidget(d->spacingX->getLabel()); - spacingSpinBoxLayoutX->addWidget(d->spacingX->getSpinBox()); + d->spacingOrSizeX = new medDoubleParameterL("X", this); + d->spacingOrSizeX->getSpinBox()->setAccessibleName("SpacingOrSizeX"); + d->spacingOrSizeX->setObjectName("SpacingOrSizeX"); + d->spacingOrSizeX->setRange(0,100); + d->spacingOrSizeX->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeX->setSingleStep(0.1f); + spacingSpinBoxLayoutX->addWidget(d->spacingOrSizeX->getLabel()); + spacingSpinBoxLayoutX->addWidget(d->spacingOrSizeX->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutX); QHBoxLayout *spacingSpinBoxLayoutY = new QHBoxLayout(); spacingSpinBoxLayoutY->setAlignment(Qt::AlignCenter); - d->spacingY = new medDoubleParameterL("Y", this); - d->spacingY->getSpinBox()->setAccessibleName("SpacingY"); - d->spacingY->setObjectName("SpacingY"); - d->spacingY->setRange(0,100); - d->spacingY->getSpinBox()->setSuffix(" mm"); - d->spacingY->setSingleStep(0.1f); - spacingSpinBoxLayoutY->addWidget(d->spacingY->getLabel()); - spacingSpinBoxLayoutY->addWidget(d->spacingY->getSpinBox()); + d->spacingOrSizeY = new medDoubleParameterL("Y", this); + d->spacingOrSizeY->getSpinBox()->setAccessibleName("SpacingOrSizeY"); + d->spacingOrSizeY->setObjectName("SpacingOrSizeY"); + d->spacingOrSizeY->setRange(0,100); + d->spacingOrSizeY->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeY->setSingleStep(0.1f); + spacingSpinBoxLayoutY->addWidget(d->spacingOrSizeY->getLabel()); + spacingSpinBoxLayoutY->addWidget(d->spacingOrSizeY->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutY); QHBoxLayout *spacingSpinBoxLayoutZ = new QHBoxLayout(); spacingSpinBoxLayoutZ->setAlignment(Qt::AlignCenter); - d->spacingZ = new medDoubleParameterL("Z", this); - d->spacingZ->getSpinBox()->setAccessibleName("SpacingZ"); - d->spacingZ->setObjectName("SpacingZ"); - d->spacingZ->setRange(0,100); - d->spacingZ->getSpinBox()->setSuffix(" mm"); - d->spacingZ->setSingleStep(0.1f); - spacingSpinBoxLayoutZ->addWidget(d->spacingZ->getLabel()); - spacingSpinBoxLayoutZ->addWidget(d->spacingZ->getSpinBox()); + d->spacingOrSizeZ = new medDoubleParameterL("Z", this); + d->spacingOrSizeZ->getSpinBox()->setAccessibleName("SpacingOrSizeZ"); + d->spacingOrSizeZ->setObjectName("SpacingOrSizeZ"); + d->spacingOrSizeZ->setRange(0,100); + d->spacingOrSizeZ->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeZ->setSingleStep(0.1f); + spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getLabel()); + spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutZ); spinBoxes->setLayout(spacingSpinBoxLayout); @@ -191,17 +191,22 @@ void resliceToolBox::startReformat() getWorkspace()->tabbedViewContainers()); d->resliceViewer->setToolBox(this); getWorkspace()->tabbedViewContainers()->setAcceptDrops(false); - connect(d->resliceViewer,SIGNAL(imageReformatedGenerated()),this,SLOT(saveReformatedImage())); medViewContainer * container = getWorkspace()->tabbedViewContainers()->insertNewTab(0, "Reslice"); getWorkspace()->tabbedViewContainers()->setCurrentIndex(0); container->setDefaultWidget(d->resliceViewer->viewWidget()); - connect(container, SIGNAL(viewRemoved()),this, SLOT(stopReformat()), Qt::UniqueConnection); - connect(d->spacingX, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(thickSlabChanged(double))); - connect(d->spacingY, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(thickSlabChanged(double))); - connect(d->spacingZ, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(thickSlabChanged(double))); - - connect(d->b_saveImage, SIGNAL(clicked()), d->resliceViewer, SLOT(saveImage())); + connect(d->resliceViewer,SIGNAL(imageReformatedGenerated()), + this,SLOT(saveReformatedImage()), Qt::UniqueConnection); + connect(container, SIGNAL(viewRemoved()), + this, SLOT(stopReformat()), Qt::UniqueConnection); + connect(d->spacingOrSizeX, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->spacingOrSizeY, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->spacingOrSizeZ, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->b_saveImage, SIGNAL(clicked()), + d->resliceViewer, SLOT(saveImage()), Qt::UniqueConnection); d->reformatedImage = nullptr; @@ -229,7 +234,7 @@ void resliceToolBox::stopReformat() { if (getWorkspace()) { - d->bySpacingOrDimension->setCurrentIndex(0); // init resample parameter + d->bySpacingOrSize->setCurrentIndex(0); // init resample parameter d->helpBegin->show(); d->reformatOptions->hide(); d->b_startReslice->show(); @@ -282,19 +287,19 @@ void resliceToolBox::updateView() void resliceToolBox::displayInfoOnCurrentView() { - if (d->bySpacingOrDimension->currentIndex() == 0) // Spacing + if (d->bySpacingOrSize->currentIndex() == 0) // Spacing { double *spacing = d->imageInfo->spacing; - d->spacingX->setValue(spacing[0]); - d->spacingY->setValue(spacing[1]); - d->spacingZ->setValue(spacing[2]); + d->spacingOrSizeX->setValue(spacing[0]); + d->spacingOrSizeY->setValue(spacing[1]); + d->spacingOrSizeZ->setValue(spacing[2]); } else // Dimension { int *dimension = d->imageInfo->dimensions; - d->spacingX->setValue(dimension[0]); - d->spacingY->setValue(dimension[1]); - d->spacingZ->setValue(dimension[2]); + d->spacingOrSizeX->setValue(dimension[0]); + d->spacingOrSizeY->setValue(dimension[1]); + d->spacingOrSizeZ->setValue(dimension[2]); } } @@ -319,27 +324,27 @@ void resliceToolBox::switchSpacingAndDimension(const QString & value) { if (value == "Spacing") { - d->spacingX->getSpinBox()->setSuffix(" mm"); - d->spacingX->setRange(0, 100); - d->spacingX->setSingleStep(0.1f); - d->spacingY->getSpinBox()->setSuffix(" mm"); - d->spacingY->setRange(0, 100); - d->spacingY->setSingleStep(0.1f); - d->spacingZ->getSpinBox()->setSuffix(" mm"); - d->spacingZ->setRange(0, 100); - d->spacingZ->setSingleStep(0.1f); + d->spacingOrSizeX->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeX->setRange(0, 100); + d->spacingOrSizeX->setSingleStep(0.1f); + d->spacingOrSizeY->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeY->setRange(0, 100); + d->spacingOrSizeY->setSingleStep(0.1f); + d->spacingOrSizeZ->getSpinBox()->setSuffix(" mm"); + d->spacingOrSizeZ->setRange(0, 100); + d->spacingOrSizeZ->setSingleStep(0.1f); } else // Dimension { - d->spacingX->getSpinBox()->setSuffix(" px"); - d->spacingX->setRange(0, 10000); - d->spacingX->setSingleStep(10); - d->spacingY->getSpinBox()->setSuffix(" px"); - d->spacingY->setRange(0, 10000); - d->spacingY->setSingleStep(10); - d->spacingZ->getSpinBox()->setSuffix(" px"); - d->spacingZ->setRange(0, 10000); - d->spacingZ->setSingleStep(10); + d->spacingOrSizeX->getSpinBox()->setSuffix(" px"); + d->spacingOrSizeX->setRange(0, 10000); + d->spacingOrSizeX->setSingleStep(10); + d->spacingOrSizeY->getSpinBox()->setSuffix(" px"); + d->spacingOrSizeY->setRange(0, 10000); + d->spacingOrSizeY->setSingleStep(10); + d->spacingOrSizeZ->getSpinBox()->setSuffix(" px"); + d->spacingOrSizeZ->setRange(0, 10000); + d->spacingOrSizeZ->setSingleStep(10); } displayInfoOnCurrentView(); } @@ -355,16 +360,16 @@ medAbstractData *resliceToolBox::processOutput() void resliceToolBox::changeButtonValue(QString buttonName, double value) { - if (buttonName == "SpacingX") + if (buttonName == "SpacingOrSizeX") { - d->spacingX->setValue(value); + d->spacingOrSizeX->setValue(value); } - else if (buttonName == "SpacingY") + else if (buttonName == "SpacingOrSizeY") { - d->spacingY->setValue(value); + d->spacingOrSizeY->setValue(value); } - else if (buttonName == "SpacingZ") + else if (buttonName == "SpacingOrSizeZ") { - d->spacingZ->setValue(value); + d->spacingOrSizeZ->setValue(value); } } From 89ce6a87042f8a20ce4076221934cc1ba9763186 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Thu, 8 Dec 2022 14:50:25 +0100 Subject: [PATCH 09/98] [Reslice] dimension -> size --- src/plugins/legacy/reformat/medResliceViewer.cpp | 2 +- src/plugins/legacy/reformat/resliceToolBox.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/legacy/reformat/medResliceViewer.cpp b/src/plugins/legacy/reformat/medResliceViewer.cpp index 5d5fe92c7d..7cedc4ca4f 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.cpp +++ b/src/plugins/legacy/reformat/medResliceViewer.cpp @@ -736,7 +736,7 @@ void medResliceViewer::generateOutput(vtkImageReslice* reslicer, QString destTyp outputData->setData(filter->GetOutput()); // Apply resampling in pix - if (reformaTlbx->findChild("bySpacingOrSize")->currentText() == "Dimension") + if (reformaTlbx->findChild("bySpacingOrSize")->currentText() == "Size") { applyResamplingPix(); } diff --git a/src/plugins/legacy/reformat/resliceToolBox.cpp b/src/plugins/legacy/reformat/resliceToolBox.cpp index c1c91324ab..b938033118 100644 --- a/src/plugins/legacy/reformat/resliceToolBox.cpp +++ b/src/plugins/legacy/reformat/resliceToolBox.cpp @@ -58,7 +58,7 @@ resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox d->bySpacingOrSize = new QComboBox(resliceToolBoxBody); d->bySpacingOrSize->setObjectName("bySpacingOrSize"); d->bySpacingOrSize->addItem("Spacing"); - d->bySpacingOrSize->addItem("Dimension"); + d->bySpacingOrSize->addItem("Size"); resampleLayout->addWidget(bySpacingOrSizeLabel); resampleLayout->addWidget(d->bySpacingOrSize); connect(d->bySpacingOrSize, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(switchSpacingAndDimension(const QString&))); From a1e9a5225dd8ad304ea93ba3e229d9dd73847b12 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Fri, 9 Dec 2022 09:46:04 +0100 Subject: [PATCH 10/98] [Paint] create mask before trying to paint + hide a button --- .../medAlgorithmPaintToolBox.cpp | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index ba0911a7d0..dcafee990e 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -564,6 +564,41 @@ void AlgorithmPaintToolBox::activateStroke() addBrushSize_shortcut->setEnabled(true); reduceBrushSize_shortcut->setEnabled(true); + setCurrentView(currentView); + + if (!m_imageData) + { + this->setData(currentView->layerData(0)); + } + if (!m_imageData) + { + qWarning() << "Could not set data"; + return; + } + + if(!currentView->contains(m_maskAnnotationData)) + { + m_maskAnnotationData->setMetaData("SeriesDescription", "mask"); + currentView->addLayer(m_maskAnnotationData); + for(medAbstractInteractor* interactor : currentView->layerInteractors(getWorkspace()->getSelectedLayerIndices()[0])) + { + if (interactor->identifier() == "medAnnotationInteractor") + { + for(medAbstractParameterL* parameter : interactor->linkableParameters()) + { + if (parameter->name() == "Opacity") + { + qobject_cast(parameter)->setValue(0.4); + } + } + } + } + + // Update Mouse Interaction ToolBox + currentView->setCurrentLayer(0); + getWorkspace()->updateMouseInteractionToolBox(); + } + activateCustomedCursor(); // Add circular cursor for painting } @@ -1250,7 +1285,6 @@ AlgorithmPaintToolBox::GenerateMinMaxValuesFromImage () void AlgorithmPaintToolBox::updateStroke(ClickAndMoveEventFilter * filter, medAbstractImageView * view) { - setCurrentView(view); if ( !isMask2dOnSlice() ) { return; @@ -1258,16 +1292,6 @@ void AlgorithmPaintToolBox::updateStroke(ClickAndMoveEventFilter * filter, medAb const double radius = m_brushSizeSlider->value(); // in image units. - if ( !m_imageData ) - { - this->setData(view->layerData(0)); - } - if (!m_imageData) - { - qWarning() << "Could not set data"; - return; - } - QVector3D newPoint = filter->points().back(); typedef MaskType::DirectionType::InternalMatrixType::element_type ElemType; @@ -1372,29 +1396,6 @@ void AlgorithmPaintToolBox::updateStroke(ClickAndMoveEventFilter * filter, medAb m_itkMask->GetPixelContainer()->Modified(); m_itkMask->SetPipelineMTime(m_itkMask->GetMTime()); - if(!view->contains(m_maskAnnotationData)) - { - m_maskAnnotationData->setMetaData("SeriesDescription", "mask"); - view->addLayer(m_maskAnnotationData); - for(medAbstractInteractor* interactor : view->layerInteractors(getWorkspace()->getSelectedLayerIndices()[0])) - { - if (interactor->identifier() == "medAnnotationInteractor") - { - for(medAbstractParameterL* parameter : interactor->linkableParameters()) - { - if (parameter->name() == "Opacity") - { - qobject_cast(parameter)->setValue(0.4); - } - } - } - } - - // Update Mouse Interaction ToolBox - view->setCurrentLayer(0); - getWorkspace()->updateMouseInteractionToolBox(); - } - if ( slicingParameter ) { slicingParameter->getSlider()->addTick(currentIdSlice); @@ -1482,6 +1483,7 @@ void AlgorithmPaintToolBox::updateButtons() m_labelColorWidget->hide(); m_strokeLabelSpinBox->hide(); m_colorLabel->hide(); + m_removeSeedButton->hide(); return; } else @@ -1498,6 +1500,7 @@ void AlgorithmPaintToolBox::updateButtons() m_wand3DRealTime->show(); m_wandInfo->show(); m_brushSizeSlider->hide(); + m_removeSeedButton->show(); } else if ( m_paintState == PaintState::Stroke ) { @@ -1506,6 +1509,7 @@ void AlgorithmPaintToolBox::updateButtons() m_wandUpperThresholdSlider->hide(); m_wand3DCheckbox->hide(); m_wand3DRealTime->hide(); + m_removeSeedButton->hide(); } } } From 37817055ec4adf96df2fde13eb2be57787ee1f49 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Mon, 12 Dec 2022 15:37:18 +0100 Subject: [PATCH 11/98] [3.3.1] release notes & version number with four view crash-fix --- CMakeLists.txt | 2 +- RELEASE_NOTES.txt | 1 + src/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d197356c5..58713e5d04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ if(POLICY CMP0020) endif() if(NOT DEFINED ${MEDINRIA_SUPERBUILD_VERSION}) - set(MEDINRIA_SUPERBUILD_VERSION 3.3.0) + set(MEDINRIA_SUPERBUILD_VERSION 3.3.1) endif() SET(CMAKE_CXX_STANDARD 17) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index ca43807ba5..2baa9fa6e9 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -8,6 +8,7 @@ medInria 3.3: - Reslice, solve crashs after removing a data in view - PolygonROI, solve graphical/ergonomy pb and crashes (split, clear) - Variational Segmentation: enhance GUI, solve a spacing error on output masks +- BUGFIX 3.3.1: [Four-views] solve crash closing the views medInria 3.2: - Add some tooltips in view navigator diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb40c13a5f..d630057dd4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.3) if(NOT DEFINED ${medInria_VERSION}) - set(medInria_VERSION 3.3.0) + set(medInria_VERSION 3.3.1) endif() project(medInria VERSION ${medInria_VERSION}) From 35f58d5fbe238a9ff807d7f82136c114767934d9 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Fri, 16 Dec 2022 14:44:18 +0100 Subject: [PATCH 12/98] [Git diff] solve differences between musicardio to medInria3.4 --- .../legacy/medCoreLegacy/CMakeLists.txt | 1 + .../database/medDatabaseExporter.cpp | 2 - .../database/medDatabaseImporter.cpp | 2 + .../medDatabaseNonPersistentImporter.cpp | 7 --- .../medDatabaseNonPersistentImporter.h | 2 +- .../readers/medAbstractDataReader.cpp | 43 +++++++++++++++++++ .../readers/medAbstractDataReader.h | 42 ++++++++++++++++++ .../medImageIO/itkDataImageWriterBase.cpp | 20 ++++++--- .../vtkMetaDataSetSequence.h | 2 +- .../vtkImageView/vtkImageView2D.cxx | 2 + .../medVtkInria/vtkImageView/vtkImageView2D.h | 2 + .../writers/itkDicomDataImageWriter.cpp | 15 ++++--- .../itkINRDataImageReader/CMakeLists.txt | 2 +- .../itkINRDataImageWriter/CMakeLists.txt | 2 +- .../medCompositeDataSets/CMakeLists.txt | 2 +- .../legacy/meshMapping/meshMappingToolBox.h | 2 +- .../undoRedoRegistration/CMakeLists.txt | 2 +- .../writers/vtkDataMeshWriterBase.h | 2 +- superbuild/projects_modules/QtDCM.cmake | 2 +- superbuild/projects_modules/TTK.cmake | 2 +- 20 files changed, 126 insertions(+), 30 deletions(-) create mode 100644 src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.cpp create mode 100644 src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.h diff --git a/src/layers/legacy/medCoreLegacy/CMakeLists.txt b/src/layers/legacy/medCoreLegacy/CMakeLists.txt index 5fc10cbda3..4da7fb1f21 100644 --- a/src/layers/legacy/medCoreLegacy/CMakeLists.txt +++ b/src/layers/legacy/medCoreLegacy/CMakeLists.txt @@ -41,6 +41,7 @@ list_source_files(${TARGET_NAME} views/interactors views/navigators parameters + readers resources ) diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp index 6e7acdabef..2a284ed69f 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp @@ -11,8 +11,6 @@ =========================================================================*/ -#include - #include #include #include diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp index 8b31a00a74..5c6411afbd 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp @@ -166,7 +166,9 @@ int medDatabaseImporter::getOrCreateStudy ( const medAbstractData* medData, QSql QString studyDate = medMetaDataKeys::StudyDate.getFirstValue(medData); if( studyName=="EmptyStudy" && seriesName=="EmptySeries" ) + { return studyDbId; + } query.prepare ( "SELECT id FROM study WHERE patient = :patient AND name = :studyName AND uid = :studyUid" ); query.bindValue ( ":patient", patientDbId ); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp index 472ae5b7da..c865bf727d 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp @@ -38,13 +38,6 @@ medDatabaseNonPersistentImporter::medDatabaseNonPersistentImporter (medAbstractD qDebug() << "medDatabaseNonPersistentImporter created with uuid:" << this->callerUuid(); } -//----------------------------------------------------------------------------------------------------------- - -medDatabaseNonPersistentImporter::~medDatabaseNonPersistentImporter () -{ - -} - //----------------------------------------------------------------------------------------------------------- /** * Retrieves patientID. Checks if patient is already in the database diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.h b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.h index 077ed96612..3984ff2046 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.h @@ -39,7 +39,7 @@ class MEDCORELEGACY_EXPORT medDatabaseNonPersistentImporter : public medAbstract */ medDatabaseNonPersistentImporter(const QString& file, const QUuid &uuid); medDatabaseNonPersistentImporter(medAbstractData* medData, const QUuid& uuid); - ~medDatabaseNonPersistentImporter(); + ~medDatabaseNonPersistentImporter() override = default; public: QString getPatientID(QString patientName, QString birthDate); diff --git a/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.cpp b/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.cpp new file mode 100644 index 0000000000..646382990d --- /dev/null +++ b/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.cpp @@ -0,0 +1,43 @@ +/*========================================================================= + + medInria + + Copyright (c) INRIA 2013. All rights reserved. + + See LICENSE.txt for details in the root of the sources or: + https://github.com/medInria/medInria-public/blob/master/LICENSE.txt + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. + +=========================================================================*/ + +#include "medAbstractDataReader.h" + +#include +#include + +medAbstractDataReader::medAbstractDataReader() + : dtkAbstractDataReader() +{ +} + +medAbstractDataReader::medAbstractDataReader(const medAbstractDataReader& other) + : dtkAbstractDataReader(other) +{ +} + +medAbstractDataReader::~medAbstractDataReader() +{ +} + +int medAbstractDataReader::getNumberOfData() const +{ + return m_data.size(); +} + +QVector medAbstractDataReader::getData() const +{ + return m_data; +} diff --git a/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.h b/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.h new file mode 100644 index 0000000000..470ef00efa --- /dev/null +++ b/src/layers/legacy/medCoreLegacy/readers/medAbstractDataReader.h @@ -0,0 +1,42 @@ +#pragma once +/*========================================================================= + + medInria + + Copyright (c) INRIA 2013. All rights reserved. + + See LICENSE.txt for details in the root of the sources or: + https://github.com/medInria/medInria-public/blob/master/LICENSE.txt + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. + +=========================================================================*/ + +#include + +#include +#include + +class medAbstractData; +class medAbstractDataReaderPrivate; + +class MEDCORELEGACY_EXPORT medAbstractDataReader : public dtkAbstractDataReader +{ + Q_OBJECT + +public: + medAbstractDataReader(void); + medAbstractDataReader(const medAbstractDataReader& other); + virtual ~medAbstractDataReader(void); + + int getNumberOfData() const; + QVector getData() const; + +signals: + void finishedReadingData(int dataNumber); + +protected: + QVector m_data; +}; diff --git a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp index 73bf742cba..a890d0bad0 100644 --- a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp +++ b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp @@ -49,19 +49,29 @@ bool itkDataImageWriterBase::canWrite(const QString& path) } template -bool itkDataImageWriterBase::write_image(const QString& path,const char* type) { +bool itkDataImageWriterBase::write_image(const QString& path,const char* type){ medAbstractData* medData = dynamic_cast(this->data()); - if (medData && medData->identifier()!=type) + if (medData && medData->identifier() != type) + { return false; + } typedef itk::Image Image; typename Image::Pointer image = dynamic_cast((itk::Object*)(this->data()->output())); if (image.IsNull()) + { return false; - if (medData->hasMetaData(medAbstractImageData::PixelMeaningMetaData)) { - itk::MetaDataDictionary& dict = image->GetMetaDataDictionary(); - itk::EncapsulateMetaData(dict,"intent_name",medData->metadata(medAbstractImageData::PixelMeaningMetaData)); } + + itk::MetaDataDictionary& dict = image->GetMetaDataDictionary(); + if (medData->hasMetaData(medAbstractImageData::PixelMeaningMetaData)) + { + itk::EncapsulateMetaData(dict, "intent_name", medData->metadata(medAbstractImageData::PixelMeaningMetaData)); + } + + // save relevant metaDataKeys + encapsulateSharedMetaData(dict); + typename itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter ::New(); writer->SetImageIO (this->io); writer->UseCompressionOn(); diff --git a/src/layers/legacy/medVtkDataMeshBase/vtkMetaDataSetSequence.h b/src/layers/legacy/medVtkDataMeshBase/vtkMetaDataSetSequence.h index ca166d9b66..651932dca7 100644 --- a/src/layers/legacy/medVtkDataMeshBase/vtkMetaDataSetSequence.h +++ b/src/layers/legacy/medVtkDataMeshBase/vtkMetaDataSetSequence.h @@ -123,7 +123,7 @@ class MEDVTKDATAMESHBASE_EXPORT vtkMetaDataSetSequence: public vtkMetaDataSet */ vtkGetMacro (SequenceDuration, double) - virtual const char* GetDataSetType() const override + const char* GetDataSetType() const override { return "Sequence"; } diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx index 582d94879f..cf558f0fe0 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx @@ -356,6 +356,8 @@ void vtkImageView2D::SetSlice(int slice) } this->SetCurrentPoint (pos); + + qtSignalHandler->emitSliceChanged(slice, this->SliceOrientation); } //---------------------------------------------------------------------------- diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.h b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.h index 3b2e32b0c8..079b88c597 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.h +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.h @@ -96,9 +96,11 @@ class MEDVTKINRIA_EXPORT vtkImageView2DQtSignals : public QObject ~vtkImageView2DQtSignals() {} void emitInterpolate(bool pi_bInterpolation, int pi_iLayer){emit interpolate(pi_bInterpolation, pi_iLayer);} + void emitSliceChanged(int slice, int sliceOrientation){emit sliceChanged(slice, sliceOrientation);} signals: void interpolate(bool, int); + void sliceChanged(int, int); private: diff --git a/src/plugins/legacy/itkDataImage/writers/itkDicomDataImageWriter.cpp b/src/plugins/legacy/itkDataImage/writers/itkDicomDataImageWriter.cpp index a943ec5072..cd25001013 100644 --- a/src/plugins/legacy/itkDataImage/writers/itkDicomDataImageWriter.cpp +++ b/src/plugins/legacy/itkDataImage/writers/itkDicomDataImageWriter.cpp @@ -336,7 +336,6 @@ void itkDicomDataImageWriter::fillDictionaryWithModalityDependentData(itk::MetaD { itk::EncapsulateMetaData(dictionary, "0018|0060", data()->metadata(medMetaDataKeys::KVP.key()).toStdString()); } - } template void itkDicomDataImageWriter::fillDictionaryWithSharedData(itk::MetaDataDictionary &dictionary, bool studyUIDExistance, @@ -474,11 +473,15 @@ template bool itkDicomDataImageWriter::fillDictionaryAndWriteD extract->Update(); itk::ImageRegionIterator it( extract->GetOutput(), extract->GetOutput()->GetLargestPossibleRegion() ); - typename Image2DType::PixelType minValue = itk::NumericTraits::max(); - typename Image2DType::PixelType maxValue = itk::NumericTraits::min(); + // Do not use the Image2DType::PixelType type, as it creates problems with + // char and uchar images. Furthermore, the Window center and width tags + // are coded as 'Decimal String' in the DICOM dictionary, so floating + // point value are fine. + double minValue = std::numeric_limits::max(); + double maxValue = std::numeric_limits::lowest(); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) { - typename Image2DType::PixelType p = it.Get(); + double p = static_cast(it.Get()); if( p > maxValue ) { maxValue = p; @@ -488,8 +491,8 @@ template bool itkDicomDataImageWriter::fillDictionaryAndWriteD minValue = p; } } - typename Image2DType::PixelType windowCenter = (minValue + maxValue) / 2; - typename Image2DType::PixelType windowWidth = (maxValue - minValue); + double windowCenter = (minValue + maxValue) / 2; + double windowWidth = std::max(1., maxValue - minValue); value.str(""); value << windowCenter; diff --git a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt index 37e65481cd..646a84f3e7 100644 --- a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.15) set(TARGET_NAME itkINRDataImageReaderPlugin) diff --git a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt index 34a1a2cef8..01b1fc9a24 100644 --- a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.15) set(TARGET_NAME itkINRDataImageWriterPlugin) diff --git a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt index f88c0f79de..f9c9c9f2e3 100644 --- a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt +++ b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt @@ -11,7 +11,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.15) project(medCompositeDataSetsPlugin) diff --git a/src/plugins/legacy/meshMapping/meshMappingToolBox.h b/src/plugins/legacy/meshMapping/meshMappingToolBox.h index 22fbf4ac18..313ec7c132 100644 --- a/src/plugins/legacy/meshMapping/meshMappingToolBox.h +++ b/src/plugins/legacy/meshMapping/meshMappingToolBox.h @@ -24,7 +24,7 @@ class MESHMAPPINGPLUGIN_EXPORT meshMappingToolBox : public medAbstractSelectable { Q_OBJECT MED_TOOLBOX_INTERFACE("Mesh Mapping", - "Map data on a mesh: keep the intersection of the mesh and data, and set these values to the mesh.", + "Map data on a mesh: keep the intersection of the mesh and data, and set these values to the mesh", <<"Meshing") public: diff --git a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt index 22b8c1d7a8..4a974bee4c 100644 --- a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt +++ b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.15) set(TARGET_NAME undoRedoRegistrationPlugin) diff --git a/src/plugins/legacy/vtkDataMesh/writers/vtkDataMeshWriterBase.h b/src/plugins/legacy/vtkDataMesh/writers/vtkDataMeshWriterBase.h index 22817d8f11..8b16f7fb6f 100644 --- a/src/plugins/legacy/vtkDataMesh/writers/vtkDataMeshWriterBase.h +++ b/src/plugins/legacy/vtkDataMesh/writers/vtkDataMeshWriterBase.h @@ -28,7 +28,7 @@ class VTKDATAMESHPLUGIN_EXPORT vtkDataMeshWriterBase : public dtkAbstractDataWri ~vtkDataMeshWriterBase() override = default; public slots: - bool canWrite (const QString& path); + bool canWrite (const QString& path) override; void addMetaDataAsFieldData(vtkMetaDataSet* dataSet); void clearMetaDataFieldData(vtkMetaDataSet* dataSet); diff --git a/superbuild/projects_modules/QtDCM.cmake b/superbuild/projects_modules/QtDCM.cmake index 99b712d99d..fe83616e35 100644 --- a/superbuild/projects_modules/QtDCM.cmake +++ b/superbuild/projects_modules/QtDCM.cmake @@ -116,4 +116,4 @@ set(${ep}_DIR ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep -endfunction() \ No newline at end of file +endfunction() diff --git a/superbuild/projects_modules/TTK.cmake b/superbuild/projects_modules/TTK.cmake index e333482d72..0c58930e30 100644 --- a/superbuild/projects_modules/TTK.cmake +++ b/superbuild/projects_modules/TTK.cmake @@ -107,4 +107,4 @@ set(${ep}_DIR ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep -endfunction() \ No newline at end of file +endfunction() From bdb554b59e35c3cb7a7cd92ca4ec2da031ceec68 Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Mon, 19 Dec 2022 14:51:13 +0100 Subject: [PATCH 13/98] [DCM] do not corrupt exported/imported dcm with odd axis --- src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index 9096bdf07e..474e49ce0c 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -670,7 +670,6 @@ void DCMTKImageIO::DetermineOrientation() } } - double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) { // Getting the unit vector of the closest axis @@ -679,15 +678,6 @@ double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) closestAxis[0] = round(m_Direction[2][0]); closestAxis[1] = round(m_Direction[2][1]); closestAxis[2] = round(m_Direction[2][2]); - if (fabs(closestAxis[0] + closestAxis[1] + closestAxis[2]) != 1) - { - itkExceptionMacro ( - << "Ambiguous slice stack direction: " - <GetMetaDataValueString("(0020,0032)", index); std::istringstream is_stream( s_position.c_str() ); From 5af77d842d295ae2b7cbabbf87790dbe45190415 Mon Sep 17 00:00:00 2001 From: fcollot Date: Mon, 19 Dec 2022 13:36:24 +0000 Subject: [PATCH 14/98] set minimum cmake version to 19 --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d197356c5..47376c4852 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.19) if(POLICY CMP0020) cmake_policy(SET CMP0020 NEW) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb40c13a5f..8fbab7c126 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.19) if(NOT DEFINED ${medInria_VERSION}) set(medInria_VERSION 3.3.0) From 07dbd8e3d94eeaff49269ea8b249f2f806b1089c Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Mon, 2 Jan 2023 15:22:53 +0100 Subject: [PATCH 15/98] [Registration] selector toolbox empty connect --- .../toolboxes/medRegistrationSelectorToolBox.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp index f7a675e053..6ade817b41 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp @@ -122,15 +122,17 @@ medAbstractData *medRegistrationSelectorToolBox::movingData(void) void medRegistrationSelectorToolBox::changeCurrentToolBox(int index) { medSelectorToolBox::changeCurrentToolBox(index); - - connect (currentToolBox(), SIGNAL (success()),this,SLOT(enableSelectorToolBox())); - connect (currentToolBox(), SIGNAL (failure()),this,SLOT(enableSelectorToolBox())); - d->nameOfCurrentAlgorithm = this->comboBox()->itemData(index).toString(); - if (!d->undoRedoProcess && !d->undoRedoToolBox) + if (currentToolBox()) { - connect(currentToolBox(), SIGNAL(success()), this, SLOT(handleOutput())); + connect (currentToolBox(), SIGNAL (success()),this,SLOT(enableSelectorToolBox())); + connect (currentToolBox(), SIGNAL (failure()),this,SLOT(enableSelectorToolBox())); + + if (!d->undoRedoProcess && !d->undoRedoToolBox) + { + connect(currentToolBox(), SIGNAL(success()), this, SLOT(handleOutput())); + } } } From 5e7dd2a0c016c1864ab0183f4854cdae42009dde Mon Sep 17 00:00:00 2001 From: MERLE Mathilde <“mathilde.merle@ihu-liryc.fr”> Date: Wed, 4 Jan 2023 12:12:12 +0100 Subject: [PATCH 16/98] [DCMTKImageIO] redo GetPositionOnStackingAxisForImage to get closest axis --- .../legacy/medImageIO/itkDCMTKImageIO.cpp | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index 474e49ce0c..af7ab0d0fb 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -672,44 +672,16 @@ void DCMTKImageIO::DetermineOrientation() double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) { - // Getting the unit vector of the closest axis - // so we know which part of the image position to use - vnl_vector closestAxis (3); - closestAxis[0] = round(m_Direction[2][0]); - closestAxis[1] = round(m_Direction[2][1]); - closestAxis[2] = round(m_Direction[2][2]); - - std::string s_position = this->GetMetaDataValueString("(0020,0032)", index); - std::istringstream is_stream( s_position.c_str() ); - - double pos = 0.0; - bool foundAxis=false; - - for (int i=0; i<3; i++) + // Get maximum absolute value, which is the closest to an axis + auto result = std::max_element(m_Direction[2].begin(), m_Direction[2].end(), [](int a, int b) { - if (!(is_stream >> pos) ) - { - itkWarningMacro ( << "Cannot convert string to double: " << s_position.c_str() << std::endl ); - } - else - { - if (fabs(closestAxis[i]) == 1) - { - foundAxis = true; - break; - } - } - } + return std::abs(a) < std::abs(b); + }); - if (!foundAxis) - { - itkWarningMacro ( <<"Could not identify position on stacking axis, returning zero." << std::endl ); - } - - return pos; + // Index of the value in the vector + return std::distance(m_Direction[2].begin(), result); } - double DCMTKImageIO::GetSliceLocation(std::string imagePosition) { // <> We should not trust DICOM tag for sliceLocation (type3 so not mandatory and may be wrong ) From 0b629443863993038169e353837d306cdd6ca95b Mon Sep 17 00:00:00 2001 From: MathouNH Date: Tue, 17 Jan 2023 10:19:16 +0100 Subject: [PATCH 17/98] [DCM] get the index value from metadata string --- src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp | 15 ++++++++++++++- src/layers/legacy/medImageIO/itkDCMTKImageIO.h | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index af7ab0d0fb..49d7f5b8e2 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -679,7 +679,20 @@ double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) }); // Index of the value in the vector - return std::distance(m_Direction[2].begin(), result); + auto principalAxisIndex = std::distance(m_Direction[2].begin(), result); + + return GetPositionFromPrincipalAxisIndex(index, principalAxisIndex); +} + +double DCMTKImageIO::GetPositionFromPrincipalAxisIndex(int index, int principalAxisIndex) +{ + std::string s_position = this->GetMetaDataValueString("(0020,0032)", index); + + // Convert string metadata to vector of double + std::stringstream lineStream(s_position); + std::vector positionVector(std::istream_iterator(lineStream), {}); + + return positionVector[principalAxisIndex]; } double DCMTKImageIO::GetSliceLocation(std::string imagePosition) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.h b/src/layers/legacy/medImageIO/itkDCMTKImageIO.h index f4fe63a445..f8940a2797 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.h +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.h @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -188,7 +189,8 @@ class MEDIMAGEIO_EXPORT DCMTKImageIO : public MultiThreadedImageIOBase void DetermineOrigin(); void DetermineOrientation(); - double GetPositionOnStackingAxisForImage (int); + double GetPositionOnStackingAxisForImage(int); + double GetPositionFromPrincipalAxisIndex(int, int); double GetSliceLocation(std::string); void ReadHeader( const std::string& name, const int& fileIndex, const int& fileCount ); From f2b6351ad2ac2416472a69c461b3018a7f01d5a5 Mon Sep 17 00:00:00 2001 From: MathouNH Date: Tue, 17 Jan 2023 14:25:51 +0100 Subject: [PATCH 18/98] [DCMTKImageIO] debug if max abs value is not first --- src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index 49d7f5b8e2..b34d4ac430 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -673,7 +673,7 @@ void DCMTKImageIO::DetermineOrientation() double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) { // Get maximum absolute value, which is the closest to an axis - auto result = std::max_element(m_Direction[2].begin(), m_Direction[2].end(), [](int a, int b) + auto result = std::max_element(m_Direction[2].begin(), m_Direction[2].end(), [](double a, double b) { return std::abs(a) < std::abs(b); }); From e7d953b14b2f9eb36c19dbae8925254677fe03f4 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 8 Feb 2023 09:11:57 +0100 Subject: [PATCH 19/98] [CMake] minimum version at 3.19 --- src/app/medPluginGenerator/template/cmake | 2 +- src/app/medPluginGenerator/template/filtering/cmake | 2 +- src/app/medPluginGenerator/template/registration/cmake | 2 +- src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt | 2 +- src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt | 2 +- src/plugins/legacy/medCompositeDataSets/CMakeLists.txt | 2 +- src/plugins/legacy/undoRedoRegistration/CMakeLists.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/medPluginGenerator/template/cmake b/src/app/medPluginGenerator/template/cmake index 994a5c9b1b..729226421b 100644 --- a/src/app/medPluginGenerator/template/cmake +++ b/src/app/medPluginGenerator/template/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.19) project(%1Plugin) diff --git a/src/app/medPluginGenerator/template/filtering/cmake b/src/app/medPluginGenerator/template/filtering/cmake index c4348ba37f..6e0d18124c 100644 --- a/src/app/medPluginGenerator/template/filtering/cmake +++ b/src/app/medPluginGenerator/template/filtering/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.19) project(%1Plugin) diff --git a/src/app/medPluginGenerator/template/registration/cmake b/src/app/medPluginGenerator/template/registration/cmake index b53684ccad..57dd91dd63 100644 --- a/src/app/medPluginGenerator/template/registration/cmake +++ b/src/app/medPluginGenerator/template/registration/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.19) project(%1Plugin) diff --git a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt index 646a84f3e7..b7d4e564f4 100644 --- a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.19) set(TARGET_NAME itkINRDataImageReaderPlugin) diff --git a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt index 01b1fc9a24..b1963172bf 100644 --- a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.19) set(TARGET_NAME itkINRDataImageWriterPlugin) diff --git a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt index f9c9c9f2e3..ceb94d99fd 100644 --- a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt +++ b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt @@ -11,7 +11,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.19) project(medCompositeDataSetsPlugin) diff --git a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt index 4a974bee4c..16cc82d55c 100644 --- a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt +++ b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.19) set(TARGET_NAME undoRedoRegistrationPlugin) From 504ffe4a1746fa37b940de86f637d928245bf76d Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 8 Feb 2023 09:24:49 +0100 Subject: [PATCH 20/98] [CMake] minimum inside src set with variable --- src/app/medPluginGenerator/template/cmake | 2 +- src/app/medPluginGenerator/template/filtering/cmake | 2 +- src/app/medPluginGenerator/template/registration/cmake | 2 +- src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt | 2 +- src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt | 2 +- src/plugins/legacy/medCompositeDataSets/CMakeLists.txt | 2 +- src/plugins/legacy/undoRedoRegistration/CMakeLists.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/medPluginGenerator/template/cmake b/src/app/medPluginGenerator/template/cmake index 729226421b..6e28dfa702 100644 --- a/src/app/medPluginGenerator/template/cmake +++ b/src/app/medPluginGenerator/template/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) project(%1Plugin) diff --git a/src/app/medPluginGenerator/template/filtering/cmake b/src/app/medPluginGenerator/template/filtering/cmake index 6e0d18124c..942bd4da2b 100644 --- a/src/app/medPluginGenerator/template/filtering/cmake +++ b/src/app/medPluginGenerator/template/filtering/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) project(%1Plugin) diff --git a/src/app/medPluginGenerator/template/registration/cmake b/src/app/medPluginGenerator/template/registration/cmake index 57dd91dd63..14a437e361 100644 --- a/src/app/medPluginGenerator/template/registration/cmake +++ b/src/app/medPluginGenerator/template/registration/cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) project(%1Plugin) diff --git a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt index b7d4e564f4..0af43bcacd 100644 --- a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) set(TARGET_NAME itkINRDataImageReaderPlugin) diff --git a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt index b1963172bf..0f0f797e65 100644 --- a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) set(TARGET_NAME itkINRDataImageWriterPlugin) diff --git a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt index ceb94d99fd..1a48b2b308 100644 --- a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt +++ b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt @@ -11,7 +11,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) project(medCompositeDataSetsPlugin) diff --git a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt index 16cc82d55c..3632a3f454 100644 --- a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt +++ b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt @@ -13,7 +13,7 @@ # ################################################################################ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) set(TARGET_NAME undoRedoRegistrationPlugin) From 032fd1e6c6bb1e9912c63252c698dd70d3db0c80 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 8 Feb 2023 10:38:15 +0100 Subject: [PATCH 21/98] [CMake] remove unnecessary cmake mimum required fct --- src/app/medPluginGenerator/template/cmake | 2 -- src/app/medPluginGenerator/template/filtering/cmake | 2 -- src/app/medPluginGenerator/template/registration/cmake | 2 -- src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt | 2 -- src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt | 2 -- src/plugins/legacy/medCompositeDataSets/CMakeLists.txt | 2 -- src/plugins/legacy/undoRedoRegistration/CMakeLists.txt | 2 -- 7 files changed, 14 deletions(-) diff --git a/src/app/medPluginGenerator/template/cmake b/src/app/medPluginGenerator/template/cmake index 6e28dfa702..5c27906546 100644 --- a/src/app/medPluginGenerator/template/cmake +++ b/src/app/medPluginGenerator/template/cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - project(%1Plugin) ## ############################################################################# diff --git a/src/app/medPluginGenerator/template/filtering/cmake b/src/app/medPluginGenerator/template/filtering/cmake index 942bd4da2b..5db81bb535 100644 --- a/src/app/medPluginGenerator/template/filtering/cmake +++ b/src/app/medPluginGenerator/template/filtering/cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - project(%1Plugin) ## ############################################################################# diff --git a/src/app/medPluginGenerator/template/registration/cmake b/src/app/medPluginGenerator/template/registration/cmake index 14a437e361..8b547e4746 100644 --- a/src/app/medPluginGenerator/template/registration/cmake +++ b/src/app/medPluginGenerator/template/registration/cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - project(%1Plugin) ## ################################################################# diff --git a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt index 0af43bcacd..cd36af3c78 100644 --- a/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageReader/CMakeLists.txt @@ -13,8 +13,6 @@ # ################################################################################ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - set(TARGET_NAME itkINRDataImageReaderPlugin) ## ################################################################# diff --git a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt index 0f0f797e65..a73420df7a 100644 --- a/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt +++ b/src/plugins/legacy/itkINRDataImageWriter/CMakeLists.txt @@ -13,8 +13,6 @@ # ################################################################################ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - set(TARGET_NAME itkINRDataImageWriterPlugin) ## ################################################################# diff --git a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt index 1a48b2b308..50e950063e 100644 --- a/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt +++ b/src/plugins/legacy/medCompositeDataSets/CMakeLists.txt @@ -11,8 +11,6 @@ # ################################################################################ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - project(medCompositeDataSetsPlugin) ## ################################################################# diff --git a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt index 3632a3f454..50696b7d08 100644 --- a/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt +++ b/src/plugins/legacy/undoRedoRegistration/CMakeLists.txt @@ -13,8 +13,6 @@ # ################################################################################ -cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) - set(TARGET_NAME undoRedoRegistrationPlugin) ## ############################################################################# From 04d669ff6504a50393b8145ed7253ab1fddb92b8 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 8 Feb 2023 11:49:19 +0100 Subject: [PATCH 22/98] [med3.4] Release notes and version number --- CMakeLists.txt | 2 +- RELEASE_NOTES.txt | 8 ++++++++ src/CMakeLists.txt | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67f8682b2c..1beaac2202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ if(POLICY CMP0020) endif() if(NOT DEFINED ${MEDINRIA_SUPERBUILD_VERSION}) - set(MEDINRIA_SUPERBUILD_VERSION 3.3.1) + set(MEDINRIA_SUPERBUILD_VERSION 3.4.0) endif() SET(CMAKE_CXX_STANDARD 17) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 2baa9fa6e9..afc97468a5 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,3 +1,11 @@ +medInria 3.4: +- Fix an incorrect zoom for some data displayed in a view. The zoom is now calculated with x and y dims, and not z +- Fix errors with view tabs if the user switch 2 of them, or remove some of them +- N4 Bias Correction process can stop now if the user wants to quit the application, it will stop at some algorithm checkpoints +- Reslice toolbox: changing the dimension of spacing parameters does not zoom one of the views +- Paint toolbox: the first click on the view quickly displays the drawing, instead of waiting for the mask to be created +- Fix: avoid corruption of exported/imported dcm with odd axis + medInria 3.3: - Fix mirrored DICOM images problem - Fix a bug in Paint toolbox with Retina screens diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad50327347..846de4ff94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.19) if(NOT DEFINED ${medInria_VERSION}) - set(medInria_VERSION 3.3.1) + set(medInria_VERSION 3.4.0) endif() project(medInria VERSION ${medInria_VERSION}) From d04eb16f966d70ab243e31193f7f02a0d0f84da2 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 8 Feb 2023 16:09:41 +0100 Subject: [PATCH 23/98] [release notes] add bug fix 3.3.2 --- RELEASE_NOTES.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index afc97468a5..dae16b6f61 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,10 +1,8 @@ medInria 3.4: - Fix an incorrect zoom for some data displayed in a view. The zoom is now calculated with x and y dims, and not z -- Fix errors with view tabs if the user switch 2 of them, or remove some of them - N4 Bias Correction process can stop now if the user wants to quit the application, it will stop at some algorithm checkpoints - Reslice toolbox: changing the dimension of spacing parameters does not zoom one of the views - Paint toolbox: the first click on the view quickly displays the drawing, instead of waiting for the mask to be created -- Fix: avoid corruption of exported/imported dcm with odd axis medInria 3.3: - Fix mirrored DICOM images problem @@ -17,6 +15,9 @@ medInria 3.3: - PolygonROI, solve graphical/ergonomy pb and crashes (split, clear) - Variational Segmentation: enhance GUI, solve a spacing error on output masks - BUGFIX 3.3.1: [Four-views] solve crash closing the views +- BUGFIX 3.3.2: + * Avoid corruption of exported/imported dcm with odd axis + * Fix errors with view tabs if the user switch 2 of them, or remove some of them medInria 3.2: - Add some tooltips in view navigator From f184f5aa59e8d235dc13f0ec06bd5282c1e2a145 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 30 Mar 2023 15:20:21 +0200 Subject: [PATCH 24/98] [Github] ssh key change --- superbuild/external_projects_tools/EP_GithubSshTest.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superbuild/external_projects_tools/EP_GithubSshTest.cmake b/superbuild/external_projects_tools/EP_GithubSshTest.cmake index eac7b8753a..898dd4186e 100644 --- a/superbuild/external_projects_tools/EP_GithubSshTest.cmake +++ b/superbuild/external_projects_tools/EP_GithubSshTest.cmake @@ -15,8 +15,7 @@ ## Add github.com's SSH signature to the .ssh/known_hosts file ## ############################################################################# -set(GITHUB_SIGN "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" - ) +set(GITHUB_SIGN "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=") file(TO_CMAKE_PATH ${HOME_PATH} HOME_PATH) set(SSH_KNOWN_HOSTS_PATH ${HOME_PATH}/.ssh/known_hosts) From aa064f6f3775f491dab4296a9590f9788f296818 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Mon, 17 Apr 2023 09:00:30 +0200 Subject: [PATCH 25/98] [MemoryLeak] homepage widgets --- .../areas/homepage/medHomepageArea.cpp | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/app/medInria/areas/homepage/medHomepageArea.cpp b/src/app/medInria/areas/homepage/medHomepageArea.cpp index 6c4f3810a1..fa9d7403d2 100644 --- a/src/app/medInria/areas/homepage/medHomepageArea.cpp +++ b/src/app/medInria/areas/homepage/medHomepageArea.cpp @@ -413,10 +413,42 @@ void medHomepageArea::initPage() QGridLayout* workspaceButtonsLayout = new QGridLayout; std::vector oLayoutVect; - if (workspaceButtonsLayoutBasic->count() > 2) oLayoutVect.push_back(workspaceButtonsLayoutBasic); - if (workspaceButtonsLayoutMethodology->count() > 2) oLayoutVect.push_back(workspaceButtonsLayoutMethodology); - if (workspaceButtonsLayoutClinical->count() > 2) oLayoutVect.push_back(workspaceButtonsLayoutClinical); - if (workspaceButtonsLayoutOther->count() > 2) oLayoutVect.push_back(workspaceButtonsLayoutOther); + if (workspaceButtonsLayoutBasic->count() > 2) + { + oLayoutVect.push_back(workspaceButtonsLayoutBasic); + } + else + { + delete workspaceLabelBasic; + delete workspaceButtonsLayoutBasic; + } + if (workspaceButtonsLayoutMethodology->count() > 2) + { + oLayoutVect.push_back(workspaceButtonsLayoutMethodology); + } + else + { + delete workspaceLabelMethodology; + delete workspaceButtonsLayoutMethodology; + } + if (workspaceButtonsLayoutClinical->count() > 2) + { + oLayoutVect.push_back(workspaceButtonsLayoutClinical); + } + else + { + delete workspaceLabelClinical; + delete workspaceButtonsLayoutClinical; + } + if (workspaceButtonsLayoutOther->count() > 2) + { + oLayoutVect.push_back(workspaceButtonsLayoutOther); + } + else + { + delete workspaceLabelOther; + delete workspaceButtonsLayoutOther; + } for (int i = 0; i < static_cast(oLayoutVect.size()); ++i) { From 26f96e0868460d033aee48d9908d2e9410b1aa74 Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 10 May 2023 09:55:04 +0200 Subject: [PATCH 26/98] Python packaging and initialization (#1123) * adapt to PYNCPP changes * switch to lowercase for pyncpp * Fix handling of install path of embedded Python * more Python fixes * Minor correction to find OpenSSL automatically on macOS --- packaging/apple/mac_packager.sh.in | 8 +++- packaging/windows/WindowsPackaging.cmake | 5 ++ src/CMakeLists.txt | 4 +- src/app/medInria/CMakeLists.txt | 32 +++++++++++++ src/app/medInria/main.cpp | 30 ++++++++++++ superbuild/CMakeLists.txt | 12 ++++- superbuild/projects_modules/medInria.cmake | 12 +++-- .../{PYNCPP.cmake => pyncpp.cmake} | 47 ++++++++++++------- 8 files changed, 125 insertions(+), 25 deletions(-) rename superbuild/projects_modules/{PYNCPP.cmake => pyncpp.cmake} (57%) diff --git a/packaging/apple/mac_packager.sh.in b/packaging/apple/mac_packager.sh.in index e20694cc09..693802ac19 100755 --- a/packaging/apple/mac_packager.sh.in +++ b/packaging/apple/mac_packager.sh.in @@ -15,8 +15,10 @@ cd @medInria_BINARY_DIR@/bin \rm -fr TmpInstall -mkdir TmpInstall -cp -r medInria.app TmpInstall +mkdir -p TmpInstall/medInria.app/Contents/Resources +cp -r medInria.app/Contents/MacOS TmpInstall/medInria.app/Contents +cp medInria.app/Contents/Info.plist TmpInstall/medInria.app/Contents +cp medInria.app/Contents/Resources/medInria.icns TmpInstall/medInria.app/Contents/Resources cd TmpInstall @@ -31,6 +33,8 @@ done @dtk_DIR@/bin/dtkDeploy medInria.app $injectDirs &>/dev/null +@CMAKE_COMMAND@ -DCMAKE_INSTALL_PREFIX:STRING=medInria.app/Contents/Resources -DCMAKE_INSTALL_COMPONENT:STRING=Python -P @pyncpp_DIR@/cmake_install.cmake + #Run fancy packaging apple script \cp -f @medInria_SOURCE_DIR@/utils/osx_packaging/BaseMedinriaPackage.sparseimage.gz @PROJECT_BINARY_DIR@/MedinriaPackage.sparseimage.gz diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index 0836d4ce03..deccaac3a8 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -105,6 +105,11 @@ list(APPEND ${RPI_DIR}/bin/Release ) +set(CPACK_INSTALL_CMAKE_PROJECTS + ${pyncpp_DIR} pyncpp Python "/" + ${CPACK_INSTALL_CMAKE_PROJECTS} + ) + install(CODE " file(GLOB_RECURSE itk_files LIST_DIRECTORIES true \"${ITK_DIR}/bin/*.dll\") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 846de4ff94..a53c3f5639 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,7 +86,7 @@ find_package(dtk REQUIRED) include_directories(${dtk_INCLUDE_DIRS}) if(USE_Python) - find_package(PYNCPP REQUIRED COMPONENTS Qt5) + find_package(pyncpp REQUIRED COMPONENTS CPP_API Qt5) endif() ## ############################################################################# @@ -166,7 +166,7 @@ if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") add_definitions(-DQT_NO_DEBUG) endif() -add_compile_definitions("USE_PYTHON=$") +add_compile_definitions(USE_PYTHON) ## ############################################################################# ## Windows specificity diff --git a/src/app/medInria/CMakeLists.txt b/src/app/medInria/CMakeLists.txt index 870b8ab68a..42f80ade62 100644 --- a/src/app/medInria/CMakeLists.txt +++ b/src/app/medInria/CMakeLists.txt @@ -147,6 +147,38 @@ target_link_libraries(${TARGET_NAME} medPacs ) +## ############################################################################# +## Python +## ############################################################################# + +if(USE_Python) + target_link_libraries(${TARGET_NAME} ${pyncpp_CPP_API_LIBRARY}) + + if(WIN32) + set(python_home ".") + else() + if(APPLE) + set(python_home "../Resources/${pyncpp_PYTHON_INSTALL_DESTINATION}") + else() + set(python_home "../${pyncpp_PYTHON_INSTALL_DESTINATION}") + endif() + + get_filename_component(python_home_base "${python_home}" DIRECTORY) + get_filename_component(python_dir "${python_home}" NAME) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "$/${python_home_base}" + ) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink "${pyncpp_PYTHON_DIR}" "${python_dir}" + WORKING_DIRECTORY "$/${python_home_base}" + ) + endif() + + target_compile_definitions(${TARGET_NAME} PUBLIC PYTHON_HOME="${python_home}") +endif() + ## ############################################################################# ## install ## ############################################################################# diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index fb6ffb6fda..6bd391f283 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -19,6 +19,10 @@ #include #endif +#if(USE_PYTHON) +#include +#endif + #include #include #include @@ -184,6 +188,24 @@ int main(int argc,char* argv[]) medDataManager::instance()->setDatabaseLocation(); +#if(USE_PYTHON) + pyncpp::Manager pythonManager; + QDir pythonHome = qApp->applicationDirPath(); + QString pythonErrorMessage; + + if (!pythonHome.cd(PYTHON_HOME)) + { + pythonErrorMessage = "The embedded Python could not be found "; + } + else + { + if(!pythonManager.initialize(qUtf8Printable(pythonHome.absolutePath()))) + { + pythonErrorMessage = "Initialization of the embedded Python failed."; + } + } +#endif + medPluginManager::instance()->setVerboseLoading(true); medPluginManager::instance()->initialize(); @@ -234,6 +256,14 @@ int main(int argc,char* argv[]) if (show_splash) splash.finish(mainwindow); +#if(USE_PYTHON) + if(!pythonErrorMessage.isEmpty()) + { + QMessageBox::warning(mainwindow, "Python", pythonErrorMessage); + qWarning() << pythonErrorMessage; + } +#endif + if (medPluginManager::instance()->plugins().isEmpty()) { QMessageBox::warning(mainwindow, QObject::tr("No plugin loaded"), diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index 626294e5e7..fac4976c35 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -30,6 +30,16 @@ find_package(Qt5 REQUIRED COMPONENTS find_package(Boost REQUIRED) +if(USE_Python AND UNIX) + if(APPLE) + set(default_root_dir "/usr/local/opt/openssl@1.1") + else() + set(default_root_dir) + endif() + set(OPENSSL_ROOT_DIR ${default_root_dir} CACHE PATH "Root directory of OpenSSL.") + find_package(OpenSSL COMPONENTS SSL) +endif() + ## ############################################################################# ## Add exteral projects ## ############################################################################# @@ -75,7 +85,7 @@ endif() if (USE_Python) list(APPEND external_projects - PYNCPP + pyncpp ) endif() diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index 260531f59b..261938aae2 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -28,6 +28,7 @@ list(APPEND ${ep}_dependencies QtDCM RPI LogDemons + pyncpp ) if (USE_DTKIMAGING) @@ -112,7 +113,7 @@ endif() if (USE_Python) list(APPEND cmake_cache_args - -DPYNCPP_DIR:PATH=${PYNCPP_DIR} + -Dpyncpp_DIR:PATH=${pyncpp_DIR} ) endif() @@ -152,6 +153,7 @@ if (WIN32) file(TO_NATIVE_PATH ${QtDCM_DIR} DCM_BIN_BASE) file(TO_NATIVE_PATH ${_qt5Core_install_prefix} QT5_BIN_BASE) file(TO_NATIVE_PATH ${medInria_BINARY_DIR} MED_BIN_BASE) + file(TO_NATIVE_PATH ${pyncpp_DIR} PYNCPP_BIN_BASE) set(CONFIG_MODE $<$:Debug>$<$:Release>$<$:MinSizeRel>$<$:RelWithDebInfo>) @@ -161,10 +163,12 @@ if (WIN32) POST_BUILD COMMAND for %%I in ( ${ITK_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) COMMAND for %%I in ( ${VTK_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - COMMAND for %%I in ( ${DTK_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - COMMAND for %%I in ( ${DCM_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) + COMMAND for %%I in ( ${DTK_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) + COMMAND for %%I in ( ${DCM_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) COMMAND for %%I in ( ${QT5_BIN_BASE}\\bin\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - ) + COMMAND for %%I in ( ${PYTHON_BIN_BASE}\\bin\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) + COMMAND for %%I in ( ${PYTHON_BIN_BASE}\\bin\\DLLs ${PYTHON_BIN_BASE}\\bin\\Lib ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /d /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /d /H ${MED_BIN_BASE}\\%%~nxI %%~fI) + ) endif() diff --git a/superbuild/projects_modules/PYNCPP.cmake b/superbuild/projects_modules/pyncpp.cmake similarity index 57% rename from superbuild/projects_modules/PYNCPP.cmake rename to superbuild/projects_modules/pyncpp.cmake index 9ab6744882..e6f8b0076b 100644 --- a/superbuild/projects_modules/PYNCPP.cmake +++ b/superbuild/projects_modules/pyncpp.cmake @@ -11,25 +11,23 @@ # ################################################################################ -function(PYNCPP_project) +set(PYTHON_VERSION_MAJOR 3 CACHE STRING "Python major version") +set(PYTHON_VERSION_MINOR 10 CACHE STRING "Python minor version") +set(PYTHON_VERSION_PATCH 10 CACHE STRING "Python patch version") - set(ep PYNCPP) +function(pyncpp_project) + + set(ep pyncpp) EP_Initialisation(${ep} USE_SYSTEM OFF BUILD_SHARED_LIBS ON - REQUIRED_FOR_PLUGINS OFF + REQUIRED_FOR_PLUGINS ON ) - if(USE_SYSTEM_${ep}) - find_package(PYNCPP - REQUIRED COMPONENTS Qt5 - ) - else() - epComputPath(${ep}) + if(NOT USE_SYSTEM_${ep}) - set(source_dir ${EP_PATH_SOURCE}/${ep}) - set(binary_dir ${build_path}) + epComputPath(${ep}) set(project_args GIT_REPOSITORY ${GITHUB_PREFIX}LIRYC-IHU/pyncpp.git @@ -38,28 +36,45 @@ function(PYNCPP_project) GIT_PROGRESS True ) + set(cmake_args + -D "PYNCPP_PYTHON_VERSION_MAJOR:STRING=${PYTHON_VERSION_MAJOR}" + -D "PYNCPP_PYTHON_VERSION_MINOR:STRING=${PYTHON_VERSION_MINOR}" + -D "PYNCPP_PYTHON_VERSION_PATCH:STRING=${PYTHON_VERSION_PATCH}" + ) + + if(UNIX) + list(APPEND cmake_args + -D Qt5_DIR:PATH=${Qt5_DIR} + -D OPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR} + ) + if(APPLE) + list(APPEND cmake_args + -D CMAKE_MACOSX_RPATH:BOOL=OFF + ) + endif() + endif() + ## ##################################################################### ## Add external project ## ##################################################################### ExternalProject_Add(${ep} PREFIX ${EP_PATH_SOURCE} - SOURCE_DIR ${source_dir} - BINARY_DIR ${binary_dir} + SOURCE_DIR ${EP_PATH_SOURCE}/${ep} + BINARY_DIR ${build_path} TMP_DIR ${tmp_path} STAMP_DIR ${stamp_path} DEPENDS ${${ep}_dependencies} + CMAKE_ARGS ${cmake_args} INSTALL_COMMAND "" "${project_args}" ) - ExternalProject_Get_Property(${ep} binary_dir) - ## ##################################################################### ## Export variables ## ##################################################################### - set(${ep}_DIR ${binary_dir} PARENT_SCOPE) + set(${ep}_DIR ${build_path} PARENT_SCOPE) endif() From f23808cf85174201f78500139fd9ac788176e77f Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Mon, 19 Jun 2023 09:20:16 +0200 Subject: [PATCH 27/98] [VTK] avoid med crash with wrong version of vtk files --- .../legacy/medCoreLegacy/database/medDatabaseReader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp index 44c1369229..effda109c6 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp @@ -256,7 +256,10 @@ medAbstractData *medDatabaseReader::readFile( const QStringList& filenames ) } else { - dataReader->read(filenames); + if (dataReader->read(filenames) == false) + { + break; + } } dataReader->enableDeferredDeletion ( false ); medData = dynamic_cast(dataReader->data()); From 4e2673fbf7bc724abbe8de6941754580bc476152 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 6 Sep 2023 12:49:03 +0200 Subject: [PATCH 28/98] [Thumbnail]3.4, add a setting to [de]activate thumbnail generation for RAM purpose --- .../medCoreLegacy/data/medAbstractData.cpp | 25 ++++++++------ .../gui/toolboxes/medActionsToolBox.cpp | 33 ++++++++++++++----- .../gui/toolboxes/medActionsToolBox.h | 1 + 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp index 847e2a732a..2a0d0825f8 100644 --- a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp +++ b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -194,17 +195,21 @@ void medAbstractData::invokeModified() QImage medAbstractData::generateThumbnail(QSize size) { QImage thumbnail; - if (QThread::currentThread() != QApplication::instance()->thread()) - { - QMetaObject::invokeMethod(this, - "generateThumbnailInGuiThread", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QImage, thumbnail), - Q_ARG(QSize, size)); - } - else + + if(medSettingsManager::instance()->value("Browser", "thumbnail_creation_setting").toBool()) { - thumbnail = this->generateThumbnailInGuiThread(size); + if (QThread::currentThread() != QApplication::instance()->thread()) + { + QMetaObject::invokeMethod(this, + "generateThumbnailInGuiThread", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QImage, thumbnail), + Q_ARG(QSize, size)); + } + else + { + thumbnail = this->generateThumbnailInGuiThread(size); + } } return thumbnail; diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp index 8dbb404f26..d5c1554a00 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include class medActionsToolBoxPrivate @@ -54,15 +55,8 @@ medActionsToolBox::medActionsToolBox( QWidget *parent /*= 0*/, bool FILE_SYSTEM d->buttonsWidget = new QWidget(this); d->noButtonsSelectedWidget = new QWidget(this); - // Information Widget for File System tab + // Information Widget d->informationWidget = new QWidget(this); - QLabel* informationLabel = new QLabel(tr("To import DICOMs, select the directory containing these files.\nDo not select them separately."), - d->informationWidget); - informationLabel->setObjectName("actionToolBoxLabel"); - informationLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - informationLabel->setWordWrap(true); - QHBoxLayout* informationLayout = new QHBoxLayout(d->informationWidget); - informationLayout->addWidget(informationLabel, 0, Qt::AlignLeft); this->addWidget(d->informationWidget); initializeItemToActionsMap(); @@ -94,7 +88,25 @@ medActionsToolBox::medActionsToolBox( QWidget *parent /*= 0*/, bool FILE_SYSTEM // The order of the buttons in this list determines the order used to place them in the grid layout d->buttonsList << d->viewBt << d->loadBt << d->importBt; + // Information and settings d->informationWidget->setVisible(true); + + QLabel* informationLabel = new QLabel(tr("To import DICOM select the directory containing the files."), d->informationWidget); + informationLabel->setObjectName("actionToolBoxLabel"); + informationLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + informationLabel->setWordWrap(true); + auto informationLayout = new QVBoxLayout(d->informationWidget); + informationLayout->addWidget(informationLabel, 0, Qt::AlignLeft); + + auto thumbnailCreationSetting = new QCheckBox("Generate a thumbnail at import"); + thumbnailCreationSetting->setToolTip("Generating a thumbnail uses RAM, so it can be useful to disable it " + "if you're short of memory and have import crashes."); + auto thumbailValue = medSettingsManager::instance()->value("Browser", + "thumbnail_creation_setting", + true).toBool(); // Default value to true + thumbnailCreationSetting->setChecked(thumbailValue); + connect(thumbnailCreationSetting, &QCheckBox::clicked, this, &medActionsToolBox::thumbnailSettingClicked); + informationLayout->addWidget(thumbnailCreationSetting); } else //IF DATABASE { @@ -352,3 +364,8 @@ void medActionsToolBox::initializeItemToActionsMap() d->itemToActions.insert("Files & Folders", "Temporary Import"); d->itemToActions.insert("Files & Folders", "View"); } + +void medActionsToolBox::thumbnailSettingClicked(bool state) +{ + medSettingsManager::instance()->setValue("Browser", "thumbnail_creation_setting", state); +} \ No newline at end of file diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.h b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.h index cd404f1288..6fa0022e48 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.h +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.h @@ -64,6 +64,7 @@ public slots: void seriesSelected(const medDataIndex& index); void noPatientOrSeriesSelected(); void selectedPathsChanged(const QStringList& paths); + void thumbnailSettingClicked(bool state); protected: From 9f69a329ef5c530afd9ec90a6a130b425268e533 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 6 Sep 2023 12:50:24 +0200 Subject: [PATCH 29/98] [Thumbnail] add final line in file --- .../legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp index d5c1554a00..4615415b0e 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp @@ -368,4 +368,4 @@ void medActionsToolBox::initializeItemToActionsMap() void medActionsToolBox::thumbnailSettingClicked(bool state) { medSettingsManager::instance()->setValue("Browser", "thumbnail_creation_setting", state); -} \ No newline at end of file +} From 0078ac2552730f9a3c04a0a70a3732b11e6fe8a2 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 6 Sep 2023 13:52:39 +0200 Subject: [PATCH 30/98] [SplashScreen] speeds up launch removing txt on splashscreen --- src/app/medInria/main.cpp | 10 +----- src/app/medInria/medSplashScreen.cpp | 51 ---------------------------- src/app/medInria/medSplashScreen.h | 36 +------------------- 3 files changed, 2 insertions(+), 95 deletions(-) diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index 6bd391f283..22b91b9b27 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -126,8 +126,6 @@ int main(int argc,char* argv[]) bool show_splash = false; #endif - medSettingsManager* mnger = medSettingsManager::instance(); - QStringList posargs; for (int i=1;isetDatabaseLocation(); @@ -253,8 +246,7 @@ int main(int argc,char* argv[]) QGLFormat::setDefaultFormat(format); } - if (show_splash) - splash.finish(mainwindow); + splash.close(); #if(USE_PYTHON) if(!pythonErrorMessage.isEmpty()) diff --git a/src/app/medInria/medSplashScreen.cpp b/src/app/medInria/medSplashScreen.cpp index 7275863009..3b2feac381 100644 --- a/src/app/medInria/medSplashScreen.cpp +++ b/src/app/medInria/medSplashScreen.cpp @@ -22,12 +22,10 @@ class medSplashScreenPrivate { public: QPixmap pixmap; - QString message; int alignment; QColor color; }; - //////////////////////////////////////////////////////////////////////////// medSplashScreen::medSplashScreen(const QPixmap& thePixmap) : QWidget(0, Qt::SplashScreen |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint) @@ -69,61 +67,12 @@ medSplashScreen::~medSplashScreen() d = nullptr; } -//////////////////////////////////////////////////////////////////////////// -void medSplashScreen::clearMessage() -{ - d->message.clear(); - repaint(); -} - -//////////////////////////////////////////////////////////////////////////// -void medSplashScreen::showMessage(const QString& message) -{ - const dtkPlugin* plugin = medPluginManager::instance()->plugin(message); - if (plugin) - { - d->message = QString("Loading: ") + plugin->name(); - } - else - { - d->message = QString("Loading: ") + message; - } - - repaint(); -} - -void medSplashScreen::repaint() -{ - QWidget::repaint(); - QApplication::flush(); - qApp->processEvents(QEventLoop::AllEvents); -} - -void medSplashScreen::finish(QWidget *mainWin) - -{ - if (mainWin) - { -#if defined(Q_OS_LINUX) - Q_UNUSED(QTest::qWaitForWindowExposed(mainWin)); -#endif - } - close(); -} - //////////////////////////////////////////////////////////////////////////// void medSplashScreen::paintEvent(QPaintEvent* pe) { Q_UNUSED(pe); - QRect aTextRect(rect()); - aTextRect.setRect(aTextRect.x() + 120, - aTextRect.y() + 5, - aTextRect.width() - 10, - aTextRect.height() - 10); - QPainter aPainter(this); aPainter.drawPixmap(rect(), d->pixmap); aPainter.setPen(d->color); - aPainter.drawText(aTextRect, d->alignment, d->message); } diff --git a/src/app/medInria/medSplashScreen.h b/src/app/medInria/medSplashScreen.h index 03a364cfad..f5eb5fc9a8 100644 --- a/src/app/medInria/medSplashScreen.h +++ b/src/app/medInria/medSplashScreen.h @@ -18,7 +18,7 @@ class medSplashScreenPrivate; /** - * @brief create SplaScreen widget with translucent background. + * @brief create Splashcreen widget with translucent background. * * not based on QSplashScreen which does not support this feature. Almost all features from the QSplashscreen are replicated, including messages refreshed before a QApplication event loop is running. @@ -38,41 +38,7 @@ class medSplashScreen : public QWidget medSplashScreen(const QPixmap& pixmap); ~medSplashScreen(); - /** - * @brief hides the splashscreen when w is shown. - * - * @param w - */ - void finish(QWidget *w); - - /** - * @brief Repaints the widget without the need of an event loop; - * - */ - void repaint(); - -public slots: - /** - * @brief Clears the message. - * - */ - void clearMessage(); - - /** - * @brief Shows a message with the desired alignment and color. - * - * @param theMessage - * @param theAlignment - * @param theColor - */ - void showMessage(const QString& theMessage); - private: - /** - * @brief - * - * @param pe - */ void paintEvent(QPaintEvent* pe); medSplashScreenPrivate * d; From e75c60f7e626320c865b2d7b366d4edd77aaf7a8 Mon Sep 17 00:00:00 2001 From: fcollot Date: Thu, 7 Sep 2023 15:27:18 +0200 Subject: [PATCH 31/98] make Packaging a dedicated project (#1141) * Packaging project * packaging fix --- CMakeLists.txt | 1 + packaging/{Packaging.cmake => CMakeLists.txt} | 20 ++++++++--------- packaging/apple/ApplePackScript.cmake.in | 4 ++-- packaging/apple/ApplePackaging.cmake | 12 +++++----- packaging/apple/mac_packager.sh.in | 13 ++++++----- packaging/linux/DEB.cmake | 2 +- packaging/linux/LinuxPackaging.cmake | 11 +++++----- packaging/linux/RPM.cmake | 4 ++-- packaging/windows/WindowsLaunchers.cmake | 2 +- packaging/windows/WindowsPackaging.cmake | 2 +- superbuild/CMakeLists.txt | 22 ++++++++++++++++--- .../launchers}/Launchers.cmake | 11 +++------- .../launchers}/locate_bin.sh | 0 .../launchers}/medInria.sh.in | 0 14 files changed, 59 insertions(+), 45 deletions(-) rename packaging/{Packaging.cmake => CMakeLists.txt} (80%) rename {packaging/unix => superbuild/launchers}/Launchers.cmake (80%) rename {packaging/unix => superbuild/launchers}/locate_bin.sh (100%) rename {packaging/unix => superbuild/launchers}/medInria.sh.in (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1beaac2202..d332a1cdb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,3 +26,4 @@ SET(CMAKE_CXX_STANDARD 17) project(MEDINRIA_SUPERBUILD VERSION ${MEDINRIA_SUPERBUILD_VERSION}) add_subdirectory(superbuild) +add_subdirectory(packaging) diff --git a/packaging/Packaging.cmake b/packaging/CMakeLists.txt similarity index 80% rename from packaging/Packaging.cmake rename to packaging/CMakeLists.txt index 62b294384c..68157eb3d6 100644 --- a/packaging/Packaging.cmake +++ b/packaging/CMakeLists.txt @@ -11,6 +11,8 @@ # ################################################################################ +project(MEDINRIA_PACKAGING VERSION ${MEDINRIA_SUPERBUILD_VERSION}) + # Set common CPACK variables set(CPACK_PACKAGE_NAME medInria CACHE STRING "Name of the package for the superproject") @@ -29,10 +31,10 @@ set(CPACK_PACKAGE_VERSION_MINOR ${${PROJECT_NAME}_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${${PROJECT_NAME}_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION ${${PROJECT_NAME}_VERSION}) -set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/packaging/Description.txt") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/Description.txt") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt") -set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/packaging/Readme.txt") -set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_SOURCE_DIR}/packaging/Welcome.txt") +set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/Readme.txt") +set(CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/Welcome.txt") set(CPACK_BINARY_STGZ OFF) set(CPACK_BINARY_TBZ2 OFF) @@ -46,24 +48,22 @@ option(CPACK_SOURCE_TXZ "Enable to build TXZ source packages" OFF) option(CPACK_SOURCE_TZ "Enable to build TZ source packages" OFF) option(CPACK_SOURCE_ZIP "Enable to build ZIP source packages" OFF) +set(CPACK_INSTALL_CMAKE_PROJECTS ${PROJECT_BINARY_DIR} ${PROJECT_NAME} ALL "/") + # Set cpack variables specific to the plateform if (WIN32) - set(WIN_PACK_DIR ${CMAKE_SOURCE_DIR}/packaging/windows) + set(WIN_PACK_DIR ${PROJECT_SOURCE_DIR}/windows) include(${WIN_PACK_DIR}/WindowsLaunchers.cmake) include(${WIN_PACK_DIR}/WindowsPackaging.cmake) endif() if (APPLE) - include(${CMAKE_SOURCE_DIR}/packaging/apple/ApplePackaging.cmake) + include(${PROJECT_SOURCE_DIR}/apple/ApplePackaging.cmake) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - include(${CMAKE_SOURCE_DIR}/packaging/linux/LinuxPackaging.cmake) -endif() - -if (UNIX) - include(${CMAKE_SOURCE_DIR}/packaging/unix/Launchers.cmake) + include(${PROJECT_SOURCE_DIR}/linux/LinuxPackaging.cmake) endif() # Include cpack modules diff --git a/packaging/apple/ApplePackScript.cmake.in b/packaging/apple/ApplePackScript.cmake.in index 34c8d47d74..c9625bd2b6 100644 --- a/packaging/apple/ApplePackScript.cmake.in +++ b/packaging/apple/ApplePackScript.cmake.in @@ -1,2 +1,2 @@ -execute_process(COMMAND rm -f @PROJECT_BINARY_DIR@/medInria-@@PROJECT_NAME@_VERSION@.dmg) -execute_process(COMMAND @PROJECT_BINARY_DIR@/packaging/apple/mac_packager.sh @medInria_BINARY_DIR@/bin/plugins @medInria_BINARY_DIR@/bin/plugins_legacy @PRIVATE_PLUGINS_DIRS@ @PRIVATE_PLUGINS_LEGACY_DIRS@) +execute_process(COMMAND rm -f @CMAKE_BINARY_DIR@/medInria-@@PROJECT_NAME@_VERSION@.dmg) +execute_process(COMMAND @PROJECT_BINARY_DIR@/apple/mac_packager.sh @medInria_DIR@/bin/plugins @medInria_DIR@/bin/plugins_legacy @PRIVATE_PLUGINS_DIRS@ @PRIVATE_PLUGINS_LEGACY_DIRS@) diff --git a/packaging/apple/ApplePackaging.cmake b/packaging/apple/ApplePackaging.cmake index 8a5d88afec..26eb19686a 100644 --- a/packaging/apple/ApplePackaging.cmake +++ b/packaging/apple/ApplePackaging.cmake @@ -29,20 +29,20 @@ set(CPACK_PACKAGE_FILE_NAME ## Add Apple packaging script ## ############################################################################# -configure_file(${CMAKE_SOURCE_DIR}/packaging/apple/ApplePackScript.cmake.in +configure_file(${PROJECT_SOURCE_DIR}/apple/ApplePackScript.cmake.in ${PROJECT_BINARY_DIR}/tmp.in ) configure_file( ${PROJECT_BINARY_DIR}/tmp.in - ${PROJECT_BINARY_DIR}/packaging/apple/ApplePackScript.cmake + ${PROJECT_BINARY_DIR}/apple/ApplePackScript.cmake ) -configure_file(${CMAKE_SOURCE_DIR}/packaging/apple/mac_packager.sh.in - ${PROJECT_BINARY_DIR}/packaging/apple/mac_packager.sh +configure_file(${PROJECT_SOURCE_DIR}/apple/mac_packager.sh.in + ${PROJECT_BINARY_DIR}/apple/mac_packager.sh @ONLY ) -set(CPACK_INSTALL_SCRIPT - ${PROJECT_BINARY_DIR}/packaging/apple/ApplePackScript.cmake +set(CPACK_INSTALL_SCRIPT + ${PROJECT_BINARY_DIR}/apple/ApplePackScript.cmake ) diff --git a/packaging/apple/mac_packager.sh.in b/packaging/apple/mac_packager.sh.in index 693802ac19..477ed4d8c4 100755 --- a/packaging/apple/mac_packager.sh.in +++ b/packaging/apple/mac_packager.sh.in @@ -12,13 +12,13 @@ # The bundle medInria.app will contain all plugins and will be standalone, packaged into MedINRIA.dmg in the install dir -cd @medInria_BINARY_DIR@/bin +cd @PROJECT_BINARY_DIR@ \rm -fr TmpInstall mkdir -p TmpInstall/medInria.app/Contents/Resources -cp -r medInria.app/Contents/MacOS TmpInstall/medInria.app/Contents -cp medInria.app/Contents/Info.plist TmpInstall/medInria.app/Contents -cp medInria.app/Contents/Resources/medInria.icns TmpInstall/medInria.app/Contents/Resources +cp -r @medInria_DIR@/bin/medInria.app/Contents/MacOS TmpInstall/medInria.app/Contents +cp @medInria_DIR@/bin/medInria.app/Contents/Info.plist TmpInstall/medInria.app/Contents +cp @medInria_DIR@/bin/medInria.app/Contents/Resources/medInria.icns TmpInstall/medInria.app/Contents/Resources cd TmpInstall @@ -43,9 +43,10 @@ gunzip -f MedinriaPackage.sparseimage.gz devName=`hdiutil attach -readwrite -noverify -noautoopen MedinriaPackage.sparseimage | egrep '^/dev/' | sed 1q | awk '{print $1}'` diskutil rename "medInria base" "medInria @MEDINRIA_SUPERBUILD_VERSION@" -\cp -rf @medInria_BINARY_DIR@/bin/TmpInstall/medInria.app /Volumes/"medInria @MEDINRIA_SUPERBUILD_VERSION@"/ +\cp -rf @PROJECT_BINARY_DIR@/TmpInstall/medInria.app /Volumes/"medInria @MEDINRIA_SUPERBUILD_VERSION@"/ sync hdiutil detach $devName hdiutil convert MedinriaPackage.sparseimage -format UDZO -imagekey zlib-level=9 -o "medInria-@MEDINRIA_SUPERBUILD_VERSION@.dmg" 1>/dev/null 2>/dev/null -\rm -fr MedinriaPackage.sparseimage @medInria_BINARY_DIR@/bin/TmpInstall +\rm -fr MedinriaPackage.sparseimage @PROJECT_BINARY_DIR@/TmpInstall +\mv medInria-@MEDINRIA_SUPERBUILD_VERSION@.dmg @CMAKE_BINARY_DIR@ diff --git a/packaging/linux/DEB.cmake b/packaging/linux/DEB.cmake index c005050e50..0be03e5b7e 100644 --- a/packaging/linux/DEB.cmake +++ b/packaging/linux/DEB.cmake @@ -24,4 +24,4 @@ set(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME}) set(CPACK_DEBIAN_PACKAGE_PROVIDES ${CPACK_PACKAGE_NAME}) set(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) -set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${CMAKE_BINARY_DIR}/packaging/linux/prerm;${CMAKE_BINARY_DIR}/packaging/linux/postinst) +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${PROJECT_BINARY_DIR}/linux/prerm;${PROJECT_BINARY_DIR}/linux/postinst) diff --git a/packaging/linux/LinuxPackaging.cmake b/packaging/linux/LinuxPackaging.cmake index 7d70129e92..c83ed55f39 100644 --- a/packaging/linux/LinuxPackaging.cmake +++ b/packaging/linux/LinuxPackaging.cmake @@ -46,8 +46,8 @@ set(CPACK_GENERATOR "ZIP") # Remember the linux packaging source dir -set(CURRENT_SRC_DIR ${CMAKE_SOURCE_DIR}/packaging/linux) -set(CURRENT_BIN_DIR ${CMAKE_BINARY_DIR}/packaging/linux) +set(CURRENT_SRC_DIR ${PROJECT_SOURCE_DIR}/linux) +set(CURRENT_BIN_DIR ${PROJECT_BINARY_DIR}/linux) # Generate CPACK_PROJECT_CONFIG_FILE @@ -79,8 +79,8 @@ install(FILES ${CURRENT_BIN_DIR}/medInria.desktop # Add project to package -# save the medinria-superproject install target to add it last -set(backup_CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS} ${CMAKE_BINARY_DIR} ${CMAKE_PROJECT_NAME} ALL "/") +# save the medinria-packaging install target to add it last +set(backup_CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS}) #clear it set(CPACK_INSTALL_CMAKE_PROJECTS "") @@ -99,8 +99,9 @@ foreach(dir ${PRIVATE_PLUGINS_LEGACY_DIRS}) set(CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS} ${dir} ${dir} ALL "/bin") endforeach() +install(PROGRAMS ${CMAKE_BINARY_DIR}/superbuild/medInria.sh DESTINATION bin) install(CODE "include(${CURRENT_BIN_DIR}/PostArchiveCleanupScript.cmake)") -# force the medinria-superproject install target to run last so we can use it +# force the medinria-packaging install target to run last so we can use it # to cleanup set(CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS} ${backup_CPACK_INSTALL_CMAKE_PROJECTS}) diff --git a/packaging/linux/RPM.cmake b/packaging/linux/RPM.cmake index b66cee7c79..ac151d3444 100644 --- a/packaging/linux/RPM.cmake +++ b/packaging/linux/RPM.cmake @@ -25,5 +25,5 @@ set(CPACK_RPM_PACKAGE_PROVIDES "${CPACK_PACKAGE_NAME} = ${CPACK_PACKAGE_VERSION} set(CPACK_RPM_PACKAGE_LICENSE BSD) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${ARCH}) -set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/packaging/linux/postinst) -set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/packaging/linux/prerm) +set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${PROJECT_BINARY_DIR}/linux/postinst) +set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE ${PROJECT_BINARY_DIR}/linux/prerm) diff --git a/packaging/windows/WindowsLaunchers.cmake b/packaging/windows/WindowsLaunchers.cmake index 6c960a5c9e..e426fbf650 100644 --- a/packaging/windows/WindowsLaunchers.cmake +++ b/packaging/windows/WindowsLaunchers.cmake @@ -11,7 +11,7 @@ # ################################################################################ -set(CURRENT_SRC_DIR ${CMAKE_SOURCE_DIR}/packaging/windows) +set(CURRENT_SRC_DIR ${PROJECT_SOURCE_DIR}/windows) foreach (dir ${PRIVATE_PLUGINS_DIRS}) set(DEV_PLUGINS_DIRS "${DEV_PLUGINS_DIRS}:${dir}/plugins/%1") diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index deccaac3a8..16816d666b 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -34,7 +34,7 @@ endif() set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${MSVC_ARCH}") -set(ICON_PATH "${PROJECT_SOURCE_DIR}/src/app/medInria/resources/medInria.ico") +set(ICON_PATH "${CMAKE_SOURCE_DIR}/src/app/medInria/resources/medInria.ico") # The icon to install the application. set(CPACK_NSIS_MUI_ICON ${ICON_PATH}) diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index fac4976c35..cfd289c714 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -156,7 +156,6 @@ file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/projects_modules - ${CMAKE_SOURCE_DIR}/packaging ${CMAKE_MODULE_PATH} ) @@ -189,7 +188,24 @@ include(EP_CheckEnvironment) include(ConfigureExternalProjects) ## ############################################################################# -## Package +## Create launch scripts ## ############################################################################# -include(Packaging) +if (UNIX) + include(${CMAKE_CURRENT_SOURCE_DIR}/launchers/Launchers.cmake) +endif() + +## ############################################################################# +## Export variables +## ############################################################################# + +set(external_projects ${external_projects} PARENT_SCOPE) + +foreach(external_project ${external_projects}) + if(NOT USE_SYSTEM_${external_project} AND BUILD_SHARED_LIBS_${external_project}) + ExternalProject_Get_Property(${external_project} binary_dir) + ExternalProject_Get_Property(${external_project} source_dir) + set(${external_project}_DIR ${binary_dir} PARENT_SCOPE) + set(${external_project}_SOURCE_DIR ${source_dir} PARENT_SCOPE) + endif() +endforeach() diff --git a/packaging/unix/Launchers.cmake b/superbuild/launchers/Launchers.cmake similarity index 80% rename from packaging/unix/Launchers.cmake rename to superbuild/launchers/Launchers.cmake index 5d8d79c718..231083d33f 100644 --- a/packaging/unix/Launchers.cmake +++ b/superbuild/launchers/Launchers.cmake @@ -11,9 +11,6 @@ # ################################################################################ -set(CURRENT_SRC_DIR ${CMAKE_SOURCE_DIR}/packaging/unix) -set(CURRENT_BIN_DIR ${CMAKE_BINARY_DIR}/packaging/unix) - # Install a launcher scripts for the application with right environment variable # For developpers. @@ -40,11 +37,11 @@ endif() set(MEDINRIA_PLUGINS_DIRS "${binary_dir}/bin/plugins:${DEV_PLUGINS_DIRS}") set(MEDINRIA_PLUGINS_LEGACY_DIRS "${binary_dir}/bin/plugins_legacy:${DEV_PLUGINS_LEGACY_DIRS}") -configure_file(${CURRENT_SRC_DIR}/medInria.sh.in medInria.sh @ONLY) +configure_file(${CMAKE_CURRENT_LIST_DIR}/medInria.sh.in ${CMAKE_BINARY_DIR}/medInria.sh @ONLY) # For end users. -file(READ "${CURRENT_SRC_DIR}/locate_bin.sh" LOCATE) +file(READ "${CMAKE_CURRENT_LIST_DIR}/locate_bin.sh" LOCATE) set(MEDINRIA_DIR "$(locate)") if (APPLE) @@ -56,6 +53,4 @@ endif() set(MEDINRIA_PLUGINS_DIRS "\${MEDINRIA_DIR}/plugins:\${MEDINRIA_DIR}/bin/plugins:\${MEDINRIA_USER_PLUGINS_DIRS}") set(MEDINRIA_PLUGINS_LEGACY_DIRS "\${MEDINRIA_DIR}/plugins_legacy:\${MEDINRIA_DIR}/bin/plugins_legacy:\${MEDINRIA_USER_PLUGINS_DIRS_LEGACY}") -configure_file(${CURRENT_SRC_DIR}/medInria.sh.in ${CURRENT_BIN_DIR}/medInria_launcher.sh @ONLY) -install(PROGRAMS ${CURRENT_BIN_DIR}/medInria_launcher.sh - DESTINATION bin) +configure_file(${CMAKE_CURRENT_LIST_DIR}/medInria.sh.in ${CMAKE_CURRENT_BINARY_DIR}/medInria.sh @ONLY) diff --git a/packaging/unix/locate_bin.sh b/superbuild/launchers/locate_bin.sh similarity index 100% rename from packaging/unix/locate_bin.sh rename to superbuild/launchers/locate_bin.sh diff --git a/packaging/unix/medInria.sh.in b/superbuild/launchers/medInria.sh.in similarity index 100% rename from packaging/unix/medInria.sh.in rename to superbuild/launchers/medInria.sh.in From cb18fe3f24069c5f1af1c7c382f8c6e65fa418e1 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Fri, 29 Sep 2023 13:59:18 +0200 Subject: [PATCH 32/98] [Thumbnail] generate thumbnail setting typo, oups --- .../legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp index 4615415b0e..ce754240e9 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp @@ -101,10 +101,10 @@ medActionsToolBox::medActionsToolBox( QWidget *parent /*= 0*/, bool FILE_SYSTEM auto thumbnailCreationSetting = new QCheckBox("Generate a thumbnail at import"); thumbnailCreationSetting->setToolTip("Generating a thumbnail uses RAM, so it can be useful to disable it " "if you're short of memory and have import crashes."); - auto thumbailValue = medSettingsManager::instance()->value("Browser", + auto thumbnailValue = medSettingsManager::instance()->value("Browser", "thumbnail_creation_setting", true).toBool(); // Default value to true - thumbnailCreationSetting->setChecked(thumbailValue); + thumbnailCreationSetting->setChecked(thumbnailValue); connect(thumbnailCreationSetting, &QCheckBox::clicked, this, &medActionsToolBox::thumbnailSettingClicked); informationLayout->addWidget(thumbnailCreationSetting); } From 77edc734f14a11dd8ff55443d7af6627aeb8bda7 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 19 Apr 2023 10:46:42 +0200 Subject: [PATCH 33/98] [MemoryLeak] DCMTKImageReader and INRImageReader --- .../readers/itkDCMTKDataImageReader.cpp | 12 +++++- .../itkINRDataImageReader.cpp | 38 +++---------------- .../itkINRDataImageReader.h | 25 +++--------- 3 files changed, 20 insertions(+), 55 deletions(-) diff --git a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp index 4998472ae5..67026c22a5 100644 --- a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp +++ b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp @@ -41,7 +41,7 @@ namespace itk { itkTypeMacro(DCMTKDataImageReaderCommand, Command) itkNewMacro(Self) - void Execute(Object *caller, const EventObject &event); + void Execute(Object *caller, const EventObject &event); void Execute(const Object *caller, const EventObject &event); void SetDataImageReader(dtkAbstractDataReader* reader) { m_Reader = reader; } @@ -160,6 +160,14 @@ void itkDCMTKDataImageReaderPrivate::threadDone(itk::DCMTKImageIO::Pointer io) if (ioThreads->size() == 0) ioPointers->clear(); + + delete ioThreads; + delete ioPointers; + QMutex *m_ptr = mutex.fetchAndStoreOrdered(nullptr); + if (m_ptr) + { + delete m_ptr; + } } void itkDCMTKDataImageReaderPrivate::initialiseStatic() @@ -192,7 +200,7 @@ itkDCMTKDataImageReader::itkDCMTKDataImageReader() : dtkAbstractDataReader(), d( itkDCMTKDataImageReader::~itkDCMTKDataImageReader() { delete d; - d = 0; + d = nullptr; } diff --git a/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.cpp b/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.cpp index b7738e69a6..ed419f92de 100644 --- a/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.cpp +++ b/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.cpp @@ -12,47 +12,23 @@ =========================================================================*/ -// ///////////////////////////////////////////////////////////////// -// Generated by medPluginGenerator -// ///////////////////////////////////////////////////////////////// - #include "itkINRDataImageReader.h" #include #include - #include -// ///////////////////////////////////////////////////////////////// -// itkINRDataImageReaderPrivate -// ///////////////////////////////////////////////////////////////// - -class itkINRDataImageReaderPrivate -{ -public: -}; - -// ///////////////////////////////////////////////////////////////// -// itkINRDataImageReader -// ///////////////////////////////////////////////////////////////// -const char itkINRDataImageReader::ID[] = "itkINRDataImageReader"; - -itkINRDataImageReader::itkINRDataImageReader(void) : itkDataImageReaderBase(), d(new itkINRDataImageReaderPrivate) +itkINRDataImageReader::itkINRDataImageReader() : itkDataImageReaderBase() { this->io = InrimageImageIO::New(); } -itkINRDataImageReader::~itkINRDataImageReader(void) +bool itkINRDataImageReader::registered() { - + return medAbstractDataFactory::instance()->registerDataReaderType("itkINRDataImageReader",s_handled(),createItkINRDataImageReader); } -bool itkINRDataImageReader::registered(void) -{ - return medAbstractDataFactory::instance()->registerDataReaderType(ID,s_handled(),createItkINRDataImageReader); -} - -QString itkINRDataImageReader::description(void) const +QString itkINRDataImageReader::description() const { return "itkINRDataImageReader"; } @@ -75,15 +51,11 @@ QStringList itkINRDataImageReader::s_handled() { << "itkDataImageRGB3" << "itkDataImageRGBA3"; } -QString itkINRDataImageReader::identifier() const { - return ID; -} - // ///////////////////////////////////////////////////////////////// // Type instantiation // ///////////////////////////////////////////////////////////////// -dtkAbstractDataReader *createItkINRDataImageReader(void) +dtkAbstractDataReader *createItkINRDataImageReader() { return new itkINRDataImageReader; } diff --git a/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.h b/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.h index 8614747850..f427d5e878 100644 --- a/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.h +++ b/src/plugins/legacy/itkINRDataImageReader/itkINRDataImageReader.h @@ -11,36 +11,21 @@ PURPOSE. =========================================================================*/ - -// ///////////////////////////////////////////////////////////////// -// Generated by dtkPluginGenerator -// ///////////////////////////////////////////////////////////////// - #pragma once #include #include "itkINRDataImageReaderPluginExport.h" -class itkINRDataImageReaderPrivate; - class ITKINRDATAIMAGEREADERPLUGIN_EXPORT itkINRDataImageReader : public itkDataImageReaderBase { public: - itkINRDataImageReader(void); - virtual ~itkINRDataImageReader(void); - - virtual QString identifier() const; - virtual QString description(void) const; + itkINRDataImageReader(); + ~itkINRDataImageReader() = default; + QString description() const; QStringList handled() const; - static QStringList s_handled(); - - static bool registered(void); - -private: - itkINRDataImageReaderPrivate *d; - static const char ID[]; + static bool registered(); }; -dtkAbstractDataReader *createItkINRDataImageReader(void); +dtkAbstractDataReader *createItkINRDataImageReader(); From cfe62c3870fd32150792db24c9d1967d7b8a0d0c Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 19 Apr 2023 11:42:55 +0200 Subject: [PATCH 34/98] [MemoryLeak] enhance dcmtk image reader destructor --- .../readers/itkDCMTKDataImageReader.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp index 67026c22a5..7d6a90192d 100644 --- a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp +++ b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp @@ -135,6 +135,13 @@ itkDCMTKDataImageReaderPrivate::itkDCMTKDataImageReaderPrivate() itkDCMTKDataImageReaderPrivate::~itkDCMTKDataImageReaderPrivate() { threadDone(io); + + QMutex *m_ptr = mutex.fetchAndStoreOrdered(nullptr); + if (m_ptr) + { + m_ptr->unlock(); + delete m_ptr; + } } itk::DCMTKImageIO::Pointer itkDCMTKDataImageReaderPrivate::getNewIO() @@ -161,13 +168,11 @@ void itkDCMTKDataImageReaderPrivate::threadDone(itk::DCMTKImageIO::Pointer io) if (ioThreads->size() == 0) ioPointers->clear(); - delete ioThreads; delete ioPointers; - QMutex *m_ptr = mutex.fetchAndStoreOrdered(nullptr); - if (m_ptr) - { - delete m_ptr; - } + ioPointers = nullptr; + + delete ioThreads; + ioThreads = nullptr; } void itkDCMTKDataImageReaderPrivate::initialiseStatic() From 969e18c95a5b1005c66301551264c1ee7e6e425e Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Mon, 2 Oct 2023 11:13:51 +0200 Subject: [PATCH 35/98] [memLeak] rm changes on mutex --- .../itkDataImage/readers/itkDCMTKDataImageReader.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp index 7d6a90192d..d1a75a0347 100644 --- a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp +++ b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp @@ -135,13 +135,6 @@ itkDCMTKDataImageReaderPrivate::itkDCMTKDataImageReaderPrivate() itkDCMTKDataImageReaderPrivate::~itkDCMTKDataImageReaderPrivate() { threadDone(io); - - QMutex *m_ptr = mutex.fetchAndStoreOrdered(nullptr); - if (m_ptr) - { - m_ptr->unlock(); - delete m_ptr; - } } itk::DCMTKImageIO::Pointer itkDCMTKDataImageReaderPrivate::getNewIO() From fe59a6e9653a985b2c3ff015d2e1893f94451e82 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Wed, 11 Oct 2023 15:32:02 +0200 Subject: [PATCH 36/98] [MemoryLeak] medLogger TeeStreams, TeeDevices and d (#1117) * [MemoryLeak] medLogger TeeStreams, TeeDevices and d * [MemoryLeak] medLogger: buffers --- src/app/medInria/medLogger.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/app/medInria/medLogger.cpp b/src/app/medInria/medLogger.cpp index d78d8a9e46..4b9ff6c4ee 100644 --- a/src/app/medInria/medLogger.cpp +++ b/src/app/medInria/medLogger.cpp @@ -167,6 +167,8 @@ medLogger::~medLogger() QObject::disconnect(this, SIGNAL(newQtMessage(QtMsgType,QString)), this, SLOT(redirectQtMessage(QtMsgType,QString))); qInstallMessageHandler(nullptr); finalizeTeeStreams(); + + delete d; } void medLogger::initializeTeeStreams() @@ -175,16 +177,28 @@ void medLogger::initializeTeeStreams() createTeeStream(&std::cerr); } +/** + * @brief Delete TeeStreams and associated TeeDevices from memory + * + */ void medLogger::finalizeTeeStreams() { - for (int i = 0; i < d->redirectedStreams.length(); i++) + for (auto teeStream : d->teeStreams) { - d->teeStreams.first()->flush(); - d->teeStreams.first()->close(); - delete d->teeStreams.takeFirst(); - delete d->redirectedStreamDummies.takeFirst(); + teeStream->flush(); + delete teeStream; + } + d->teeStreams.clear(); + + for (auto redirectedStreamDummy : d->redirectedStreamDummies) + { + delete redirectedStreamDummy; d->redirectedStreams.takeFirst()->rdbuf(d->previousStreamBuffers.takeFirst()); } + d->redirectedStreamDummies.clear(); + + d->redirectedStreams.clear(); + d->previousStreamBuffers.clear(); } void medLogger::createTeeStream(std::ostream* targetStream) @@ -192,8 +206,7 @@ void medLogger::createTeeStream(std::ostream* targetStream) d->redirectedStreams.append(targetStream); d->previousStreamBuffers.append(targetStream->rdbuf()); d->redirectedStreamDummies.append(new std::ostream(targetStream->rdbuf())); - TeeDevice* teeDevice = new TeeDevice(*d->redirectedStreamDummies.last(), d->logFile); - d->teeStreams.append(new TeeStream(*teeDevice)); + d->teeStreams.append(new TeeStream(TeeDevice(*d->redirectedStreamDummies.last(), d->logFile))); targetStream->rdbuf(d->teeStreams.last()->rdbuf()); } From ff5e3e49f9e7ef778453673d599c886a570b680c Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 12 Oct 2023 09:46:02 +0200 Subject: [PATCH 37/98] [Settings] solve graphical bug of database widget --- .../gui/settingsWidgets/medDatabaseSettingsWidget.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp index 68249679b7..71c9fdd19e 100644 --- a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp @@ -35,18 +35,14 @@ medDatabaseSettingsWidget::medDatabaseSettingsWidget(QWidget *parent) : d->dbPath = new QLineEdit(this); d->btChooseDir = new QPushButton(tr("Select directory..."), this); - QWidget* databaseLocation = new QWidget(this); QHBoxLayout* dbLayout = new QHBoxLayout; dbLayout->addWidget(d->dbPath); dbLayout->addWidget(d->btChooseDir); - databaseLocation->setLayout(dbLayout); connect(d->btChooseDir, SIGNAL(clicked()), this, SLOT(selectDbDirectory())); - databaseLocation->setContentsMargins(0,-8,0,0); - QFormLayout* formLayout = new QFormLayout(this); - formLayout->addRow(tr("Database location:"), databaseLocation); + formLayout->addRow(tr("Database location:"), dbLayout); } void medDatabaseSettingsWidget::selectDbDirectory() From 9858f752197604b1af3188f6b8c5bc6083585fc4 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Thu, 19 Oct 2023 10:49:58 +0200 Subject: [PATCH 38/98] [MemoryLeak] medVtkView::buildThumbnail (#1124) * [MemoryLeak] medVtkView::buildThumbnail * [MemoryLeak] medAbstractData::generateThumbnailInGuiThread * [MemoryLeak] rm TimeLineParam changes, created segfault closing the app --- .../medCoreLegacy/data/medAbstractData.cpp | 57 ------------------- .../parameters/medParameterPoolManagerL.cpp | 12 ++++ .../parameters/medParameterPoolManagerL.h | 1 + src/plugins/legacy/medVtkView/medVtkView.cpp | 23 +------- 4 files changed, 16 insertions(+), 77 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp index 847e2a732a..4fc9d020da 100644 --- a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp +++ b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp @@ -31,8 +31,6 @@ class medAbstractDataPrivate public: medDataIndex index; QList< dtkSmartPointer > attachedData; - - QImage thumbnail; }; medAbstractData::medAbstractData( medAbstractData *parent ) @@ -212,62 +210,7 @@ QImage medAbstractData::generateThumbnail(QSize size) QImage medAbstractData::generateThumbnailInGuiThread(QSize size) { - // Hack: some drivers crash on offscreen rendering, so we detect which one - // we're currently using, and if it is one of the crashy ones, render to a - // proper window instead, that we try to hide behind the main one. - - bool offscreenCapable = false; - med::GPUInfo gpu = med::gpuModel(); - -#if defined(Q_OS_MAC) - // all drivers work so far - offscreenCapable = true; -#elif defined(Q_OS_WIN32) - // doesn't work on Intel drivers - if ( ! gpu.vendor.toLower().contains("intel")) - offscreenCapable = true; -#elif defined(Q_OS_LINUX) - if (gpu.vendor.toLower().contains("nvidia") - || gpu.vendor.toLower().contains("intel") - || gpu.vendor.toLower().contains("mesa/x.org")) - { - offscreenCapable = true; - } -#endif - dtkSmartPointer view = medViewFactory::instance()->createView("medVtkView"); - - if(offscreenCapable) - { - view->setOffscreenRendering(true); - } - else - { - // We need to get a handle to the main window, so we can A) find its position, and B) ensure it is drawn over the temporary window - const QVariant property = QApplication::instance()->property("MainWindow"); - QObject* qObject = property.value(); - - if (qObject) - { - QMainWindow* aMainWindow = dynamic_cast(qObject); - QWidget * viewWidget = view->viewWidget(); - - // Show our view in a separate, temporary window - viewWidget->show(); - // position the temporary window behind the main application - viewWidget->move(aMainWindow->geometry().x(), aMainWindow->geometry().y()); - // and raise the main window above the temporary - aMainWindow->raise(); - - // We need to wait for the window manager to finish animating before we can continue. -#ifdef Q_OS_LINUX - Q_UNUSED(QTest::qWaitForWindowExposed(viewWidget)); -#endif - } - } - view->addLayer(this); - - // We're rendering here, to the temporary window, and will then use the resulting image return view->generateThumbnail(size); } diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp index d209f8e6b6..c153054fb4 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp @@ -37,6 +37,18 @@ medParameterPoolManagerL::medParameterPoolManagerL(void) : d(new medParameterPoo { } +medParameterPoolManagerL::~medParameterPoolManagerL() +{ + for (auto it = d->pools.begin(); it != d->pools.end(); ++it) + { + delete it.value(); + } + d->pools.clear(); + + delete d; + d = nullptr; +} + void medParameterPoolManagerL::removePool(QString poolId) { medParameterPoolL *poolToRemove = d->pools.value(poolId); diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h index 3ea7f353a8..334956a75f 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h @@ -42,6 +42,7 @@ public slots: protected: medParameterPoolManagerL(); + ~medParameterPoolManagerL(); static medParameterPoolManagerL *s_instance; diff --git a/src/plugins/legacy/medVtkView/medVtkView.cpp b/src/plugins/legacy/medVtkView/medVtkView.cpp index 03053aea81..049ccfbc17 100644 --- a/src/plugins/legacy/medVtkView/medVtkView.cpp +++ b/src/plugins/legacy/medVtkView/medVtkView.cpp @@ -194,6 +194,7 @@ medVtkView::~medVtkView() d->renWin->SetOffScreenRendering(0); d->renWin->Delete(); delete d->viewWidget; + delete d->mainWindow; delete d; } @@ -465,29 +466,11 @@ QImage medVtkView::buildThumbnail(const QSize &size) this->blockSignals(true); int w(size.width()), h(size.height()); - // will cause crashes if any calls to renWin->Render() happened before this line - d->mainWindow->resize(w,h); - d->mainWindow->show(); - d->renWin->SetSize(w,h); - render(); - -#ifdef Q_OS_LINUX - // X11 likes to animate window creation, which means by the time we grab the - // widget, it might not be fully ready yet, in which case we get artefacts. - // Only necessary if rendering to an actual screen window. - if(d->renWin->GetOffScreenRendering() == 0) - { - Q_UNUSED(QTest::qWaitForWindowExposed(d->viewWidget)); - } -#endif - QImage thumbnail = d->viewWidget->grabFramebuffer(); + thumbnail = thumbnail.scaledToHeight(h, Qt::SmoothTransformation); + thumbnail = thumbnail.copy((thumbnail.width()-w)/2, 0, w, h); - d->mainWindow->hide(); this->blockSignals(false); - - thumbnail = thumbnail.copy(0, thumbnail.height() - h, w, h); - return thumbnail; } From 3be79cbed475e627a4d89947725ad38563883041 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 30 Oct 2023 10:18:08 +0100 Subject: [PATCH 39/98] [MemoryLeaks] solve several memory leaks (#1130) * [MemoryLeak] medAbstractData::generateThumbnailInGuiThread * [MemoryLeak] rm TimeLineParam changes, created segfault closing the app * [RAM] solve some memory leaks * [RAM] mem leak with instances * [MemLeak] rm composer, do not instantiate everything on homepage, etc * [MemLeak] solve a graphic pb in Paint * [vtkImage2DDisplay] vtkSmartPointer instead of Delete * [medParameterPoolManagerL] parameters in std::shared_ptr, private class in unique_ptr * [instances] switch to std::unique_ptr * [viewinteractor] add back a deletion * [medToolBoxHeader] remove unnecessary nullptr after delete * [RAM] after review, remove some QString and changes --- .../medInria/areas/browser/medBrowserArea.cpp | 2 +- .../diffusion/medDiffusionWorkspace.cpp | 2 +- .../areas/workspaces/medWorkspaceArea.cpp | 18 +-- src/app/medInria/main.cpp | 12 +- src/app/medInria/medDataSourceManager.cpp | 49 +++---- src/app/medInria/medDataSourceManager.h | 14 +- src/app/medInria/medDatabaseDataSource.cpp | 5 + src/app/medInria/medEmptyDbWarning.cpp | 6 +- src/app/medInria/medFileSystemDataSource.cpp | 19 ++- src/app/medInria/medMainWindow.cpp | 130 ++++++++++-------- src/app/medInria/medPacsDataSource.cpp | 2 +- src/app/medInria/medPluginWidget.cpp | 10 +- src/app/medInria/medQuickAccessMenu.cpp | 2 +- src/app/medInria/medSaveModifiedDialog.cpp | 10 +- src/app/medInria/medSplashScreen.cpp | 4 +- .../medCoreLegacy/data/medAbstractData.cpp | 19 ++- .../medCoreLegacy/data/medMetaDataKeys.h | 9 +- .../database/medAbstractDatabaseImporter.cpp | 6 +- .../medCoreLegacy/database/medDataManager.cpp | 35 ++--- .../medCoreLegacy/database/medDataManager.h | 10 +- .../database/medDataPacsController.cpp | 36 +++-- .../database/medDataPacsController.h | 9 +- .../database/medDataPacsImporter.cpp | 29 ++-- .../database/medDatabaseImporter.cpp | 11 +- .../database/medDatabaseModel.cpp | 30 ++-- .../medDatabaseNonPersistentController.cpp | 28 ++-- .../medDatabaseNonPersistentController.h | 6 +- .../medDatabaseNonPersistentImporter.cpp | 19 +-- .../medDatabasePersistentController.cpp | 20 +-- .../database/medDatabaseReader.cpp | 2 +- .../database/medDatabaseRemover.cpp | 22 +-- .../database/medLocalDbController.cpp | 15 +- .../database/medLocalDbController.h | 10 +- .../medCoreLegacy/database/medStorage.cpp | 4 +- .../gui/commonWidgets/medProgressionStack.cpp | 2 +- .../gui/database/medDatabasePreview.cpp | 43 ++++-- .../gui/database/medDatabaseView.cpp | 28 ++-- .../gui/lookUpTables/medClutEditor.h | 2 + .../gui/medAbstractWorkspaceLegacy.cpp | 71 +++++----- .../legacy/medCoreLegacy/gui/medLinkMenu.cpp | 12 +- .../gui/medSelectorWorkspace.cpp | 2 +- .../gui/medTabbedViewContainers.cpp | 20 +-- .../medDatabaseSettingsWidget.cpp | 3 +- .../gui/settingsWidgets/medSettingsEditor.cpp | 4 +- .../medStartupSettingsWidget.cpp | 10 +- .../gui/toolboxes/medActionsToolBox.cpp | 6 +- ...ompositeDataSetImporterSelectorToolBox.cpp | 5 +- .../medRegistrationSelectorToolBox.cpp | 6 +- .../gui/toolboxes/medToolBox.cpp | 4 +- .../gui/toolboxes/medToolBoxHeader.cpp | 7 +- .../gui/viewContainers/medViewContainer.cpp | 35 +++-- .../medViewContainerManager.cpp | 15 +- .../viewContainers/medViewContainerManager.h | 10 +- .../medViewContainerSplitter.cpp | 2 +- .../legacy/medCoreLegacy/medJobManagerL.cpp | 13 +- .../legacy/medCoreLegacy/medJobManagerL.h | 10 +- .../medCoreLegacy/medMessageController.cpp | 15 +- .../medCoreLegacy/medMessageController.h | 11 +- .../legacy/medCoreLegacy/medPluginManager.cpp | 15 +- .../legacy/medCoreLegacy/medPluginManager.h | 14 +- .../medCoreLegacy/medSettingsManager.cpp | 27 ++-- .../legacy/medCoreLegacy/medSettingsManager.h | 14 +- .../parameters/medAbstractParameterGroupL.cpp | 2 +- .../parameters/medLayerParameterGroupL.cpp | 4 +- .../parameters/medParameterGroupManagerL.cpp | 15 +- .../parameters/medParameterGroupManagerL.h | 9 +- .../parameters/medParameterPoolManagerL.cpp | 52 +++---- .../parameters/medParameterPoolManagerL.h | 16 ++- .../parameters/medViewParameterGroupL.cpp | 4 +- .../medAbstractLayeredViewInteractor.cpp | 2 +- .../views/medAbstractImageView.cpp | 4 +- .../views/medAbstractLayeredView.cpp | 12 +- .../medCoreLegacy/views/medAbstractView.cpp | 3 +- .../medAbstractImageViewNavigator.cpp | 2 +- .../medImageIO/itkDataImageReaderBase.cpp | 2 +- .../medImageIO/itkDataImageWriterBase.cpp | 2 +- .../vtkImageView/vtkImage2DDisplay.cpp | 4 +- .../vtkImageView/vtkImageView2D.cxx | 3 +- ...actArithmeticOperationProcessPresenter.cpp | 2 +- .../medAbstractDWIMaskingProcessPresenter.cpp | 2 +- ...ffusionModelEstimationProcessPresenter.cpp | 2 +- ...actDiffusionScalarMapsProcessPresenter.cpp | 2 +- ...edAbstractTractographyProcessPresenter.cpp | 2 +- ...ionModelEstimationMetaProcessPresenter.cpp | 2 +- .../medAbstractMaskImageProcessPresenter.cpp | 2 +- ...actMorphomathOperationProcessPresenter.cpp | 2 +- ...tSingleFilterOperationProcessPresenter.cpp | 2 +- .../LCCLogDemons/LCCLogDemonsToolBox.cpp | 4 +- .../diffeomorphicDemonsToolBox.cpp | 4 +- .../iterativeClosestPointToolBox.cpp | 8 +- .../medVtkViewItkDataImageInteractor.cpp | 3 +- .../writers/itkNiftiDataImageWriter.cpp | 16 +-- .../legacy/itkFilters/itkFiltersToolBox.cpp | 4 +- .../itkMorphologicalFiltersToolBox.cpp | 4 +- .../itkProcessRegistrationOptimusToolBox.cpp | 6 +- .../manualRegistrationLandmarkController.cpp | 6 +- .../manualRegistrationToolBox.cpp | 4 +- .../medAlgorithmPaintToolBox.cpp | 10 +- .../medBinaryOperationToolBox.cpp | 14 +- .../legacy/medComposerArea/CMakeLists.txt | 98 ------------- .../medComposerArea/medComposerArea.cpp | 104 -------------- .../legacy/medComposerArea/medComposerArea.h | 38 ----- .../medComposerArea/medComposerAreaPlugin.cpp | 34 ----- .../medComposerArea/medComposerAreaPlugin.h | 32 ----- .../medComposerAreaPlugin.json | 5 - .../medCreateMeshFromMaskToolBox.cpp | 4 +- .../medFilteringWorkspaceL.cpp | 4 +- .../medMaskApplicationToolBox.cpp | 8 +- .../medN4BiasCorrection.cpp | 2 +- .../medN4BiasCorrectionToolBox.cpp | 4 +- .../medRemeshing/medRemeshingToolBox.cpp | 6 +- .../medSegmentation/medMagicWandCommand.cpp | 2 +- .../medVtkFibersDataInteractor.cpp | 16 +-- src/plugins/legacy/medVtkView/medVtkView.cpp | 21 +-- .../legacy/medVtkView/medVtkViewNavigator.cpp | 2 +- .../legacy/medVtkView/medVtkViewPlugin.cpp | 55 ++------ .../legacy/medVtkView/medVtkViewPlugin.h | 15 +- .../meshManipulationToolBox.cpp | 6 +- .../legacy/meshMapping/meshMappingToolBox.cpp | 4 +- .../legacy/polygonRoi/polygonLabel.cpp | 2 +- .../toolboxes/polygonRoiToolBox.cpp | 8 +- .../polygonRoi/viewevent/baseViewEvent.cpp | 4 +- .../qtdcmDataSourceSerieToolBox.cpp | 6 - .../legacy/reformat/medCropToolBox.cpp | 6 +- .../legacy/reformat/resliceToolBox.cpp | 12 +- .../variationalSegmentation/varSegToolBox.cpp | 8 +- .../legacy/voiCutter/voiCutterToolBox.cpp | 6 +- 127 files changed, 693 insertions(+), 1067 deletions(-) delete mode 100644 src/plugins/legacy/medComposerArea/CMakeLists.txt delete mode 100644 src/plugins/legacy/medComposerArea/medComposerArea.cpp delete mode 100644 src/plugins/legacy/medComposerArea/medComposerArea.h delete mode 100644 src/plugins/legacy/medComposerArea/medComposerAreaPlugin.cpp delete mode 100644 src/plugins/legacy/medComposerArea/medComposerAreaPlugin.h delete mode 100644 src/plugins/legacy/medComposerArea/medComposerAreaPlugin.json diff --git a/src/app/medInria/areas/browser/medBrowserArea.cpp b/src/app/medInria/areas/browser/medBrowserArea.cpp index c1323631ac..8a9ff41ae8 100644 --- a/src/app/medInria/areas/browser/medBrowserArea.cpp +++ b/src/app/medInria/areas/browser/medBrowserArea.cpp @@ -77,7 +77,7 @@ medBrowserArea::medBrowserArea(QWidget *parent) : QWidget(parent), d(new medBrow layout->addWidget(d->stack); // DataSources - for(medAbstractDataSource *dataSource : medDataSourceManager::instance()->dataSources()) + for(medAbstractDataSource *dataSource : medDataSourceManager::instance().dataSources()) { addDataSource(dataSource); } diff --git a/src/app/medInria/areas/workspaces/diffusion/medDiffusionWorkspace.cpp b/src/app/medInria/areas/workspaces/diffusion/medDiffusionWorkspace.cpp index df69d64e77..3f78bb9baf 100644 --- a/src/app/medInria/areas/workspaces/diffusion/medDiffusionWorkspace.cpp +++ b/src/app/medInria/areas/workspaces/diffusion/medDiffusionWorkspace.cpp @@ -167,7 +167,7 @@ void medDiffusionWorkspace::changeCurrentContainer() disconnect (d->diffusionContainer,SIGNAL(viewContentChanged()), this, SLOT(updateToolBoxesInputs())); // Now connect new container - d->diffusionContainer = medViewContainerManager::instance()->container(containersSelectedList.first()); + d->diffusionContainer = medViewContainerManager::instance().container(containersSelectedList.first()); if (!d->diffusionContainer.isNull()) { d->diffusionContainer->setClosingMode(medViewContainer::CLOSE_CONTAINER); diff --git a/src/app/medInria/areas/workspaces/medWorkspaceArea.cpp b/src/app/medInria/areas/workspaces/medWorkspaceArea.cpp index 8f9326c707..1b699d48ec 100644 --- a/src/app/medInria/areas/workspaces/medWorkspaceArea.cpp +++ b/src/app/medInria/areas/workspaces/medWorkspaceArea.cpp @@ -91,10 +91,10 @@ medWorkspaceArea::medWorkspaceArea(QWidget *parent) : QWidget(parent), d(new med d->splitter->addWidget(d->viewContainer); d->splitter->addWidget(d->toolBoxContainer); - this->addDatabaseView(medDataSourceManager::instance()->databaseDataSource()); - connect(medDataSourceManager::instance(), SIGNAL(open(medDataIndex)), this, SIGNAL(open(medDataIndex))); + this->addDatabaseView(medDataSourceManager::instance().databaseDataSource()); + connect(&medDataSourceManager::instance(), SIGNAL(open(medDataIndex)), this, SIGNAL(open(medDataIndex))); - if (!d->splitter->restoreState(medSettingsManager::instance()->value("medWorkspaceArea", "splitterState").toByteArray())) + if (!d->splitter->restoreState(medSettingsManager::instance().value("medWorkspaceArea", "splitterState").toByteArray())) { d->splitter->setOrientation(Qt::Horizontal); //viewcontainer size @@ -111,7 +111,7 @@ medWorkspaceArea::medWorkspaceArea(QWidget *parent) : QWidget(parent), d(new med medWorkspaceArea::~medWorkspaceArea() { - medSettingsManager::instance()->setValue("workspaceAreaSplitter", "state", d->splitter->saveState()); + medSettingsManager::instance().setValue("workspaceAreaSplitter", "state", d->splitter->saveState()); delete d; d = nullptr; } @@ -326,9 +326,11 @@ bool medWorkspaceArea::setCurrentWorkspace(medAbstractWorkspaceLegacy *workspace } } - disconnect(this, SIGNAL(open(medDataIndex)), d->currentWorkspace, 0); - disconnect(d->currentWorkspace, nullptr, this, nullptr); - + if (d->currentWorkspace) + { + disconnect(this, SIGNAL(open(medDataIndex)), d->currentWorkspace, 0); + disconnect(d->currentWorkspace, nullptr, this, nullptr); + } d->currentWorkspace = workspace; connect(this, SIGNAL(open(medDataIndex)), d->currentWorkspace, SLOT(open(medDataIndex))); connect(d->currentWorkspace, &medAbstractWorkspaceLegacy::toolBoxInserted, d->toolBoxContainer, &medToolBoxContainer::insertToolBox); @@ -349,7 +351,7 @@ bool medWorkspaceArea::setCurrentWorkspace(medAbstractWorkspaceLegacy *workspace } d->toolBoxContainer->setVisible(workspace->areToolBoxesVisible()); - medParameterGroupManagerL::instance()->setCurrentWorkspace(workspace->identifier()); + medParameterGroupManagerL::instance().setCurrentWorkspace(workspace->identifier()); return true; } diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index 22b91b9b27..d3eced0d48 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -179,7 +179,7 @@ int main(int argc,char* argv[]) splash.show(); } - medDataManager::instance()->setDatabaseLocation(); + medDataManager::instance().setDatabaseLocation(); #if(USE_PYTHON) pyncpp::Manager pythonManager; @@ -199,8 +199,8 @@ int main(int argc,char* argv[]) } #endif - medPluginManager::instance()->setVerboseLoading(true); - medPluginManager::instance()->initialize(); + medPluginManager::instance().setVerboseLoading(true); + medPluginManager::instance().initialize(); //Use Qt::WA_DeleteOnClose attribute to be sure to always have only one closeEvent. medMainWindow *mainwindow = new medMainWindow; @@ -209,7 +209,7 @@ int main(int argc,char* argv[]) if (DirectView) mainwindow->setStartup(medMainWindow::WorkSpace,posargs); - bool fullScreen = medSettingsManager::instance()->value("startup", "fullscreen", false).toBool(); + bool fullScreen = medSettingsManager::instance().value("startup", "fullscreen", false).toBool(); const bool hasFullScreenArg = application.arguments().contains("--fullscreen"); const bool hasNoFullScreenArg = application.arguments().contains("--no-fullscreen"); @@ -256,7 +256,7 @@ int main(int argc,char* argv[]) } #endif - if (medPluginManager::instance()->plugins().isEmpty()) { + if (medPluginManager::instance().plugins().isEmpty()) { QMessageBox::warning(mainwindow, QObject::tr("No plugin loaded"), QObject::tr("Warning : no plugin loaded successfully.")); @@ -275,7 +275,7 @@ int main(int argc,char* argv[]) // Start main loop. const int status = application.exec(); - medPluginManager::instance()->uninitialize(); + medPluginManager::instance().uninitialize(); return status; } diff --git a/src/app/medInria/medDataSourceManager.cpp b/src/app/medInria/medDataSourceManager.cpp index 7a6da77893..93d16c79d0 100644 --- a/src/app/medInria/medDataSourceManager.cpp +++ b/src/app/medInria/medDataSourceManager.cpp @@ -40,6 +40,17 @@ class medDataSourceManagerPrivate medPacsDataSource *pacsSource; }; +std::unique_ptr medDataSourceManager::s_instance = nullptr; + +medDataSourceManager &medDataSourceManager::instance() +{ + if(!s_instance) + { + s_instance = std::unique_ptr(new medDataSourceManager()); + } + return *s_instance.get(); +} + medDataSourceManager::medDataSourceManager(): d(new medDataSourceManagerPrivate) { // Data base data source @@ -100,9 +111,9 @@ void medDataSourceManager::connectDataSource(medAbstractDataSource *dataSource) connect(dataSource, SIGNAL(dataToFetchReceived(QHash >, QHash >)), this, SLOT(fetchData(QHash >, QHash >))); - connect(dataSource, SIGNAL(updateProgress(int)), medDataManager::instance(), SIGNAL(updateProgress(int))); - connect(dataSource, SIGNAL(moveState(int, const QString &)), medDataManager::instance(), SIGNAL(moveState(int, const QString &))); - connect(medDataManager::instance(), SIGNAL(moveRequested(const QString &, const QString &)), dataSource, SIGNAL(moveRequested(const QString &, const QString &))); + connect(dataSource, SIGNAL(updateProgress(int)), &medDataManager::instance(), SIGNAL(updateProgress(int))); + connect(dataSource, SIGNAL(moveState(int, const QString &)), &medDataManager::instance(), SIGNAL(moveState(int, const QString &))); + connect(&medDataManager::instance(), SIGNAL(moveRequested(const QString &, const QString &)), dataSource, SIGNAL(moveRequested(const QString &, const QString &))); } //TODO: Maybe it is not the best place to put it (medDataManager?) @@ -127,36 +138,29 @@ void medDataSourceManager::importData(medAbstractData *data) return; } - medDataManager::instance()->importData(data, true); + medDataManager::instance().importData(data, true); } void medDataSourceManager::exportData(const medDataIndex &index) { //TODO did it all from the medDataManager ? - RDE - dtkSmartPointer data = medDataManager::instance()->retrieveData(index); - medDataManager::instance()->exportData(data); + dtkSmartPointer data = medDataManager::instance().retrieveData(index); + medDataManager::instance().exportData(data); } void medDataSourceManager::importFile(QString path) { - medDataManager::instance()->importPath(path, false, true); + medDataManager::instance().importPath(path, false, true); } void medDataSourceManager::fetchData(QHash > pData, QHash > sData) { - medDataManager::instance()->fetchData(pData, sData); + medDataManager::instance().fetchData(pData, sData); } void medDataSourceManager::emitDataReceivingFailed(QString fileName) { - medMessageController::instance()->showError(tr("Unable to get from source the data named ") + fileName, 3000); -} - -medDataSourceManager *medDataSourceManager::instance(void) -{ - if (!s_instance) - s_instance = new medDataSourceManager; - return s_instance; + medMessageController::instance().showError(tr("Unable to get from source the data named ") + fileName, 3000); } medDataSourceManager::~medDataSourceManager() @@ -182,16 +186,7 @@ void medDataSourceManager::openFromIndex(medDataIndex index) void medDataSourceManager::loadFromPath(QString path) { - medDataManager::instance()->importPath(path, false); -} - -void medDataSourceManager::destroy() -{ - if (s_instance) - { - delete s_instance; - s_instance = nullptr; - } + medDataManager::instance().importPath(path, false); } QList medDataSourceManager::dataSources() @@ -203,5 +198,3 @@ medDatabaseDataSource* medDataSourceManager::databaseDataSource() { return d->dbSource; } - -medDataSourceManager *medDataSourceManager::s_instance = nullptr; diff --git a/src/app/medInria/medDataSourceManager.h b/src/app/medInria/medDataSourceManager.h index 9bef985e47..6cd5ad2456 100644 --- a/src/app/medInria/medDataSourceManager.h +++ b/src/app/medInria/medDataSourceManager.h @@ -17,6 +17,8 @@ #include +#include + class medAbstractDataSource; class medDataSourceManagerPrivate; class medAbstractData; @@ -28,9 +30,8 @@ class medDataSourceManager : public QObject Q_OBJECT public: - static medDataSourceManager *instance(); - - static void destroy(); + ~medDataSourceManager(); + static medDataSourceManager &instance(); QList dataSources(); medDatabaseDataSource *databaseDataSource(); @@ -49,10 +50,8 @@ protected slots: void load(QString); protected: - void connectDataSource(medAbstractDataSource *dataSource); - medDataSourceManager(); - ~medDataSourceManager(); + void connectDataSource(medAbstractDataSource *dataSource); protected slots: void openFromPath(QString path); @@ -60,7 +59,6 @@ protected slots: void loadFromPath(QString path); private: - - static medDataSourceManager *s_instance; + static std::unique_ptr s_instance; medDataSourceManagerPrivate *d; }; diff --git a/src/app/medInria/medDatabaseDataSource.cpp b/src/app/medInria/medDatabaseDataSource.cpp index b1f52270f5..ff9e8273ee 100644 --- a/src/app/medInria/medDatabaseDataSource.cpp +++ b/src/app/medInria/medDatabaseDataSource.cpp @@ -56,6 +56,11 @@ medDatabaseDataSource::medDatabaseDataSource( QWidget* parent ): medAbstractData medDatabaseDataSource::~medDatabaseDataSource() { + qDeleteAll(d->toolBoxes.begin(), d->toolBoxes.end()); + d->toolBoxes.clear(); + + delete d->model; + delete d; d = nullptr; } diff --git a/src/app/medInria/medEmptyDbWarning.cpp b/src/app/medInria/medEmptyDbWarning.cpp index f8df33316b..c112a6b13c 100644 --- a/src/app/medInria/medEmptyDbWarning.cpp +++ b/src/app/medInria/medEmptyDbWarning.cpp @@ -56,14 +56,12 @@ medEmptyDbWarning::~medEmptyDbWarning() void medEmptyDbWarning::accept() { - medSettingsManager* mng = medSettingsManager::instance(); - mng->setValue("system","showEmptyDbWarning",d->showAgainBox->isChecked()); + medSettingsManager::instance().setValue("system","showEmptyDbWarning",d->showAgainBox->isChecked()); QDialog::accept(); } void medEmptyDbWarning::reject() { - medSettingsManager* mng = medSettingsManager::instance(); - mng->setValue("system","showEmptyDbWarning",d->showAgainBox->isChecked()); + medSettingsManager::instance().setValue("system","showEmptyDbWarning",d->showAgainBox->isChecked()); QDialog::reject(); } diff --git a/src/app/medInria/medFileSystemDataSource.cpp b/src/app/medInria/medFileSystemDataSource.cpp index f1731188dc..cc70fa9985 100644 --- a/src/app/medInria/medFileSystemDataSource.cpp +++ b/src/app/medInria/medFileSystemDataSource.cpp @@ -36,7 +36,7 @@ medFileSystemDataSource::medFileSystemDataSource( QWidget* parent ): medAbstract d->filesystemWidget = new QWidget(); // Get previous path or default one - QString defaultPath = medSettingsManager::instance()->value("Browser", "default_pipeline_import_path").toString(); + QString defaultPath = medSettingsManager::instance().value("Browser", "default_pipeline_import_path").toString(); if (defaultPath.isEmpty()) { @@ -145,9 +145,9 @@ medFileSystemDataSource::medFileSystemDataSource( QWidget* parent ): medAbstract connect (d->toolbar, SIGNAL(showHiddenFiles(bool)), this, SLOT(saveHiddenFilesSettings(bool))); // set default values - medSettingsManager* mng = medSettingsManager::instance(); - bool showHiddenFiles = mng->value("medFileSystemDataSource", "showHiddenFiles", false).toBool(); - bool listViewOn = mng->value("medFileSystemDataSource", "listView", true).toBool(); + medSettingsManager &mng = medSettingsManager::instance(); + bool showHiddenFiles = mng.value("medFileSystemDataSource", "showHiddenFiles", false).toBool(); + bool listViewOn = mng.value("medFileSystemDataSource", "listView", true).toBool(); d->toolbar->onShowHiddenFiles(showHiddenFiles); @@ -272,7 +272,7 @@ void medFileSystemDataSource::onFileClicked(const QFileInfo& info) d->infoText->setText("" + info.fileName() + " selected - " + this->formatByteSize(info.size()) + ""); } - medSettingsManager::instance()->setValue("Browser", "default_pipeline_import_path", info.path()); + medSettingsManager::instance().setValue("Browser", "default_pipeline_import_path", info.path()); } QString medFileSystemDataSource::formatByteSize(qint64 bytes) @@ -300,18 +300,15 @@ void medFileSystemDataSource::onNothingSelected(void) void medFileSystemDataSource::saveHiddenFilesSettings(bool show) { - medSettingsManager* mng = medSettingsManager::instance(); - mng->setValue("medFileSystemDataSource", "showHiddenFiles", show); + medSettingsManager::instance().setValue("medFileSystemDataSource", "showHiddenFiles", show); } void medFileSystemDataSource::saveListViewSettings() { - medSettingsManager* mng = medSettingsManager::instance(); - mng->setValue("medFileSystemDataSource", "listView", false); + medSettingsManager::instance().setValue("medFileSystemDataSource", "listView", false); } void medFileSystemDataSource::saveTreeViewSettings() { - medSettingsManager* mng = medSettingsManager::instance(); - mng->setValue("medFileSystemDataSource", "listView", true); + medSettingsManager::instance().setValue("medFileSystemDataSource", "listView", true); } diff --git a/src/app/medInria/medMainWindow.cpp b/src/app/medInria/medMainWindow.cpp index d2a28f13e3..4213870e4c 100644 --- a/src/app/medInria/medMainWindow.cpp +++ b/src/app/medInria/medMainWindow.cpp @@ -106,8 +106,7 @@ medMainWindow::medMainWindow ( QWidget *parent ) : QMainWindow ( parent ), d ( n d->currentArea = nullptr; // Browser area. - d->browserArea = new medBrowserArea(this); - d->browserArea->setObjectName("medBrowserArea"); + d->browserArea = nullptr; // Workspace area. d->workspaceArea = new medWorkspaceArea (this); @@ -118,15 +117,12 @@ medMainWindow::medMainWindow ( QWidget *parent ) : QMainWindow ( parent ), d ( n d->homepageArea->setObjectName("medHomePageArea"); //Composer - d->composerArea = new medComposerArea(this); - d->composerArea->setObjectName("medComposerArea"); + d->composerArea = nullptr; // Stack d->stack = new QStackedWidget(this); d->stack->addWidget(d->homepageArea); - d->stack->addWidget(d->browserArea); d->stack->addWidget(d->workspaceArea); - d->stack->addWidget(d->composerArea); // Setup quick access menu d->quickAccessButton = new medQuickAccessPushButton ( this ); @@ -273,8 +269,8 @@ medMainWindow::medMainWindow ( QWidget *parent ) : QMainWindow ( parent ), d ( n this->setWindowTitle(qApp->applicationName()); // Connect the messageController with the status for notification messages management - connect(medMessageController::instance(), SIGNAL(addMessage(medMessage*)), d->statusBar, SLOT(addMessage(medMessage*))); - connect(medMessageController::instance(), SIGNAL(removeMessage(medMessage*)), d->statusBar, SLOT(removeMessage(medMessage*))); + connect(&medMessageController::instance(), SIGNAL(addMessage(medMessage*)), d->statusBar, SLOT(addMessage(medMessage*))); + connect(&medMessageController::instance(), SIGNAL(removeMessage(medMessage*)), d->statusBar, SLOT(removeMessage(medMessage*))); d->shortcutShortcut = new QShortcut(QKeySequence(tr(CONTROL_KEY "+Space")), this, @@ -303,22 +299,21 @@ void medMainWindow::mousePressEvent ( QMouseEvent* event ) void medMainWindow::restoreSettings() { - medSettingsManager * mnger = medSettingsManager::instance(); - - this->restoreState(mnger->value("medMainWindow", "state").toByteArray()); - this->restoreGeometry(mnger->value("medMainWindow", "geometry").toByteArray()); + medSettingsManager &mnger = medSettingsManager::instance(); + this->restoreState(mnger.value("medMainWindow", "state").toByteArray()); + this->restoreGeometry(mnger.value("medMainWindow", "geometry").toByteArray()); } void medMainWindow::saveSettings() { if(!this->isFullScreen()) { - medSettingsManager * mnger = medSettingsManager::instance(); - mnger->setValue("medMainWindow", "state", this->saveState()); - mnger->setValue("medMainWindow", "geometry", this->saveGeometry()); + medSettingsManager &mnger = medSettingsManager::instance(); + mnger.setValue("medMainWindow", "state", this->saveState()); + mnger.setValue("medMainWindow", "geometry", this->saveGeometry()); // Keep the current screen for multiple-screens display - mnger->setValue("medMainWindow", "currentScreen", QApplication::desktop()->screenNumber(this)); + mnger.setValue("medMainWindow", "currentScreen", QApplication::desktop()->screenNumber(this)); } } @@ -378,11 +373,11 @@ void medMainWindow::open(const medDataIndex &index) void medMainWindow::open(const QString & path) { QEventLoop loop; - QUuid uuid = medDataManager::instance()->importPath(path, false); + QUuid uuid = medDataManager::instance().importPath(path, false); if (!uuid.isNull()) { d->expectedUuids.append(uuid); - connect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), this, SLOT(open_waitForImportedSignal(medDataIndex,QUuid))); + connect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), this, SLOT(open_waitForImportedSignal(medDataIndex,QUuid))); while( d->expectedUuids.contains(uuid)) { loop.processEvents(QEventLoop::ExcludeUserInputEvents); @@ -402,7 +397,7 @@ void medMainWindow::open_waitForImportedSignal(medDataIndex index, QUuid uuid) if(d->expectedUuids.contains(uuid)) { d->expectedUuids.removeAll(uuid); - disconnect(medDataManager::instance(),SIGNAL(dataImported(medDataIndex,QUuid)), this,SLOT(open_waitForImportedSignal(medDataIndex,QUuid))); + disconnect(&medDataManager::instance(),SIGNAL(dataImported(medDataIndex,QUuid)), this,SLOT(open_waitForImportedSignal(medDataIndex,QUuid))); if (index.isValid()) { this->showWorkspace(medVisualizationWorkspace::staticIdentifier()); @@ -533,26 +528,35 @@ void medMainWindow::switchToHomepageArea() void medMainWindow::switchToBrowserArea() { - if(d->currentArea == d->browserArea) - return; - - d->currentArea = d->browserArea; - - d->shortcutAccessWidget->updateSelected("Browser"); - d->quickAccessWidget->updateSelected("Browser"); + if(d->currentArea != d->browserArea) + { + if (d->browserArea == nullptr) + { + d->browserArea = new medBrowserArea(this); + d->browserArea->setObjectName("medBrowserArea"); + d->stack->addWidget(d->browserArea); + } - d->quickAccessButton->setText(tr("Workspace: Browser")); - d->quickAccessButton->setMinimumWidth(170); - if (d->quickAccessWidget->isVisible()) - this->hideQuickAccess(); + d->currentArea = d->browserArea; - if (d->shortcutAccessVisible) - this->hideShortcutAccess(); + d->shortcutAccessWidget->updateSelected("Browser"); + d->quickAccessWidget->updateSelected("Browser"); - d->screenshotButton->setEnabled(false); - d->movieButton->setEnabled(false); - d->adjustSizeButton->setEnabled(false); - d->stack->setCurrentWidget(d->browserArea); + d->quickAccessButton->setText(tr("Workspace: Browser")); + d->quickAccessButton->setMinimumWidth(170); + if (d->quickAccessWidget->isVisible()) + { + this->hideQuickAccess(); + } + if (d->shortcutAccessVisible) + { + this->hideShortcutAccess(); + } + d->screenshotButton->setEnabled(false); + d->movieButton->setEnabled(false); + d->adjustSizeButton->setEnabled(false); + d->stack->setCurrentWidget(d->browserArea); + } } void medMainWindow::switchToSearchArea() @@ -608,7 +612,6 @@ void medMainWindow::switchToSearchArea() void medMainWindow::switchToWorkspaceArea() { - if(d->currentArea == d->workspaceArea) return; @@ -624,14 +627,14 @@ void medMainWindow::switchToWorkspaceArea() // Dialog window to recall users if database is empty // but only if the warning is enabled in medSettings - bool showWarning = medSettingsManager::instance()->value( + bool showWarning = medSettingsManager::instance().value( "system", "showEmptyDbWarning", QVariant(true)).toBool(); if ( showWarning ) { - QList indexes = medDatabaseNonPersistentController::instance()->availableItems(); - QList patients = medDataManager::instance()->controller()->patients(); + QList indexes = medDatabaseNonPersistentController::instance().availableItems(); + QList patients = medDataManager::instance().controller()->patients(); if( indexes.isEmpty() ) if( patients.isEmpty()) { @@ -643,25 +646,34 @@ void medMainWindow::switchToWorkspaceArea() void medMainWindow::switchToComposerArea() { - if(d->currentArea == d->composerArea) - return; - - d->currentArea = d->composerArea; + if(d->currentArea != d->composerArea) + { + if (d->composerArea == nullptr) + { + d->composerArea = new medComposerArea(this); + d->composerArea->setObjectName("medComposerArea"); + d->stack->addWidget(d->composerArea); + } - d->shortcutAccessWidget->updateSelected("Composer"); - d->quickAccessWidget->updateSelected("Composer"); + d->currentArea = d->composerArea; - d->quickAccessButton->setText(tr("Workspace: Composer")); - d->quickAccessButton->setMinimumWidth(170); - if (d->quickAccessWidget->isVisible()) - this->hideQuickAccess(); + d->shortcutAccessWidget->updateSelected("Composer"); + d->quickAccessWidget->updateSelected("Composer"); - if (d->shortcutAccessVisible) - this->hideShortcutAccess(); - - d->screenshotButton->setEnabled(false); - d->adjustSizeButton->setEnabled(false); - d->stack->setCurrentWidget(d->composerArea); + d->quickAccessButton->setText(tr("Workspace: Composer")); + d->quickAccessButton->setMinimumWidth(170); + if (d->quickAccessWidget->isVisible()) + { + this->hideQuickAccess(); + } + if (d->shortcutAccessVisible) + { + this->hideShortcutAccess(); + } + d->screenshotButton->setEnabled(false); + d->adjustSizeButton->setEnabled(false); + d->stack->setCurrentWidget(d->composerArea); + } } void medMainWindow::showWorkspace(QString workspace) @@ -678,7 +690,7 @@ void medMainWindow::showWorkspace(QString workspace) if (!d->workspaceArea->setCurrentWorkspace(workspace)) { QString message = QString("Cannot open workspace ") + details->name; - medMessageController::instance()->showError(message, 3000); + medMessageController::instance().showError(message, 3000); switchToHomepageArea(); } @@ -766,7 +778,7 @@ void medMainWindow::hideShortcutAccess() int medMainWindow::saveModifiedAndOrValidateClosing() { - QList indexes = medDatabaseNonPersistentController::instance()->availableItems(); + QList indexes = medDatabaseNonPersistentController::instance().availableItems(); if(indexes.isEmpty()) { @@ -872,7 +884,7 @@ void medMainWindow::closeEvent(QCloseEvent *event) { // send cancel request to all running jobs, then wait for them // Note: most Jobs don't have the cancel method implemented, so this will be effectively the same as waitfordone. - medJobManagerL::instance()->dispatchGlobalCancelEvent(); + medJobManagerL::instance().dispatchGlobalCancelEvent(); } QThreadPool::globalInstance()->waitForDone(); } diff --git a/src/app/medInria/medPacsDataSource.cpp b/src/app/medInria/medPacsDataSource.cpp index 4d80cb5dbb..eb99be7546 100644 --- a/src/app/medInria/medPacsDataSource.cpp +++ b/src/app/medInria/medPacsDataSource.cpp @@ -105,6 +105,6 @@ void medPacsDataSource::onPacsMove( const QVector& cmdList) { medPacsMover* mover = new medPacsMover(cmdList); connect(mover, SIGNAL(import(QString)), this, SIGNAL(dataReceived(QString))); - medJobManagerL::instance()->registerJobItem(mover, tr("Moving")); + medJobManagerL::instance().registerJobItem(mover, tr("Moving")); QThreadPool::globalInstance()->start(mover); } diff --git a/src/app/medInria/medPluginWidget.cpp b/src/app/medInria/medPluginWidget.cpp index 1d2c239ade..6137e56062 100644 --- a/src/app/medInria/medPluginWidget.cpp +++ b/src/app/medInria/medPluginWidget.cpp @@ -46,9 +46,9 @@ void medPluginWidgetPrivate::resetPluginsTree() pluginsTree->clear(); //get the list of plugins - medPluginManager* mpm = medPluginManager::instance(); + medPluginManager &mpm = medPluginManager::instance(); - for(dtkPlugin* plugin : mpm->plugins()) + for(dtkPlugin* plugin : mpm.plugins()) { QTreeWidgetItem * item = new QTreeWidgetItem(pluginsTree); item->setText(0,plugin->name()); @@ -146,9 +146,9 @@ void medPluginWidgetPrivate::resetFailedPluginsTree() errorTree->clear(); //get the list of plugins - medPluginManager* mpm = medPluginManager::instance(); + medPluginManager &mpm = medPluginManager::instance(); - for(QString error : mpm->loadErrors()) + for(QString error : mpm.loadErrors()) { QTreeWidgetItem * item = new QTreeWidgetItem(errorTree); item->setText(0,error); @@ -240,7 +240,7 @@ void medPluginWidget::onPluginTreeItemActivated(QTreeWidgetItem *item, int colum { Q_UNUSED (column); QDialog * dial = new QDialog(this); - dtkPlugin * plugin = medPluginManager::instance()->plugin(item->text(0)); + dtkPlugin * plugin = medPluginManager::instance().plugin(item->text(0)); QString windowTitle = qApp->applicationName()+tr(": about "); windowTitle += plugin->name(); dial->setWindowTitle(windowTitle); diff --git a/src/app/medInria/medQuickAccessMenu.cpp b/src/app/medInria/medQuickAccessMenu.cpp index 94a1c3eaba..a651edd4cf 100644 --- a/src/app/medInria/medQuickAccessMenu.cpp +++ b/src/app/medInria/medQuickAccessMenu.cpp @@ -29,7 +29,7 @@ int retrieveDefaultWorkSpace() bool bMatch = false; medWorkspaceFactory::Details *poDetail = nullptr; QList oListOfWorkspaceDetails = medWorkspaceFactory::instance()->workspaceDetailsSortedByName(true); - QVariant oStartupWorkspace = medSettingsManager::instance()->value("startup", "default_starting_area"); + QVariant oStartupWorkspace = medSettingsManager::instance().value("startup", "default_starting_area"); if (oStartupWorkspace.toString() == "Search") { diff --git a/src/app/medInria/medSaveModifiedDialog.cpp b/src/app/medInria/medSaveModifiedDialog.cpp index 46991a1856..86e7e0eac1 100644 --- a/src/app/medInria/medSaveModifiedDialog.cpp +++ b/src/app/medInria/medSaveModifiedDialog.cpp @@ -97,7 +97,7 @@ medSaveModifiedDialog::medSaveModifiedDialog(QWidget *parent) : QDialog(parent), layout->addWidget(d->treeWidget); layout->addLayout(hlayout); - for(medDatabaseNonPersistentItem *item : medDatabaseNonPersistentController::instance()->items()) + for(medDatabaseNonPersistentItem *item : medDatabaseNonPersistentController::instance().items()) { if ((item->studyName() != "") && (item->seriesName() != "")) new medSaveModifiedDialogCheckListItem(d->treeWidget->invisibleRootItem(), item->index(), item->name(), item->studyName(), item->seriesName(), item->file(), item->thumb()); @@ -109,7 +109,7 @@ medSaveModifiedDialog::medSaveModifiedDialog(QWidget *parent) : QDialog(parent), connect (d->cancelButton,SIGNAL(clicked()), this, SLOT(reject())); connect (d->quitButton,SIGNAL(clicked()), this, SLOT(accept())); - connect (medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)),this, SLOT(updateCounter())); + connect (&medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)),this, SLOT(updateCounter())); setModal(true); } @@ -136,10 +136,10 @@ void medSaveModifiedDialog::saveAndQuit() } } - medDataManager * mdm = medDataManager::instance(); + medDataManager &mdm = medDataManager::instance(); for(medDataIndex index : list) { - mdm->makePersistent(index); + mdm.makePersistent(index); } if (d->counter != 0) @@ -167,7 +167,7 @@ void medSaveModifiedDialog::onUpdateTree() { d->treeWidget->clear(); - for(medDatabaseNonPersistentItem *item : medDatabaseNonPersistentController::instance()->items()) + for(medDatabaseNonPersistentItem *item : medDatabaseNonPersistentController::instance().items()) { d->treeWidget->insertTopLevelItem(0,new medSaveModifiedDialogCheckListItem(d->treeWidget->invisibleRootItem(), item->index(), item->name(), item->studyName(), diff --git a/src/app/medInria/medSplashScreen.cpp b/src/app/medInria/medSplashScreen.cpp index 3b2feac381..aebb682cc9 100644 --- a/src/app/medInria/medSplashScreen.cpp +++ b/src/app/medInria/medSplashScreen.cpp @@ -42,10 +42,8 @@ medSplashScreen::medSplashScreen(const QPixmap& thePixmap) QRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height()); // Get back the previous screen used to display the application - medSettingsManager *manager = medSettingsManager::instance(); int currentScreen = 0; - - QVariant currentScreenQV = manager->value("medMainWindow", "currentScreen"); + QVariant currentScreenQV = medSettingsManager::instance().value("medMainWindow", "currentScreen"); if (!currentScreenQV.isNull()) { currentScreen = currentScreenQV.toInt(); diff --git a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp index badff12c8f..63a4c086df 100644 --- a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp +++ b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp @@ -215,7 +215,20 @@ QImage medAbstractData::generateThumbnail(QSize size) QImage medAbstractData::generateThumbnailInGuiThread(QSize size) { - dtkSmartPointer view = medViewFactory::instance()->createView("medVtkView"); - view->addLayer(this); - return view->generateThumbnail(size); + auto view = medViewFactory::instance()->createView("medVtkView"); + + QImage thumbnail; + if(view) + { + view->addLayer(this); + thumbnail = view->generateThumbnail(size); + } + else + { + qWarning() << "medViewContainer: unable to create a medVtkView"; + } + + delete view; + + return thumbnail; } diff --git a/src/layers/legacy/medCoreLegacy/data/medMetaDataKeys.h b/src/layers/legacy/medCoreLegacy/data/medMetaDataKeys.h index f04d607aea..56b9b77830 100644 --- a/src/layers/legacy/medCoreLegacy/data/medMetaDataKeys.h +++ b/src/layers/legacy/medCoreLegacy/data/medMetaDataKeys.h @@ -30,10 +30,13 @@ namespace medMetaDataKeys public: typedef std::vector Registery; - Key(const char* name, const char* label="", + Key(QString name, QString label="", QVariant::Type type=QVariant::String, bool isEditable = true): KEY(name), LABEL(label), TYPE(type), ISEDITABLE(isEditable) { - if(QString(label)=="") LABEL=QString(name); + if(label == "") + { + LABEL = name; + } registery.push_back(this); } @@ -62,7 +65,7 @@ namespace medMetaDataKeys bool operator==(const Key& other){ return ( this->key() == other.key() ); } - static const Key* fromKeyName(const char* name) + static const Key* fromKeyName(QString name) { std::vector::iterator it; for ( it=registery.begin() ; it < registery.end(); it++ ) diff --git a/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp index afb1c931ce..a8595d8e6e 100644 --- a/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp @@ -883,14 +883,12 @@ QString medAbstractDatabaseImporter::determineFutureImageExtensionByDataType ( c QList writers = medAbstractDataFactory::instance()->writers(); - dtkSmartPointer dataWriter; - // first let's try to retrieve extension for writer information for ( int i=0; iwriterSmartPointer ( writers[i] ); + auto dataWriter = medAbstractDataFactory::instance()->writerSmartPointer ( writers[i] ); - if (dataWriter->handled().contains(medData->identifier()) ) + if (dataWriter && dataWriter->handled().contains(medData->identifier()) ) { QStringList extensions = dataWriter->supportedFileExtensions(); if(!extensions.isEmpty()) diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp index 097c8a202d..a22a0e1fa4 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp @@ -80,15 +80,15 @@ class medDataManagerPrivate // ------------------------- medDataManager ----------------------------------- -medDataManager *medDataManager::s_instance = nullptr; +std::unique_ptr medDataManager::s_instance = nullptr; -medDataManager *medDataManager::instance() +medDataManager &medDataManager::instance() { - if (!s_instance) + if(!s_instance) { - s_instance = new medDataManager(); + s_instance = std::unique_ptr(new medDataManager()); } - return s_instance; + return *s_instance.get(); } medAbstractData* medDataManager::retrieveData(const medDataIndex& index) @@ -229,7 +229,7 @@ QHash medDataManager::getPossibleWriters( delete writer; } if (possibleWriters.isEmpty()) - medMessageController::instance()->showError( + medMessageController::instance().showError( "Sorry, we have no exporter for this format."); return possibleWriters; @@ -355,7 +355,7 @@ void medDataManager::exportData(dtkSmartPointer data) } else if (!jsonPath.isEmpty()) { - jsonPath.prepend(medSettingsManager::instance()->value("database", "actual_database_location").toString()); + jsonPath.prepend(medSettingsManager::instance().value("database", "actual_database_location").toString()); if (!QDir(exportPath).exists()) { QDir().mkpath(exportPath); @@ -473,7 +473,7 @@ void medDataManager::launchExporter(medDatabaseExporter *exporter, const QString &filename) { QFileInfo info(filename); - medMessageProgress *message = medMessageController::instance()->showProgress( + medMessageProgress *message = medMessageController::instance().showProgress( "Exporting data to " + info.baseName()); connect(exporter, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); @@ -482,7 +482,7 @@ void medDataManager::launchExporter(medDatabaseExporter *exporter, connect(exporter, SIGNAL(success(QObject *)), this, SIGNAL(exportFinished())); connect(exporter, SIGNAL(failure(QObject *)), this, SIGNAL(exportFinished())); - medJobManagerL::instance()->registerJobItem(exporter); + medJobManagerL::instance().registerJobItem(exporter); QThreadPool::globalInstance()->start(exporter); } @@ -634,7 +634,7 @@ QUuid medDataManager::makePersistent(medDataIndex index) if (data->metadata(medMetaDataKeys::Toolbox.key())=="PolygonROI") { // Save json file associated to data (APHP/incepto requirement) - QString path = medSettingsManager::instance()->value("database", "actual_database_location").toString(); + QString path = medSettingsManager::instance().value("database", "actual_database_location").toString(); path += QDir::separator() + data->metadata(medMetaDataKeys::PatientID.key()) + QDir::separator() + data->metadata(medMetaDataKeys::SeriesID.key()) ; saveAttachedMetadataToFile(data, path); @@ -760,10 +760,10 @@ medDataManager::medDataManager() : d_ptr(new medDataManagerPrivate(this)) { Q_D(medDataManager); - d->nonPersDbController = medDatabaseNonPersistentController::instance(); - d->pacsController = medDataPacsController::instance(); + d->nonPersDbController = &medDatabaseNonPersistentController::instance(); + d->pacsController = &medDataPacsController::instance(); // Setting up database connection - d->dbController = medLocalDbController::instance(); + d->dbController = &medLocalDbController::instance(); if (!d->dbController->createConnection()) { @@ -788,7 +788,7 @@ medDataManager::medDataManager() : d_ptr(new medDataManagerPrivate(this)) connect(&(d->timer), SIGNAL(timeout()), this, SLOT(garbageCollect())); d->timer.start(5 * 1000); - connect(medPluginManager::instance(), SIGNAL(allPluginsLoaded()), this, + connect(&medPluginManager::instance(), SIGNAL(allPluginsLoaded()), this, SLOT(setWriterPriorities())); connect(this, SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(removeFromNonPersistent(medDataIndex, QUuid))); @@ -802,7 +802,7 @@ void medDataManager::setDatabaseLocation() // If the user configured a new location for the database in the settings // editor, we'll need to move it - medSettingsManager *mnger = medSettingsManager::instance(); + medSettingsManager *mnger = &medSettingsManager::instance(); QString newLocation = mnger->value("medDatabaseSettingsWidget", "new_database_location") .toString(); @@ -831,4 +831,7 @@ void medDataManager::setDatabaseLocation() // END OF DATABASE INITIALISATION } -medDataManager::~medDataManager() {} +medDataManager::~medDataManager() +{ + delete d_ptr; +} diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.h b/src/layers/legacy/medCoreLegacy/database/medDataManager.h index 7a908a3e87..98de11e56f 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.h +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.h @@ -21,6 +21,8 @@ #include #include +#include + class medDataManagerPrivate; class medAbstractData; class medAbstractDbController; @@ -31,7 +33,9 @@ class MEDCORELEGACY_EXPORT medDataManager : public QObject Q_OBJECT public: - static medDataManager * instance(); + ~medDataManager(); + + static medDataManager &instance(); medAbstractData* retrieveData(const medDataIndex& index); void loadData(const medDataIndex &index); @@ -96,11 +100,9 @@ private slots: private: medDataManager(); - virtual ~medDataManager(); - medAbstractData* retrieveData(const medDataIndex& index, bool readFullData); - static medDataManager * s_instance; + static std::unique_ptr s_instance; void launchExporter(medDatabaseExporter* exporter, const QString & filename); Q_DECLARE_PRIVATE(medDataManager) diff --git a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.cpp b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.cpp index 62f7230f86..c14193c016 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.cpp @@ -54,15 +54,15 @@ class medDataPacsControllerPrivate // medDataPacsController // ///////////////////////////////////////////////////////////////// -medDataPacsController *medDataPacsController::s_instance = nullptr; +std::unique_ptr medDataPacsController::s_instance = nullptr; -medDataPacsController *medDataPacsController::instance() +medDataPacsController &medDataPacsController::instance() { - if (!s_instance) + if(!s_instance) { - s_instance = new medDataPacsController(); + s_instance = std::unique_ptr(new medDataPacsController()); } - return s_instance; + return *s_instance.get(); } QList medDataPacsController::items(void) @@ -190,7 +190,7 @@ medDataIndex medDataPacsController::storeSeriesMetaDataFromPacs(medDataIndex &pt QString studyInstanceUID = stItem->studyUid(); medDataIndex index; - medDataIndex databaseIndex = medDataManager::instance()->controller()->indexForSeriesUID(patientName, studyInstanceUID, seriesInstanceUID); + medDataIndex databaseIndex = medDataManager::instance().controller()->indexForSeriesUID(patientName, studyInstanceUID, seriesInstanceUID); if (databaseIndex.isValid()) { qDebug() << "Series exists in the database, We do not want to fetch this data"; @@ -260,8 +260,8 @@ medDataIndex medDataPacsController::storeStudyMetaDataFromPacs(medDataIndex &ptI if (studyDescription != "EmptyStudy") { // check if study is already in the persistent database - medDataIndex databaseIndex = medDataManager::instance()->controller()->indexForStudyUID(patientName, studyInstanceUID); - medDatabaseNonPersistentItem *studyItem = NULL; + medDataIndex databaseIndex = medDataManager::instance().controller()->indexForStudyUID(patientName, studyInstanceUID); + medDatabaseNonPersistentItem *studyItem = nullptr; if (databaseIndex.isValid()) { @@ -331,7 +331,7 @@ medDataIndex medDataPacsController::storePatientMetaDataFromPacs(QString &patien patientName = "John Doe"; } // check if patient is already in the persistent database - medDataIndex databaseIndex = medDataManager::instance()->controller()->indexForPatient(patientName); + medDataIndex databaseIndex = medDataManager::instance().controller()->indexForPatient(patientName); medDatabaseNonPersistentItem *patientItem = nullptr; medDataIndex ptIndex; if (databaseIndex.isValid()) @@ -414,10 +414,8 @@ medDataPacsController::medDataPacsController() : d(new medDataPacsControllerPriv { } -medDataPacsController::~medDataPacsController(void) +medDataPacsController::~medDataPacsController() { - // qDeleteAll(d->items); - delete d; d = nullptr; } @@ -670,8 +668,6 @@ medDataIndex medDataPacsController::getPatientIfEmpty(const medDataIndex studyIn void medDataPacsController::remove(const medDataIndex &index) { - typedef QSet medDataIndexSet; - // Main index to remove if (d->items.contains(index)) @@ -860,7 +856,7 @@ bool medDataPacsController::loadData(const medDataIndex &index) } else { - message = medMessageController::instance()->showProgress(tr("Request data from PACS (C-Move Request)")); + message = medMessageController::instance().showProgress(tr("Request data from PACS (C-Move Request)")); message->setProgress(0); QString instanceUID, queryLevel; if (index.isValidForSeries()) @@ -880,7 +876,7 @@ bool medDataPacsController::loadData(const medDataIndex &index) } uuid = d->uuids.value(index); emit moveRequested(instanceUID, queryLevel); - connect(medDataManager::instance(), SIGNAL(updateProgress(int)), message, SLOT(setProgress(int))); + connect(&medDataManager::instance(), SIGNAL(updateProgress(int)), message, SLOT(setProgress(int))); } QTimer timer; timer.setSingleShot(true); @@ -890,7 +886,7 @@ bool medDataPacsController::loadData(const medDataIndex &index) }); timer.start(d->timeout); - connect(medDataManager::instance(), &medDataManager::moveState, &loop, [&](int status, const QString &pathOrMessage) { + connect(&medDataManager::instance(), &medDataManager::moveState, &loop, [&](int status, const QString &pathOrMessage) { switch (status) { case 0: //OK @@ -948,7 +944,7 @@ void medDataPacsController::onImportFinished(const QString &path, QEventLoop &lo } } medDataPacsImporter *importer = new medDataPacsImporter(info.absoluteFilePath(), uuid); - medMessageProgress *message = medMessageController::instance()->showProgress("Importing " + name); + medMessageProgress *message = medMessageController::instance().showProgress("Importing " + name); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int)), Qt::UniqueConnection); connect(importer, &medDataPacsImporter::progressed, &timer, [&](int p) { @@ -996,9 +992,9 @@ void medDataPacsController::onImportFinished(const QString &path, QEventLoop &lo connect(importer, SIGNAL(success(QObject *)), message, SLOT(success())); connect(importer, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(importer, SIGNAL(showError(const QString &, unsigned int)), - medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); + &medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); - medJobManagerL::instance()->registerJobItem(importer); + medJobManagerL::instance().registerJobItem(importer); QThreadPool::globalInstance()->start(importer); } diff --git a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h index 6971b11328..eb21828982 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h @@ -20,8 +20,8 @@ #include #include -// class medAbstractData; -// class medJobItemL; +#include + class medDatabaseNonPersistentItem; class medDataPacsControllerPrivate; @@ -38,7 +38,7 @@ class MEDCORELEGACY_EXPORT medDataPacsController : public medAbstractDbControlle FAILURE }; - static medDataPacsController *instance(); + static medDataPacsController &instance(); ~medDataPacsController(); QList items(); @@ -113,6 +113,7 @@ public slots: void onImportFinished(const QString &path, QEventLoop &loop, QUuid &uuid, QTimer &timer); medDataIndex getStudyIfEmpty(const medDataIndex seriesIndex); medDataIndex getPatientIfEmpty(const medDataIndex studyIndex); + medDataPacsControllerPrivate *d; - static medDataPacsController *s_instance; + static std::unique_ptr s_instance; }; diff --git a/src/layers/legacy/medCoreLegacy/database/medDataPacsImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDataPacsImporter.cpp index 6f924e9745..4b0f888aa1 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataPacsImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDataPacsImporter.cpp @@ -42,9 +42,7 @@ medDataPacsImporter::~medDataPacsImporter() */ QString medDataPacsImporter::getPatientID(QString patientName, QString birthDate) { - QPointer ctrl = medDataPacsController::instance(); - - QList items = ctrl->items(); + QList items = medDataPacsController::instance().items(); bool patientExists = false; QString patientID; @@ -78,14 +76,13 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr { medDataIndex retIndex; Q_UNUSED(pathToStoreThumbnails); - QPointer controller = - medDataPacsController::instance(); + medDataPacsController &controller = medDataPacsController::instance(); QString patientId = medMetaDataKeys::PatientID.getFirstValue(data); QString studyInstanceUID = medMetaDataKeys::StudyInstanceUID.getFirstValue(data); QString studyId = medMetaDataKeys::StudyID.getFirstValue(data); - medDatabaseNonPersistentItem *studyItem = controller->getStudyItem(patientId, studyInstanceUID); + medDatabaseNonPersistentItem *studyItem = controller.getStudyItem(patientId, studyInstanceUID); if (studyItem) { medAbstractData *medData = studyItem->data(); @@ -103,7 +100,7 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr QString columns = medMetaDataKeys::Columns.getFirstValue(data); QFileInfo info(file()); - medDatabaseNonPersistentItem *seriesItem = controller->getSeriesItem(patientId, studyInstanceUID, seriesInstanceUID); + medDatabaseNonPersistentItem *seriesItem = controller.getSeriesItem(patientId, studyInstanceUID, seriesInstanceUID); if (seriesItem) { QString patientName = medMetaDataKeys::PatientName.getFirstValue(data); @@ -117,8 +114,8 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr { // seriesItem has already a medAbstractData"; // we have to check others attributes"; - QList seriesIndexes = controller->series(medDataIndex::makeStudyIndex(controller->dataSourceId(), seriesItem->index().patientId(), seriesItem->index().studyId())); - QList items = controller->items(); + QList seriesIndexes = controller.series(medDataIndex::makeStudyIndex(controller.dataSourceId(), seriesItem->index().patientId(), seriesItem->index().studyId())); + QList items = controller.items(); bool seriesFound = false; int numberOfSeries = 0; for (medDatabaseNonPersistentItem *item : items) @@ -146,7 +143,7 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr if (!seriesFound) { // we have to create a new one"; - medDataIndex newIndex = medDataIndex::makeSeriesIndex(controller->dataSourceId(), seriesItem->index().patientId(), seriesItem->index().studyId(), controller->seriesId(true)); + medDataIndex newIndex = medDataIndex::makeSeriesIndex(controller.dataSourceId(), seriesItem->index().patientId(), seriesItem->index().studyId(), controller.seriesId(true)); medDatabaseNonPersistentItem *newItem = new medDatabaseNonPersistentItem; newItem->setName(patientName); newItem->setPatientId(patientId); @@ -169,7 +166,7 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr newItem->setRows(rows); newItem->setColumns(columns); - controller->insert(newIndex, newItem); + controller.insert(newIndex, newItem); retIndex = newIndex; } } @@ -203,11 +200,7 @@ medDataIndex medDataPacsImporter::populateDatabaseAndGenerateThumbnails(medAbstr **/ QString medDataPacsImporter::ensureUniqueSeriesName(const QString seriesName, const QString studyId) { - QPointer - controller = - medDataPacsController::instance(); - - QStringList seriesNames = controller->series(seriesName, studyId); + QStringList seriesNames = medDataPacsController::instance().series(seriesName, studyId); QString originalSeriesName = seriesName; QString newSeriesName = seriesName; @@ -225,7 +218,5 @@ QString medDataPacsImporter::ensureUniqueSeriesName(const QString seriesName, co void medDataPacsImporter::setNumberOfFilesInDirectory(int num) { - QPointer controller = - medDataPacsController::instance(); - controller->setNumberOfFilesInDir(num); + medDataPacsController::instance().setNumberOfFilesInDir(num); } diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp index 5c6411afbd..57be53140b 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp @@ -52,8 +52,7 @@ QString medDatabaseImporter::getPatientID(QString patientName, QString birthDate QString patientID = ""; //Let's see if the patient is already in the db - QSqlQuery query ( medDataManager::instance()->controller()->database() ); - // QSqlQuery query (medDataManager::instance()->controller()->database() ); + QSqlQuery query ( medDataManager::instance().controller()->database() ); query.prepare ( "SELECT patientId FROM patient WHERE name = :name AND birthdate = :birthdate" ); query.bindValue ( ":name", patientName ); query.bindValue ( ":birthdate", birthDate ); @@ -81,7 +80,7 @@ QString medDatabaseImporter::getPatientID(QString patientName, QString birthDate medDataIndex medDatabaseImporter::populateDatabaseAndGenerateThumbnails ( medAbstractData* medData, QString pathToStoreThumbnail ) { - QSqlDatabase db = medDataManager::instance()->controller()->database(); + QSqlDatabase db = medDataManager::instance().controller()->database(); generateThumbnail ( medData, pathToStoreThumbnail ); @@ -96,7 +95,7 @@ medDataIndex medDatabaseImporter::populateDatabaseAndGenerateThumbnails ( medAbs int seriesDbId = getOrCreateSeries ( medData, db, studyDbId ); - medDataIndex index = medDataIndex ( medDataManager::instance()->controller()->dataSourceId() , patientDbId, studyDbId, seriesDbId ); + medDataIndex index = medDataIndex ( medDataManager::instance().controller()->dataSourceId() , patientDbId, studyDbId, seriesDbId ); return index; } @@ -356,7 +355,7 @@ int medDatabaseImporter::getOrCreateSeries ( const medAbstractData* medData, QSq **/ QString medDatabaseImporter::ensureUniqueSeriesName(const QString seriesName, const QString studyId) { - QStringList seriesNames = medDataManager::instance()->controller()->series(seriesName, studyId); + QStringList seriesNames = medDataManager::instance().controller()->series(seriesName, studyId); QString originalSeriesName = seriesName; QString newSeriesName = seriesName; @@ -374,7 +373,7 @@ QString medDatabaseImporter::ensureUniqueSeriesName(const QString seriesName, co void medDatabaseImporter::createDBEntryForMetadataAttachedFile(medAbstractData *medData, int seriesDbId) { - QSqlDatabase db = medDataManager::instance()->controller()->database(); + QSqlDatabase db = medDataManager::instance().controller()->database(); QSqlQuery query ( db ); query.exec("SELECT COUNT(*) as cpt FROM pragma_table_info('series') WHERE name='json_meta_path'"); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseModel.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseModel.cpp index 375bc6839a..c2357c70b7 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseModel.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseModel.cpp @@ -136,9 +136,9 @@ medDatabaseModel::medDatabaseModel(QObject *parent, bool justBringStudies) : QAb populate(d->root); - connect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); - connect(medDataManager::instance(), SIGNAL(dataRemoved(medDataIndex)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); - connect(medDataManager::instance(), SIGNAL(metadataModified(medDataIndex, QString, QString)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); + connect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); + connect(&medDataManager::instance(), SIGNAL(dataRemoved(medDataIndex)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); + connect(&medDataManager::instance(), SIGNAL(metadataModified(medDataIndex, QString, QString)), this, SLOT(update(medDataIndex)), Qt::QueuedConnection); } medDatabaseModel::~medDatabaseModel(void) @@ -343,7 +343,7 @@ bool medDatabaseModel::setData(const QModelIndex &index, const QVariant &value, QString attribute = item->attribute(index.column()).toString(); //first, we try to set metadata - result = medDataManager::instance()->setMetadata(dataIndex, attribute, value.toString()); + result = medDataManager::instance().setMetadata(dataIndex, attribute, value.toString()); if (!result) { @@ -476,7 +476,7 @@ bool medDatabaseModel::dropMimeData(const QMimeData *data, Qt::DropAction action return false; for (int i = 0; i < data->urls().size(); ++i) - medDataManager::instance()->importPath(data->urls().at(i).path(), true); + medDataManager::instance().importPath(data->urls().at(i).path(), true); return true; } @@ -491,12 +491,12 @@ bool medDatabaseModel::dropMimeData(const QMimeData *data, Qt::DropAction action { if (destinationDataIndex.isValidForSeries() || destinationDataIndex.isValidForStudy()) { - newIndexList << medDataManager::instance()->moveSeries(originDataIndex, destinationDataIndex); + newIndexList << medDataManager::instance().moveSeries(originDataIndex, destinationDataIndex); } } else if (originDataIndex.isValidForStudy() && destinationDataIndex.isValidForPatient()) { - newIndexList = medDataManager::instance()->moveStudy(originDataIndex, destinationDataIndex); + newIndexList = medDataManager::instance().moveStudy(originDataIndex, destinationDataIndex); } if (!newIndexList.isEmpty() && newIndexList[0].isValid()) @@ -544,8 +544,8 @@ void medDatabaseModel::populate(medAbstractDatabaseItem *root) // first : populate non persistent database // [WARNING] : I think we can remove this loop // populate is called at the software initialization. And the non persistent database is always empty at this time. - int dataSourceId = medDatabaseNonPersistentController::instance()->dataSourceId(); - medAbstractDbController *dbc = medDataManager::instance()->controllerForDataSource(dataSourceId); + int dataSourceId = medDatabaseNonPersistentController::instance().dataSourceId(); + medAbstractDbController *dbc = medDataManager::instance().controllerForDataSource(dataSourceId); IndexList patientsForSource = dbc->patients(); // Iterate over patientIds for this data source for (const medDataIndex &patient : patientsForSource) @@ -616,8 +616,8 @@ void medDatabaseModel::populate(medAbstractDatabaseItem *root) } // for patient // 2nd : populate persistent database (psql or mysql) - dataSourceId = medDataManager::instance()->controller()->dataSourceId(); - dbc = medDataManager::instance()->controllerForDataSource(dataSourceId); + dataSourceId = medDataManager::instance().controller()->dataSourceId(); + dbc = medDataManager::instance().controllerForDataSource(dataSourceId); QHash> patientData; QHash> studyData; QHash> seriesData; @@ -726,7 +726,7 @@ void medDatabaseModel::updateSeries(const medDataIndex &dataIndex) QModelIndex index = d->medIndexMap[dataIndex]; medAbstractDatabaseItem *item = static_cast(index.internalPointer()); - medAbstractDbController *dbc = medDataManager::instance()->controllerForDataSource(dataIndex.dataSourceId()); + medAbstractDbController *dbc = medDataManager::instance().controllerForDataSource(dataIndex.dataSourceId()); if (!dbc->contains(dataIndex)) { @@ -828,7 +828,7 @@ void medDatabaseModel::updateStudy(const medDataIndex &dataIndex, bool updateChi // QModelIndex index = d->medIndexMap.value(dataIndex); medAbstractDatabaseItem *item = static_cast(index.internalPointer()); - medAbstractDbController *dbc = medDataManager::instance()->controllerForDataSource(dataIndex.dataSourceId()); + medAbstractDbController *dbc = medDataManager::instance().controllerForDataSource(dataIndex.dataSourceId()); if (!dbc->contains(dataIndex)) { @@ -943,7 +943,7 @@ void medDatabaseModel::updatePatient(const medDataIndex &dataIndex, bool updateC Q_UNUSED(updateChildren); QModelIndex index = d->medIndexMap.value(dataIndex); medAbstractDatabaseItem *item = static_cast(index.internalPointer()); - medAbstractDbController *dbc = medDataManager::instance()->controllerForDataSource(dataIndex.dataSourceId()); + medAbstractDbController *dbc = medDataManager::instance().controllerForDataSource(dataIndex.dataSourceId()); if (!dbc->contains(dataIndex)) { @@ -1047,7 +1047,7 @@ QStringList medDatabaseModel::columnNames() const QVariant medDatabaseModel::convertQStringToQVariant(QString keyName, QString value) { - const medMetaDataKeys::Key *key = medMetaDataKeys::Key::fromKeyName(keyName.toStdString().c_str()); + const medMetaDataKeys::Key *key = medMetaDataKeys::Key::fromKeyName(keyName); QVariant res; QDate date; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp index 83ad668eed..9285167d36 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp @@ -42,16 +42,18 @@ class medDatabaseNonPersistentControllerPrivate }; // ///////////////////////////////////////////////////////////////// -// medDatabaseNonPersitentController +// medDatabaseNonPersistentController // ///////////////////////////////////////////////////////////////// -medDatabaseNonPersistentController* medDatabaseNonPersistentController::s_instance = nullptr; +std::unique_ptr medDatabaseNonPersistentController::s_instance = nullptr; -medDatabaseNonPersistentController* medDatabaseNonPersistentController::instance() { - if ( ! s_instance) { - s_instance = new medDatabaseNonPersistentController(); +medDatabaseNonPersistentController &medDatabaseNonPersistentController::instance() +{ + if(!s_instance) + { + s_instance = std::unique_ptr(new medDatabaseNonPersistentController()); } - return s_instance; + return *s_instance.get(); } int medDatabaseNonPersistentController::patientId(bool increment) @@ -91,7 +93,7 @@ void medDatabaseNonPersistentController::insert(medDataIndex index, medDatabaseN void medDatabaseNonPersistentController::importPath(const QString& file,const QUuid & importUuid, bool /*indexWithoutCopying*/) { medDatabaseNonPersistentImporter * importer = new medDatabaseNonPersistentImporter(file,importUuid); - medMessageProgress * message = medMessageController::instance()->showProgress(tr("Opening file item")); + medMessageProgress * message = medMessageController::instance().showProgress(tr("Opening file item")); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); connect(importer, SIGNAL(dataImported(medDataIndex,QUuid)), this, SIGNAL(dataImported(medDataIndex,QUuid))); @@ -99,9 +101,9 @@ void medDatabaseNonPersistentController::importPath(const QString& file,const QU connect(importer, SIGNAL(success(QObject *)), message, SLOT(success())); connect(importer, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(importer, SIGNAL(showError(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); - medJobManagerL::instance()->registerJobItem(importer); + medJobManagerL::instance().registerJobItem(importer); QThreadPool::globalInstance()->start(importer); } @@ -117,7 +119,7 @@ medDatabaseNonPersistentController::medDatabaseNonPersistentController(): d(new d->seriesIndex = nonPersistentDataStartingIndex(); } -medDatabaseNonPersistentController::~medDatabaseNonPersistentController(void) +medDatabaseNonPersistentController::~medDatabaseNonPersistentController() { qDeleteAll(d->items); @@ -135,7 +137,7 @@ void medDatabaseNonPersistentController::importData(medAbstractData *data, const QUuid & callerUuid) { medDatabaseNonPersistentImporter * importer = new medDatabaseNonPersistentImporter(data,callerUuid); - medMessageProgress * message = medMessageController::instance()->showProgress("Importing data item"); + medMessageProgress * message = medMessageController::instance().showProgress("Importing data item"); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); connect(importer, SIGNAL(dataImported(medDataIndex,QUuid)), this, SIGNAL(dataImported(medDataIndex,QUuid))); @@ -143,9 +145,9 @@ void medDatabaseNonPersistentController::importData(medAbstractData *data, connect(importer, SIGNAL(success(QObject *)), message, SLOT(success())); connect(importer, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(importer, SIGNAL(showError(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); - medJobManagerL::instance()->registerJobItem(importer); + medJobManagerL::instance().registerJobItem(importer); QThreadPool::globalInstance()->start(importer); } diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h index 5ccf981ed0..6538a1df5e 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h @@ -19,6 +19,8 @@ #include #include +#include + class medAbstractData; class medDatabaseNonPersistentItem; class medDatabaseNonPersistentControllerPrivate; @@ -29,7 +31,7 @@ class MEDCORELEGACY_EXPORT medDatabaseNonPersistentController: public medAbstrac Q_OBJECT public: - static medDatabaseNonPersistentController * instance(); + static medDatabaseNonPersistentController &instance(); ~medDatabaseNonPersistentController(); int patientId(bool increment=false); @@ -88,5 +90,5 @@ public slots: medDatabaseNonPersistentController(); medDatabaseNonPersistentControllerPrivate *d; - static medDatabaseNonPersistentController* s_instance; + static std::unique_ptr s_instance; }; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp index c865bf727d..f199adfedf 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp @@ -45,10 +45,7 @@ medDatabaseNonPersistentImporter::medDatabaseNonPersistentImporter (medAbstractD */ QString medDatabaseNonPersistentImporter::getPatientID(QString patientName, QString birthDate) { - QPointer npdc = - medDatabaseNonPersistentController::instance(); - - QList items = npdc->items(); + QList items = medDatabaseNonPersistentController::instance().items(); bool patientExists = false; QString patientID; @@ -81,8 +78,8 @@ QString medDatabaseNonPersistentImporter::getPatientID(QString patientName, QStr medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbnails ( medAbstractData* data, QString pathToStoreThumbnails ) { Q_UNUSED(pathToStoreThumbnails); - QPointer npdc = - medDatabaseNonPersistentController::instance(); + medDatabaseNonPersistentController *npdc = + &medDatabaseNonPersistentController::instance(); QList items = npdc->items(); @@ -92,7 +89,7 @@ medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbn QString birthdate = medMetaDataKeys::BirthDate.getFirstValue(data); // check if patient is already in the persistent database - medDataIndex databaseIndex = medDataManager::instance()->controller()->indexForPatient(patientName); + medDataIndex databaseIndex = medDataManager::instance().controller()->indexForPatient(patientName); medDatabaseNonPersistentItem *patientItem = nullptr; if ( databaseIndex.isValid() ) @@ -148,8 +145,8 @@ medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbn if( studyName!="EmptyStudy" || seriesName!="EmptySeries" ) { // check if study is already in the persistent database - databaseIndex = medDataManager::instance()->controller()->indexForStudy ( patientName, studyName ); - medDatabaseNonPersistentItem *studyItem = NULL; + databaseIndex = medDataManager::instance().controller()->indexForStudy ( patientName, studyName ); + medDatabaseNonPersistentItem *studyItem = nullptr; if ( databaseIndex.isValid() ) { @@ -251,9 +248,7 @@ medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbn **/ QString medDatabaseNonPersistentImporter::ensureUniqueSeriesName(const QString seriesName, const QString studyId) { - QPointer npdc = - medDatabaseNonPersistentController::instance(); - QStringList seriesNames = npdc->series(seriesName, studyId); + QStringList seriesNames = medDatabaseNonPersistentController::instance().series(seriesName, studyId); QString originalSeriesName = seriesName; QString newSeriesName = seriesName; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp index 024a6a55cd..d41ca26e3e 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp @@ -164,7 +164,7 @@ void medDatabasePersistentController::importPath(const QString &file, const QUui QFileInfo info(file); qDebug() << "persistent import UUID " << importUuid; medDatabaseImporter *importer = new medDatabaseImporter(info.absoluteFilePath(), importUuid, indexWithoutCopying); - medMessageProgress *message = medMessageController::instance()->showProgress("Importing " + info.fileName()); + medMessageProgress *message = medMessageController::instance().showProgress("Importing " + info.fileName()); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); // connect(importer, SIGNAL(dataImported(medDataIndex, QUuid)), this, SIGNAL(dataImported(medDataIndex, QUuid))); @@ -176,9 +176,9 @@ void medDatabasePersistentController::importPath(const QString &file, const QUui connect(importer, SIGNAL(success(QObject *)), message, SLOT(success())); connect(importer, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(importer, SIGNAL(showError(const QString &, unsigned int)), - medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); + &medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); - medJobManagerL::instance()->registerJobItem(importer); + medJobManagerL::instance().registerJobItem(importer); QThreadPool::globalInstance()->start(importer); } @@ -189,7 +189,7 @@ void medDatabasePersistentController::importPath(const QString &file, const QUui void medDatabasePersistentController::importData(medAbstractData *data, const QUuid &importUuid) { medDatabaseImporter *importer = new medDatabaseImporter(data, importUuid); - medMessageProgress *message = medMessageController::instance()->showProgress("Saving database item"); + medMessageProgress *message = medMessageController::instance().showProgress("Saving database item"); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); connect(importer, SIGNAL(dataImported(medDataIndex, QUuid)), this, SIGNAL(dataImported(medDataIndex, QUuid))); @@ -197,15 +197,15 @@ void medDatabasePersistentController::importData(medAbstractData *data, const QU connect(importer, SIGNAL(success(QObject *)), message, SLOT(success())); connect(importer, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(importer, SIGNAL(showError(const QString &, unsigned int)), - medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); + &medMessageController::instance(), SLOT(showError(const QString &, unsigned int))); - medJobManagerL::instance()->registerJobItem(importer); + medJobManagerL::instance().registerJobItem(importer); QThreadPool::globalInstance()->start(importer); } void medDatabasePersistentController::showOpeningError(QObject *sender) { - medMessageController::instance()->showError("Opening item failed.", 3000); + medMessageController::instance().showError("Opening item failed.", 3000); } /** @@ -233,14 +233,14 @@ bool medDatabasePersistentController::moveDatabase(QString newLocation) void medDatabasePersistentController::remove(const medDataIndex &index) { medDatabaseRemover *remover = new medDatabaseRemover(index); - medMessageProgress *message = medMessageController::instance()->showProgress("Removing item"); + medMessageProgress *message = medMessageController::instance().showProgress("Removing item"); connect(remover, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); connect(remover, SIGNAL(success(QObject *)), message, SLOT(success())); connect(remover, SIGNAL(failure(QObject *)), message, SLOT(failure())); connect(remover, SIGNAL(removed(const medDataIndex &)), this, SIGNAL(dataRemoved(medDataIndex))); - medJobManagerL::instance()->registerJobItem(remover); + medJobManagerL::instance().registerJobItem(remover); QThreadPool::globalInstance()->start(remover); } @@ -271,7 +271,7 @@ bool medDatabasePersistentController::isPersistent() const medAbstractData *medDatabasePersistentController::retrieve(const medDataIndex &index, bool readFullData) const { QScopedPointer reader(new medDatabaseReader(index)); - medMessageProgress *message = medMessageController::instance()->showProgress("Opening database item"); + medMessageProgress *message = medMessageController::instance().showProgress("Opening database item"); connect(reader.data(), SIGNAL(progressed(int)), message, SLOT(setProgress(int))); connect(reader.data(), SIGNAL(success(QObject *)), message, SLOT(success())); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp index effda109c6..4695242aaa 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp @@ -63,7 +63,7 @@ medAbstractData* medDatabaseReader::run() QVariant studyDbId = d->index.studyId(); QVariant seriesDbId = d->index.seriesId(); - QSqlQuery query(medDataManager::instance()->controller()->database()); + QSqlQuery query(medDataManager::instance().controller()->database()); QString patientName, birthdate, gender, patientId; QString studyName, studyUid, studyId, studyTime, studyDate; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp index 7d661a17f7..a8f39203c3 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp @@ -40,7 +40,7 @@ const QString medDatabaseRemoverPrivate::T_SERIES = "series"; medDatabaseRemover::medDatabaseRemover ( const medDataIndex &index_ ) : medJobItemL(), d ( new medDatabaseRemoverPrivate ) { d->index = index_; - d->db = medDataManager::instance()->controller()->database(); + d->db = medDataManager::instance().controller()->database(); d->isCancelled = false; } @@ -67,7 +67,7 @@ void medDatabaseRemover::internalRun() ptQuery.prepare ( "SELECT id FROM " + d->T_PATIENT ); } - medDataManager::instance()->controller()->execQuery(ptQuery); + medDataManager::instance().controller()->execQuery(ptQuery); while ( ptQuery.next() ) { if ( d->isCancelled ) @@ -88,7 +88,7 @@ void medDatabaseRemover::internalRun() } stQuery.bindValue ( ":patient", patientDbId ); - medDataManager::instance()->controller()->execQuery(stQuery); + medDataManager::instance().controller()->execQuery(stQuery); while ( stQuery.next() ) { if ( d->isCancelled ) @@ -109,7 +109,7 @@ void medDatabaseRemover::internalRun() } seQuery.bindValue ( ":study", studyDbId ); - medDataManager::instance()->controller()->execQuery(seQuery); + medDataManager::instance().controller()->execQuery(seQuery); while ( seQuery.next() ) { @@ -173,7 +173,7 @@ void medDatabaseRemover::removeSeries ( int patientDbId, int studyDbId, int seri d->T_SERIES + " WHERE id = :series "); } query.bindValue ( ":series", seriesDbId ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); if ( query.next() ) { @@ -204,7 +204,7 @@ bool medDatabaseRemover::isStudyEmpty ( int studyDbId ) query.prepare ( "SELECT id FROM " + d->T_SERIES + " WHERE study = :study " ); query.bindValue ( ":study", studyDbId ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); return !query.next(); } @@ -216,11 +216,11 @@ void medDatabaseRemover::removeStudy ( int patientDbId, int studyDbId ) query.prepare ( "SELECT thumbnail, name, uid FROM " + d->T_STUDY + " WHERE id = :id " ); query.bindValue ( ":id", studyDbId ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); if ( query.next() ) { - medAbstractDbController * dbc = medDataManager::instance()->controllerForDataSource(d->index.dataSourceId()); + medAbstractDbController * dbc = medDataManager::instance().controllerForDataSource(d->index.dataSourceId()); if (dbc->metaData(d->index, medMetaDataKeys::StudyID.key()).toInt() == studyDbId) { removeThumbnailIfNeeded(query); @@ -238,7 +238,7 @@ bool medDatabaseRemover::isPatientEmpty ( int patientDbId ) query.prepare ( "SELECT id FROM " + d->T_STUDY + " WHERE patient = :patient " ); query.bindValue ( ":patient", patientDbId ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); return !query.next(); } @@ -251,7 +251,7 @@ void medDatabaseRemover::removePatient ( int patientDbId ) query.prepare ( "SELECT thumbnail, patientId FROM " + d->T_PATIENT + " WHERE id = :patient " ); query.bindValue ( ":patient", patientDbId ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); if ( query.next() ) { removeThumbnailIfNeeded(query); @@ -267,7 +267,7 @@ bool medDatabaseRemover::removeTableRow ( const QString &table, int id ) QSqlQuery query ( db ); query.prepare ( "DELETE FROM " + table + " WHERE id = :id" ); query.bindValue ( ":id", id ); - medDataManager::instance()->controller()->execQuery(query); + medDataManager::instance().controller()->execQuery(query); return (query.numRowsAffected()==1); } diff --git a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp index 9c38f90f67..fa0aeb5f2a 100644 --- a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp @@ -20,21 +20,26 @@ #include #include -medLocalDbController *medLocalDbController::s_instance = NULL; +std::unique_ptr medLocalDbController::s_instance = nullptr; -medLocalDbController *medLocalDbController::instance() +medLocalDbController &medLocalDbController::instance() { - if (!s_instance) + if(!s_instance) { - s_instance = new medLocalDbController(); + s_instance = std::unique_ptr(new medLocalDbController()); } - return s_instance; + return *s_instance.get(); } medLocalDbController::medLocalDbController() : medDatabasePersistentController() { } +medLocalDbController::~medLocalDbController() +{ + m_database.close(); +} + bool medLocalDbController::createConnection(void) { medStorage::mkpath(medStorage::dataLocation() + "/"); diff --git a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.h b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.h index 87d8c35eb0..5a9cc0cc3f 100644 --- a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.h +++ b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.h @@ -15,8 +15,10 @@ #include #include -#include #include +#include + +#include /** * Specialization of Concrete dbController implementation which allow to connect to local sqlite database @@ -26,7 +28,8 @@ class MEDCORELEGACY_EXPORT medLocalDbController : public medDatabasePersistentCo Q_OBJECT public: - static medLocalDbController *instance(); + ~medLocalDbController(); + static medLocalDbController &instance(); bool createConnection() override; bool closeConnection() override; @@ -48,5 +51,6 @@ class MEDCORELEGACY_EXPORT medLocalDbController : public medDatabasePersistentCo bool createSeriesTable(); bool updateFromNoVersionToVersion1(); - static medLocalDbController *s_instance; + + static std::unique_ptr s_instance; }; diff --git a/src/layers/legacy/medCoreLegacy/database/medStorage.cpp b/src/layers/legacy/medCoreLegacy/database/medStorage.cpp index a989364c19..8cad4120c4 100644 --- a/src/layers/legacy/medCoreLegacy/database/medStorage.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medStorage.cpp @@ -52,7 +52,7 @@ QString medStorage::dataLocation() } else { - vDbLoc = medSettingsManager::instance()->value("database", "actual_database_location").toString(); + vDbLoc = medSettingsManager::instance().value("database", "actual_database_location").toString(); // if the location is still not set we return the default paths if ( vDbLoc.isEmpty() ) @@ -94,7 +94,7 @@ void medStorage::setDataLocation( QString newLocation) m_dataLocation = newLocation; - medSettingsManager::instance()->setValue("database", "actual_database_location", newLocation); + medSettingsManager::instance().setValue("database", "actual_database_location", newLocation); } diff --git a/src/layers/legacy/medCoreLegacy/gui/commonWidgets/medProgressionStack.cpp b/src/layers/legacy/medCoreLegacy/gui/commonWidgets/medProgressionStack.cpp index 93900de880..0afb3eca37 100644 --- a/src/layers/legacy/medCoreLegacy/gui/commonWidgets/medProgressionStack.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/commonWidgets/medProgressionStack.cpp @@ -218,7 +218,7 @@ void medProgressionStack::addJobItem(medJobItemL* job, QString label) connect(job, SIGNAL(failure(QObject*)), this, SLOT(onFailure(QObject*)), Qt::QueuedConnection); connect(job, SIGNAL(showError(const QString&, unsigned int)), - medMessageController::instance(), SLOT(showError(const QString&, unsigned int)), Qt::QueuedConnection); + &medMessageController::instance(), SLOT(showError(const QString&, unsigned int)), Qt::QueuedConnection); connect(job, SIGNAL(cancelled(QObject*)), this,SLOT(onCancel(QObject*)), Qt::QueuedConnection); connect(this, SIGNAL(cancelRequest(QObject*)),job, SLOT(onCancel(QObject*)), Qt::QueuedConnection); diff --git a/src/layers/legacy/medCoreLegacy/gui/database/medDatabasePreview.cpp b/src/layers/legacy/medCoreLegacy/gui/database/medDatabasePreview.cpp index 85cb4a9692..15036f8a53 100644 --- a/src/layers/legacy/medCoreLegacy/gui/database/medDatabasePreview.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/database/medDatabasePreview.cpp @@ -62,7 +62,7 @@ void medDatabasePreviewStaticScene::setImage(const medDataIndex &index) d->currentDataIndex = index; QGraphicsPixmapItem *pixmap = new QGraphicsPixmapItem; - pixmap->setPixmap(medDataManager::instance()->thumbnail(index)); + pixmap->setPixmap(medDataManager::instance().thumbnail(index)); this->addItem(pixmap); if( ! this->views().isEmpty()) @@ -85,7 +85,7 @@ void medDatabasePreviewStaticScene::addImage(const medDataIndex &index) return; QGraphicsPixmapItem *pixmap = new QGraphicsPixmapItem; - pixmap->setPixmap(medDataManager::instance()->thumbnail(index)); + pixmap->setPixmap(medDataManager::instance().thumbnail(index)); this->addItem(pixmap); switch(nbItem) @@ -146,7 +146,7 @@ void medDatabasePreviewStaticScene::mouseMoveEvent(QGraphicsSceneMouseEvent *eve { if(!d->isMulti && event->buttons() == Qt::LeftButton) { - QPixmap pixmap = medDataManager::instance()->thumbnail(d->currentDataIndex); + QPixmap pixmap = medDataManager::instance().thumbnail(d->currentDataIndex); QMimeData *data = d->currentDataIndex.createMimeData(); data->setImageData(pixmap); @@ -210,6 +210,9 @@ class medDatabasePreviewPrivate QLabel *label; QString defaultText; + + QGraphicsPixmapItem *pixmap; + QGraphicsScene *scene; }; @@ -219,16 +222,16 @@ medDatabasePreview::medDatabasePreview(QWidget *parent): d(new medDatabasePrevie this->setMouseTracking(true); - QGraphicsScene *scene = new QGraphicsScene; - this->setScene(scene); + d->scene = new QGraphicsScene; + this->setScene(d->scene); d->dynamicScene = nullptr; d->staticScene = nullptr; - QGraphicsPixmapItem *pixmap = new QGraphicsPixmapItem; - pixmap->setPixmap(QPixmap(":/pixmaps/default_thumbnail.png")); - this->fitInView(pixmap, Qt::KeepAspectRatio); - scene->addItem(pixmap); + d->pixmap = new QGraphicsPixmapItem; + d->pixmap->setPixmap(QPixmap(":/pixmaps/default_thumbnail.png")); + this->fitInView(d->pixmap, Qt::KeepAspectRatio); + d->scene->addItem(d->pixmap); d->label = new QLabel(this); d->label->setAlignment(Qt::AlignCenter); @@ -245,6 +248,22 @@ medDatabasePreview::medDatabasePreview(QWidget *parent): d(new medDatabasePrevie medDatabasePreview::~medDatabasePreview() { + delete d->dynamicScene; + d->dynamicScene = nullptr; + delete d->staticScene; + d->staticScene = nullptr; + + if (d->pixmap) + { + d->pixmap->pixmap().detach(); + delete d->pixmap; + d->pixmap = nullptr; + } + + d->scene->clear(); + delete d->scene; + d->scene = nullptr; + delete d; d = nullptr; } @@ -260,7 +279,7 @@ void medDatabasePreview::resizeEvent(QResizeEvent *event) void medDatabasePreview::showSeriesPreview(const medDataIndex &index) { d->currentDataType = medDatabasePreview::SERIES; - medAbstractDbController * dbc = medDataManager::instance()->controllerForDataSource(index.dataSourceId()); + medAbstractDbController * dbc = medDataManager::instance().controllerForDataSource(index.dataSourceId()); if (d->staticScene) delete d->staticScene; @@ -280,7 +299,7 @@ void medDatabasePreview::showSeriesPreview(const medDataIndex &index) void medDatabasePreview::showStudyPreview(const medDataIndex &index) { d->currentDataType = medDatabasePreview::STUDY; - medAbstractDbController * dbc = medDataManager::instance()->controllerForDataSource(index.dataSourceId()); + medAbstractDbController * dbc = medDataManager::instance().controllerForDataSource(index.dataSourceId()); if (d->staticScene) delete d->staticScene; @@ -318,7 +337,7 @@ void medDatabasePreview::showStudyPreview(const medDataIndex &index) void medDatabasePreview::showPatientPreview(const medDataIndex &index) { d->currentDataType = medDatabasePreview::PATIENT; - medAbstractDbController * dbc = medDataManager::instance()->controllerForDataSource(index.dataSourceId()); + medAbstractDbController * dbc = medDataManager::instance().controllerForDataSource(index.dataSourceId()); if (d->staticScene) delete d->staticScene; diff --git a/src/layers/legacy/medCoreLegacy/gui/database/medDatabaseView.cpp b/src/layers/legacy/medCoreLegacy/gui/database/medDatabaseView.cpp index 9b6e6e1ef2..7a7f461527 100644 --- a/src/layers/legacy/medCoreLegacy/gui/database/medDatabaseView.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/database/medDatabaseView.cpp @@ -69,7 +69,7 @@ void NoFocusDelegate::paint(QPainter* painter, const QStyleOptionViewItem & opti // items that failed to open will have a pinkish background painter->fillRect(option.rect, QColor("#FF3333")); - medAbstractDbController * dbc = medDataManager::instance()->controllerForDataSource(item->dataIndex().dataSourceId()); + medAbstractDbController * dbc = medDataManager::instance().controllerForDataSource(item->dataIndex().dataSourceId()); if ( dbc ) { if(!dbc->isPersistent()) { @@ -262,7 +262,7 @@ void medDatabaseView::updateContextMenu(const QPoint& point) d->contextMenu->addAction(d->exportAction); d->contextMenu->addAction(d->metadataAction); d->contextMenu->addAction(d->removeAction); - if (!(medDataManager::instance()->controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) + if (!(medDataManager::instance().controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) { d->contextMenu->addAction(d->saveAction); } @@ -276,7 +276,7 @@ void medDatabaseView::updateContextMenu(const QPoint& point) d->contextMenu->addAction(d->editAction); d->editAction->setIcon(QIcon(":icons/page_edit.png")); d->contextMenu->addAction(d->removeAction); - if (!(medDataManager::instance()->controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) + if (!(medDataManager::instance().controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) { d->contextMenu->addAction(d->saveAction); } @@ -291,7 +291,7 @@ void medDatabaseView::updateContextMenu(const QPoint& point) d->contextMenu->addAction(d->editAction); d->editAction->setIcon(QIcon(":icons/user_edit.png")); d->contextMenu->addAction(d->removeAction); - if (!(medDataManager::instance()->controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) + if (!(medDataManager::instance().controllerForDataSource(item->dataIndex().dataSourceId())->isPersistent())) { d->contextMenu->addAction(d->saveAction); } @@ -450,7 +450,7 @@ void medDatabaseView::onRemoveSelectedItemRequested( void ) { // Copy the data index, because the data item may cease to be valid. medDataIndex index = item->dataIndex(); - medDataManager::instance()->removeData(index); + medDataManager::instance().removeData(index); } } } @@ -474,7 +474,7 @@ void medDatabaseView::onSaveSelectedItemRequested(void) { // Copy the data index, because the data item may cease to be valid. medDataIndex index = item->dataIndex(); - medDataManager::instance()->makePersistent(index); + medDataManager::instance().makePersistent(index); qDebug() << "onMenuSaveClicked() after storeNonPersistentSingleDataToDatabase"; qDebug() << "index" << index; } @@ -497,7 +497,7 @@ void medDatabaseView::onRetrieveDataRequested() { // Copy the data index, because the data item may cease to be valid. medDataIndex index = item->dataIndex(); - medDataManager::instance()->loadData(index); + medDataManager::instance().loadData(index); qDebug() << "onRetrieveDataRequested()"; qDebug() << "index" << index; } @@ -522,7 +522,7 @@ void medDatabaseView::onCreatePatientRequested(void) if (item) { dataSourceId = item->dataIndex().dataSourceId(); - isPersistent = medDataManager::instance()->controllerForDataSource(dataSourceId)->isPersistent(); + isPersistent = medDataManager::instance().controllerForDataSource(dataSourceId)->isPersistent(); } } @@ -566,7 +566,7 @@ void medDatabaseView::onCreatePatientRequested(void) medData->setMetaData ( medMetaDataKeys::BirthDate.key(), QStringList() << birthdate ); medData->setMetaData ( medMetaDataKeys::Gender.key(), QStringList() << gender ); - medDataManager::instance()->importData(medData, editDialog.isPersistent()); + medDataManager::instance().importData(medData, editDialog.isPersistent()); } } @@ -586,7 +586,7 @@ void medDatabaseView::onCreateStudyRequested() if (item) { - bool isPersistent = medDataManager::instance()->controllerForDataSource( + bool isPersistent = medDataManager::instance().controllerForDataSource( item->dataIndex().dataSourceId() )->isPersistent(); int patientNameIndex = item->attributes().indexOf(medMetaDataKeys::PatientName.key()); @@ -627,7 +627,7 @@ void medDatabaseView::onCreateStudyRequested() medData->setMetaData(medMetaDataKeys::StudyID.key(), QStringList() << "0"); medData->setMetaData(medMetaDataKeys::StudyInstanceUID.key(), QStringList() << ""); - medDataManager::instance()->importData(medData, editDialog.isPersistent()); + medDataManager::instance().importData(medData, editDialog.isPersistent()); } } } @@ -664,7 +664,7 @@ void medDatabaseView::onEditRequested() for(QVariant attrib : attributes) { - const medMetaDataKeys::Key* key = medMetaDataKeys::Key::fromKeyName(attrib.toString().toStdString().c_str()); + const medMetaDataKeys::Key* key = medMetaDataKeys::Key::fromKeyName(attrib.toString()); if(key) labels << key->label(); else labels << ""; @@ -688,7 +688,7 @@ void medDatabaseView::onEditRequested() { QVariant data = editDialog.value(label); QVariant variant = item->attribute(i); - medDataManager::instance()->setMetadata(index, variant.toString(), data.toString()); + medDataManager::instance().setMetadata(index, variant.toString(), data.toString()); i++; } } @@ -730,7 +730,7 @@ void medDatabaseView::onMetadataRequested(void) // Get back keys and metadata from the selected data for(const medMetaDataKeys::Key* key : medMetaDataKeys::Key::all()) { - metadata = medDataManager::instance()->getMetaData(item->dataIndex(), key->key()); + metadata = medDataManager::instance().getMetaData(item->dataIndex(), key->key()); keyList.push_back(key->key()); metadataList.push_back(metadata); diff --git a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h index ff2b71a810..5bf1c7ef4e 100644 --- a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h +++ b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h @@ -30,6 +30,7 @@ class medClutEditorVertexPrivate; class MEDCORELEGACY_EXPORT medClutEditorVertex : public QObject, public QGraphicsItem { Q_OBJECT + Q_INTERFACES(QGraphicsItem) public: medClutEditorVertex(QPointF value, QPointF coord, QColor color = Qt::white, QGraphicsItem *parent = nullptr); @@ -84,6 +85,7 @@ class medClutEditorTablePrivate; class MEDCORELEGACY_EXPORT medClutEditorTable : public QObject, public QGraphicsItem { Q_OBJECT + Q_INTERFACES(QGraphicsItem) public: medClutEditorTable(const medClutEditorTable & table); diff --git a/src/layers/legacy/medCoreLegacy/gui/medAbstractWorkspaceLegacy.cpp b/src/layers/legacy/medCoreLegacy/gui/medAbstractWorkspaceLegacy.cpp index fa52f86506..3955cae078 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medAbstractWorkspaceLegacy.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medAbstractWorkspaceLegacy.cpp @@ -93,7 +93,8 @@ medAbstractWorkspaceLegacy::medAbstractWorkspaceLegacy(QWidget *parent) d->layersToolBox = new medToolBox; d->layersToolBox->setTitle("Layer settings"); d->layersToolBox->show(); - + addToolBox(d->layersToolBox); + d->layerListToolBox = new medToolBox; d->layerListToolBox->header()->hide(); d->layersToolBox->addWidget(d->layerListToolBox); @@ -102,8 +103,6 @@ medAbstractWorkspaceLegacy::medAbstractWorkspaceLegacy(QWidget *parent) d->interactorToolBox->header()->hide(); d->layersToolBox->addWidget(d->interactorToolBox); - addToolBox(d->layersToolBox); - d->progressionStack = new medProgressionStack(); d->progressionStackToolBox = new medToolBox; d->progressionStackToolBox->header()->hide(); @@ -232,7 +231,7 @@ void medAbstractWorkspaceLegacy::updateNavigatorsToolBox() for(QUuid uuid : d->viewContainerStack->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); // update the toolbox when the content of the view change view = container->view(); @@ -274,7 +273,7 @@ void medAbstractWorkspaceLegacy::updateMouseInteractionToolBox() QStringList viewType; for(QUuid uuid : d->viewContainerStack->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); // update the toolbox when the content of the view change medAbstractView* view = container->view(); // add nothing if the view is empty @@ -315,7 +314,7 @@ void medAbstractWorkspaceLegacy::handleLayerSelectionChange() void medAbstractWorkspaceLegacy::resetCameraOnSelectedLayer(QListWidgetItem *item) { QUuid uuid = d->containerForLayerWidgetsItem.value(item); - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); if (container) { medAbstractLayeredView *layeredView = dynamic_cast(container->view()); @@ -348,7 +347,7 @@ void medAbstractWorkspaceLegacy::updateLayersToolBox() for(QUuid uuid : d->viewContainerStack->containersSelected()) { // fill the layer widget - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); medAbstractLayeredView* layeredView = dynamic_cast(container->view()); if(layeredView) { @@ -394,7 +393,7 @@ void medAbstractWorkspaceLegacy::updateLayersToolBox() layout->addWidget(poolIndicator); d->poolIndicators.insert(d->layerListWidget->count(), poolIndicator); - QList layerGroups = medParameterGroupManagerL::instance()->layerGroups(layeredView, data); + QList layerGroups = medParameterGroupManagerL::instance().layerGroups(layeredView, data); for(medLayerParameterGroupL *layerGroup : layerGroups) { poolIndicator->addColorIndicator(layerGroup->color(), layerGroup->name()); @@ -455,7 +454,7 @@ void medAbstractWorkspaceLegacy::changeCurrentLayer(int row) { QListWidgetItem* item = d->layerListWidget->item(row); QUuid uuid = d->containerForLayerWidgetsItem.value(item); - medViewContainer* container = medViewContainerManager::instance()->container(uuid); + medViewContainer* container = medViewContainerManager::instance().container(uuid); if(!container) return; @@ -476,7 +475,7 @@ void medAbstractWorkspaceLegacy::changeCurrentLayer(int row) void medAbstractWorkspaceLegacy::updateInteractorsToolBox() { - medViewContainerManager *containerMng = medViewContainerManager::instance(); + medViewContainerManager *containerMng = &medViewContainerManager::instance(); for(QUuid uuid : d->viewContainerStack->containersSelected()) { containerMng->container(uuid)->highlight(); @@ -571,22 +570,22 @@ void medAbstractWorkspaceLegacy::removeLayer() int layer = item->data(Qt::UserRole).toInt(); - medAbstractLayeredView *layerView = dynamic_cast(medViewContainerManager::instance()->container(containerUuid)->view()); + medAbstractLayeredView *layerView = dynamic_cast(medViewContainerManager::instance().container(containerUuid)->view()); if(!layerView) return; layerView->removeLayer(layer); if (layerView->layersCount() == 0) { - if (medViewContainerManager::instance()->container(containerUuid)->closingMode() + if (medViewContainerManager::instance().container(containerUuid)->closingMode() == medViewContainer::CLOSE_CONTAINER) { - medViewContainerManager::instance()->container(containerUuid)->checkIfStillDeserveToLiveContainer(); + medViewContainerManager::instance().container(containerUuid)->checkIfStillDeserveToLiveContainer(); } else { // For containers that we want to keep even if there are no views/data in it, as in Filtering - medViewContainerManager::instance()->container(containerUuid)->removeView(); + medViewContainerManager::instance().container(containerUuid)->removeView(); } } @@ -596,7 +595,7 @@ void medAbstractWorkspaceLegacy::removeLayer() void medAbstractWorkspaceLegacy::buildTemporaryPool() { - medViewContainerManager *containerMng = medViewContainerManager::instance(); + medViewContainerManager *containerMng = &medViewContainerManager::instance(); d->temporaryPoolForInteractors->clear(); for(QListWidgetItem* item : d->layerListWidget->selectedItems()) @@ -621,20 +620,20 @@ void medAbstractWorkspaceLegacy::open(const medDataIndex &index) if(containersSelected.size() != 1) return; - medViewContainer *container = medViewContainerManager::instance()->container(containersSelected.first()); + medViewContainer *container = medViewContainerManager::instance().container(containersSelected.first()); if(index.isValidForSeries()) { - container->addData(medDataManager::instance()->retrieveData(index)); + container->addData(medDataManager::instance().retrieveData(index)); } else if(index.isValidForStudy()) { // We get the list of each series from that study index, and open it - QList seriesList = medDataManager::instance()->getSeriesListFromStudy(index); + QList seriesList = medDataManager::instance().getSeriesListFromStudy(index); if (seriesList.count() > 0) { for(medDataIndex seriesIndex : seriesList) { - container->addData(medDataManager::instance()->retrieveData(seriesIndex)); + container->addData(medDataManager::instance().retrieveData(seriesIndex)); } } } @@ -695,7 +694,7 @@ QWidget* medAbstractWorkspaceLegacy::buildViewLinkMenu() for(QUuid uuid : d->viewContainerStack->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); view = container->view(); // add nothing if the view is empty @@ -724,7 +723,7 @@ QWidget* medAbstractWorkspaceLegacy::buildViewLinkMenu() QStringList selectedGroups; QStringList partiallySelectedGroups; - auto viewGroupsList = medParameterGroupManagerL::instance()->viewGroups(this->identifier()); + auto viewGroupsList = medParameterGroupManagerL::instance().viewGroups(this->identifier()); for(medViewParameterGroupL *viewGroup : viewGroupsList) { abstractGroupsList.append(viewGroup); @@ -761,7 +760,7 @@ QWidget* medAbstractWorkspaceLegacy::buildLayerLinkMenu(QList QStringList paramNames; QList layersParams; QMultiHash layers; - medViewContainerManager *containerMng = medViewContainerManager::instance(); + medViewContainerManager *containerMng = &medViewContainerManager::instance(); for(QListWidgetItem *item : selectedlayers) { @@ -808,7 +807,7 @@ QWidget* medAbstractWorkspaceLegacy::buildLayerLinkMenu(QList QStringList selectedGroups; QStringList partiallySelectedGroups; - auto layerGroupsList = medParameterGroupManagerL::instance()->layerGroups(this->identifier()); + auto layerGroupsList = medParameterGroupManagerL::instance().layerGroups(this->identifier()); for(medLayerParameterGroupL *layerGroup : layerGroupsList) { abstractGroupsList.append(layerGroup); @@ -850,11 +849,11 @@ void medAbstractWorkspaceLegacy::addViewstoGroup(QString group) { medAbstractView* view = nullptr; - medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->viewGroup(group, this->identifier()); + medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance().viewGroup(group, this->identifier()); for(QUuid uuid : d->viewContainerStack->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); view = container->view(); if(view) @@ -868,11 +867,11 @@ void medAbstractWorkspaceLegacy::removeViewsFromGroup(QString group) { medAbstractView* view = nullptr; - medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->viewGroup(group, this->identifier()); + medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance().viewGroup(group, this->identifier()); for(QUuid uuid : d->viewContainerStack->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); view = container->view(); if(view) @@ -884,8 +883,8 @@ void medAbstractWorkspaceLegacy::removeViewsFromGroup(QString group) void medAbstractWorkspaceLegacy::addLayerstoGroup(QString group) { - medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->layerGroup(group, this->identifier()); - medViewContainerManager *containerMng = medViewContainerManager::instance(); + medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance().layerGroup(group, this->identifier()); + medViewContainerManager *containerMng = &medViewContainerManager::instance(); for(QListWidgetItem *item : d->selectedLayers) { @@ -909,8 +908,8 @@ void medAbstractWorkspaceLegacy::addLayerstoGroup(QString group) void medAbstractWorkspaceLegacy::removeLayersFromGroup(QString group) { - medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->layerGroup(group, this->identifier()); - medViewContainerManager *containerMng = medViewContainerManager::instance(); + medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance().layerGroup(group, this->identifier()); + medViewContainerManager *containerMng = &medViewContainerManager::instance(); for(QListWidgetItem *item : d->selectedLayers) { @@ -931,12 +930,12 @@ void medAbstractWorkspaceLegacy::removeLayersFromGroup(QString group) void medAbstractWorkspaceLegacy::removeViewGroup(QString group) { - delete medParameterGroupManagerL::instance()->viewGroup(group, this->identifier()); + delete medParameterGroupManagerL::instance().viewGroup(group, this->identifier()); } void medAbstractWorkspaceLegacy::removeLayerGroup(QString group) { - delete medParameterGroupManagerL::instance()->layerGroup(group, this->identifier()); + delete medParameterGroupManagerL::instance().layerGroup(group, this->identifier()); updateLayersToolBox(); } @@ -970,7 +969,7 @@ void medAbstractWorkspaceLegacy::addLayerGroup(medLayerParameterGroupL * group) void medAbstractWorkspaceLegacy::setViewGroups(QList groups) { Q_UNUSED(groups); - for(medViewParameterGroupL* group : medParameterGroupManagerL::instance()->viewGroups(this->identifier())) + for(medViewParameterGroupL* group : medParameterGroupManagerL::instance().viewGroups(this->identifier())) { addViewGroup(group); } @@ -996,13 +995,13 @@ void medAbstractWorkspaceLegacy::setInitialGroups() void medAbstractWorkspaceLegacy::changeViewGroupColor(QString group, QColor color) { - medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->viewGroup(group, this->identifier()); + medViewParameterGroupL *paramGroup = medParameterGroupManagerL::instance().viewGroup(group, this->identifier()); paramGroup->setColor(color); } void medAbstractWorkspaceLegacy::changeLayerGroupColor(QString group, QColor color) { - medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance()->layerGroup(group, this->identifier()); + medLayerParameterGroupL *paramGroup = medParameterGroupManagerL::instance().layerGroup(group, this->identifier()); paramGroup->setColor(color); } diff --git a/src/layers/legacy/medCoreLegacy/gui/medLinkMenu.cpp b/src/layers/legacy/medCoreLegacy/gui/medLinkMenu.cpp index a0f2a5a844..1f13404875 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medLinkMenu.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medLinkMenu.cpp @@ -605,7 +605,7 @@ void medLinkMenu::saveAsPreset() } group = newgroup; - medSettingsManager::instance()->setValue("GroupPresets", group, params); + medSettingsManager::instance().setValue("GroupPresets", group, params); d->presets.insert(group, params); addPresetItem(group); @@ -620,10 +620,10 @@ void medLinkMenu::saveAsPreset() void medLinkMenu::loadPreset() { - QStringList presets = medSettingsManager::instance()->keys("GroupPresets"); + QStringList presets = medSettingsManager::instance().keys("GroupPresets"); for(QString preset : presets) { - QStringList params = medSettingsManager::instance()->value("GroupPresets", preset).toStringList(); + QStringList params = medSettingsManager::instance().value("GroupPresets", preset).toStringList(); bool ok = true; for(QString param : params) { @@ -683,8 +683,8 @@ void medLinkMenu::applyPreset(QListWidgetItem* item) presetParams = d->presets[key]; d->presets.remove(key); d->presets.insert(preset, presetParams); - medSettingsManager::instance()->remove("GroupPresets", key); - medSettingsManager::instance()->setValue("GroupPresets", preset, presetParams); + medSettingsManager::instance().remove("GroupPresets", key); + medSettingsManager::instance().setValue("GroupPresets", preset, presetParams); } } } @@ -779,7 +779,7 @@ void medLinkMenu::deletePreset() d->presetList->model()->removeRow(d->presetList->row(itemToRemove)); d->presets.remove(preset); - medSettingsManager::instance()->remove("GroupPresets", preset); + medSettingsManager::instance().remove("GroupPresets", preset); } d->presetList->blockSignals(false); diff --git a/src/layers/legacy/medCoreLegacy/gui/medSelectorWorkspace.cpp b/src/layers/legacy/medCoreLegacy/gui/medSelectorWorkspace.cpp index 1820622986..0fa1438a73 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medSelectorWorkspace.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medSelectorWorkspace.cpp @@ -57,5 +57,5 @@ medSelectorToolBox* medSelectorWorkspace::selectorToolBox() void medSelectorWorkspace::importProcessOutput() { medAbstractData *output = selectorToolBox()->currentToolBox()->processOutput(); - medDataManager::instance()->importData(output); + medDataManager::instance().importData(output); } diff --git a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp index 37384d952f..26afd6c2d7 100644 --- a/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/medTabbedViewContainers.cpp @@ -66,7 +66,7 @@ medTabbedViewContainers::medTabbedViewContainers(medAbstractWorkspaceLegacy* own connect(tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(movedTabs(int,int))); // Connect group of view handling - connect(medViewContainerManager::instance(), SIGNAL(containerAboutToBeDestroyed(QUuid)), this, SLOT(removeContainerFromSelection(QUuid))); + connect(&medViewContainerManager::instance(), SIGNAL(containerAboutToBeDestroyed(QUuid)), this, SLOT(removeContainerFromSelection(QUuid))); connect(this, SIGNAL(containersSelectedChanged()), this, SLOT(buildTemporaryPool())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(connectContainerSelectedForCurrentTab())); @@ -126,7 +126,7 @@ medViewContainer* medTabbedViewContainers::getFirstSelectedContainer() if (listUuid.count() > 0) { // only keep the first selected container - view = medViewContainerManager::instance()->container(listUuid[0]); + view = medViewContainerManager::instance().container(listUuid[0]); } return view; @@ -318,7 +318,7 @@ void medTabbedViewContainers::disconnectTabFromSplitter(int index) void medTabbedViewContainers::connectContainer(QUuid container) { - medViewContainer* cont = medViewContainerManager::instance()->container(container); + medViewContainer* cont = medViewContainerManager::instance().container(container); connect(cont, SIGNAL(containerSelected(QUuid)), this, SLOT(addContainerToSelection(QUuid)), Qt::UniqueConnection); connect(cont, SIGNAL(containerUnSelected(QUuid)), this, SLOT(removeContainerFromSelection(QUuid)), Qt::UniqueConnection); connect(cont, SIGNAL(maximized(QUuid,bool)), this, SLOT(minimizeOtherContainers(QUuid,bool))); @@ -335,14 +335,14 @@ void medTabbedViewContainers::addContainerToSelection(QUuid container) { for(QUuid uuid : containersSelected) { - medViewContainerManager::instance()->container(uuid)->setSelected(false); + medViewContainerManager::instance().container(uuid)->setSelected(false); } } // containersSelected may have been modified in removeContainerFromSelection so // we have to get it back again containersSelected = d->containerSelectedForTabIndex.value(this->currentIndex()); containersSelected.append(container); - medViewContainer *newSelectedContainer = medViewContainerManager::instance()->container(container); + medViewContainer *newSelectedContainer = medViewContainerManager::instance().container(container); d->containerSelectedForTabIndex.insert(this->currentIndex(), containersSelected); connect(newSelectedContainer, SIGNAL(viewRemoved()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); @@ -359,7 +359,7 @@ void medTabbedViewContainers::removeContainerFromSelection(QUuid container) if(containersSelected.removeOne(container)) { d->containerSelectedForTabIndex.insert(tabIndex, containersSelected); - medViewContainer *unSelectedContainer = medViewContainerManager::instance()->container(container); + medViewContainer *unSelectedContainer = medViewContainerManager::instance().container(container); this->disconnect(unSelectedContainer, SIGNAL(viewRemoved()), this, 0); this->disconnect(unSelectedContainer, SIGNAL(viewContentChanged()), this, 0); emit containersSelectedChanged(); @@ -373,7 +373,7 @@ void medTabbedViewContainers::buildTemporaryPool() d->pool->clear(); for(QUuid uuid : this->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); if(!container->view()) { @@ -396,7 +396,7 @@ void medTabbedViewContainers::connectContainerSelectedForCurrentTab() { for(QUuid uuid : containersSelected) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); connect(container, SIGNAL(viewRemoved()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); connect(container, SIGNAL(viewContentChanged()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); } @@ -405,7 +405,7 @@ void medTabbedViewContainers::connectContainerSelectedForCurrentTab() { for(QUuid uuid : containersSelected) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); this->disconnect(container, SIGNAL(viewRemoved()), this, 0); this->disconnect(container, SIGNAL(viewContentChanged()), this, 0); } @@ -426,7 +426,7 @@ void medTabbedViewContainers::minimizeOtherContainers(QUuid containerMaximized, if(maximized) { - medViewContainer *container = medViewContainerManager::instance()->container(containerMaximized); + medViewContainer *container = medViewContainerManager::instance().container(containerMaximized); QWidget *parent = container->parentWidget(); diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp index 71c9fdd19e..c92206cb5b 100644 --- a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDatabaseSettingsWidget.cpp @@ -84,7 +84,6 @@ void medDatabaseSettingsWidget::read() bool medDatabaseSettingsWidget::write() { - medSettingsManager * mnger = medSettingsManager::instance(); - mnger->setValue(this->identifier(),"new_database_location", d->dbPath->text()); + medSettingsManager::instance().setValue(this->identifier(),"new_database_location", d->dbPath->text()); return true; } diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medSettingsEditor.cpp b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medSettingsEditor.cpp index ada2bf9d80..807c4d66e2 100644 --- a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medSettingsEditor.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medSettingsEditor.cpp @@ -206,9 +206,9 @@ void medSettingsEditor::initialize() // connections connect(this,SIGNAL(showError(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showError (const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showError (const QString&,unsigned int))); connect(this,SIGNAL(showInfo(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showInfo (const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showInfo (const QString&,unsigned int))); d->isInitialized = true; } diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medStartupSettingsWidget.cpp b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medStartupSettingsWidget.cpp index d8ecfdb0d2..93b8ce7fdd 100644 --- a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medStartupSettingsWidget.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medStartupSettingsWidget.cpp @@ -85,7 +85,7 @@ bool medStartupSettingsWidget::validate() void medStartupSettingsWidget::read() { - medSettingsManager *mnger = medSettingsManager::instance(); + medSettingsManager *mnger = &medSettingsManager::instance(); d->startInFullScreen->setChecked(mnger->value("startup", "fullscreen").toBool()); //if nothing is configured then Homepage is the default area @@ -130,9 +130,9 @@ void medStartupSettingsWidget::read() bool medStartupSettingsWidget::write() { - medSettingsManager *mnger = medSettingsManager::instance(); - mnger->setValue("startup", "fullscreen", d->startInFullScreen->isChecked()); - mnger->setValue("startup", "default_starting_area", d->defaultStartingArea->currentText()); - mnger->setValue("startup", "default_segmentation_speciality", d->defaultSegmentationSpeciality->currentText()); + medSettingsManager &mnger = medSettingsManager::instance(); + mnger.setValue("startup", "fullscreen", d->startInFullScreen->isChecked()); + mnger.setValue("startup", "default_starting_area", d->defaultStartingArea->currentText()); + mnger.setValue("startup", "default_segmentation_speciality", d->defaultSegmentationSpeciality->currentText()); return true; } diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp index ce754240e9..4674968e6b 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp @@ -201,7 +201,7 @@ medActionsToolBox::~medActionsToolBox() **/ void medActionsToolBox::patientSelected(const medDataIndex& index) { - if( !(medDataManager::instance()->controllerForDataSource(index.dataSourceId())->isPersistent()) ) + if( !(medDataManager::instance().controllerForDataSource(index.dataSourceId())->isPersistent()) ) { updateButtons("Unsaved Patient"); } @@ -218,7 +218,7 @@ void medActionsToolBox::patientSelected(const medDataIndex& index) **/ void medActionsToolBox::studySelected(const medDataIndex& index) { - if( !(medDataManager::instance()->controllerForDataSource(index.dataSourceId())->isPersistent()) ) + if( !(medDataManager::instance().controllerForDataSource(index.dataSourceId())->isPersistent()) ) { updateButtons("Unsaved Study"); } @@ -235,7 +235,7 @@ void medActionsToolBox::studySelected(const medDataIndex& index) **/ void medActionsToolBox::seriesSelected(const medDataIndex& index) { - if( !(medDataManager::instance()->controllerForDataSource(index.dataSourceId())->isPersistent()) ) + if( !(medDataManager::instance().controllerForDataSource(index.dataSourceId())->isPersistent()) ) { updateButtons("Unsaved Series"); } diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medCompositeDataSetImporterSelectorToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medCompositeDataSetImporterSelectorToolBox.cpp index 6887854834..e357fac96b 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medCompositeDataSetImporterSelectorToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medCompositeDataSetImporterSelectorToolBox.cpp @@ -167,10 +167,9 @@ void medCompositeDataSetImporterSelectorToolBox::initialize() // connections connect(this,SIGNAL(showError(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showError (const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showError (const QString&,unsigned int))); connect(this,SIGNAL(showInfo(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showInfo (const QString&,unsigned int))); - //connect(this,SIGNAL(success()),this,SLOT(onSuccess())); + &medMessageController::instance(),SLOT(showInfo (const QString&,unsigned int))); d->isInitialized = true; } diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp index 6ade817b41..f9f7d9b4af 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medRegistrationSelectorToolBox.cpp @@ -91,9 +91,9 @@ medRegistrationSelectorToolBox::medRegistrationSelectorToolBox(QWidget *parent, //Connect Message Controller: connect(this,SIGNAL(showError(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showError(const QString&,unsigned int))); connect(this,SIGNAL(showInfo(const QString&,unsigned int)), - medMessageController::instance(),SLOT(showInfo(const QString&,unsigned int))); + &medMessageController::instance(),SLOT(showInfo(const QString&,unsigned int))); } medRegistrationSelectorToolBox::~medRegistrationSelectorToolBox(void) @@ -353,7 +353,7 @@ void medRegistrationSelectorToolBox::handleOutput(typeOfOperation type, QString output->setMetaData ( medMetaDataKeys::SeriesID.key(), generatedID ); if (type==algorithm) - medDataManager::instance()->importData(output); + medDataManager::instance().importData(output); d->movingData = output; emit movingDataRegistered(output); diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBox.cpp index ed81dcb0f3..6dc0a9d843 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBox.cpp @@ -304,7 +304,7 @@ void medToolBox::toXMLNode(QDomDocument *doc, QDomElement *currentNode) void medToolBox::displayMessageError(QString error) { qDebug() << qPrintable(name() + ": " + error); - medMessageController::instance()->showError(error, 3000); + medMessageController::instance().showError(error, 3000); } void medToolBox::handleDisplayError(int error) @@ -387,7 +387,7 @@ void medToolBox::addConnectionsAndStartJob(medJobItemL *job) getProgressionStack()->addJobItem(job, "Progress "+this->name()+":"); - medJobManagerL::instance()->registerJobItem(job); + medJobManagerL::instance().registerJobItem(job); QThreadPool::globalInstance()->start(dynamic_cast(job)); } diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp index 6faeea2cda..bbadb0393f 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp @@ -21,10 +21,8 @@ class medToolBoxHeaderPrivate QPoint titleOffset; medButton* about; static QPixmap* png; - static const QString tooltip; }; -const QString medToolBoxHeaderPrivate::tooltip = QObject::tr("About this plugin"); QPixmap * medToolBoxHeaderPrivate::png = nullptr; medToolBoxHeader::medToolBoxHeader(QWidget *parent) : QFrame(parent), d(new medToolBoxHeaderPrivate) @@ -37,7 +35,7 @@ medToolBoxHeader::medToolBoxHeader(QWidget *parent) : QFrame(parent), d(new medT layout->setMargin(0); d->about = new medButton(this,*(d->png), - medToolBoxHeaderPrivate::tooltip); + "About this plugin"); layout->addStretch(); layout->addWidget(d->about); d->about->hide(); @@ -45,8 +43,9 @@ medToolBoxHeader::medToolBoxHeader(QWidget *parent) : QFrame(parent), d(new medT medToolBoxHeader::~medToolBoxHeader(void) { - delete d; + delete d->png; + delete d; d = nullptr; } diff --git a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainer.cpp b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainer.cpp index b9baa15c36..5949575631 100644 --- a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainer.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainer.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -106,7 +105,7 @@ medViewContainer::medViewContainer(medViewContainerSplitter *parent): QFrame(par d->parent = parent; d->uuid = QUuid::createUuid(); - medViewContainerManager::instance()->registerNewContainer(this); + medViewContainerManager::instance().registerNewContainer(this); d->view = nullptr; d->viewToolbar = nullptr; @@ -262,7 +261,7 @@ medViewContainer::medViewContainer(medViewContainerSplitter *parent): QFrame(par medViewContainer::~medViewContainer() { removeInternView(); - medViewContainerManager::instance()->unregisterContainer(this); + medViewContainerManager::instance().unregisterContainer(this); delete d; d = nullptr; @@ -802,12 +801,12 @@ bool medViewContainer::dropEventFromFile(QDropEvent * event) { pathList.append(urlList.at(i).toLocalFile()); } - connect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(droppedDataReady(medDataIndex, QUuid)), Qt::UniqueConnection); + connect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(droppedDataReady(medDataIndex, QUuid)), Qt::UniqueConnection); d->oQuuidVect.resize(pathList.size()); for (int i = 0; i < pathList.size(); ++i) { d->oQuuidVect[i].second = false; - d->oQuuidVect[i].first = medDataManager::instance()->importPath(pathList[i], true, false); + d->oQuuidVect[i].first = medDataManager::instance().importPath(pathList[i], true, false); } event->acceptProposedAction(); bRes = true; @@ -862,12 +861,12 @@ void medViewContainer::addData(medDataIndex index) if (index.isValidForSeries()) { - this->addData(medDataManager::instance()->retrieveData(index)); + this->addData(medDataManager::instance().retrieveData(index)); } else if (index.isValidForStudy()) { // We get the list of each series from that study index, and open it - QList seriesList = medDataManager::instance()->getSeriesListFromStudy(index); + QList seriesList = medDataManager::instance().getSeriesListFromStudy(index); if (seriesList.count() > 0) { bool userIsOk = true; @@ -881,7 +880,7 @@ void medViewContainer::addData(medDataIndex index) { for(medDataIndex seriesIndex : seriesList) { - this->addData(medDataManager::instance()->retrieveData(seriesIndex)); + this->addData(medDataManager::instance().retrieveData(seriesIndex)); } } } @@ -949,13 +948,13 @@ void medViewContainer::openFromSystem() dialog.setFileMode(QFileDialog::ExistingFile); dialog.setViewMode(QFileDialog::Detail); - dialog.restoreState(medSettingsManager::instance()->value("state", "openFromSystem").toByteArray()); - dialog.restoreGeometry(medSettingsManager::instance()->value("geometry", "openFromSystem").toByteArray()); + dialog.restoreState(medSettingsManager::instance().value("state", "openFromSystem").toByteArray()); + dialog.restoreGeometry(medSettingsManager::instance().value("geometry", "openFromSystem").toByteArray()); if(dialog.exec()) path = dialog.selectedFiles().first(); - medSettingsManager::instance()->setValue("state", "openFromSystem", dialog.saveState()); - medSettingsManager::instance()->setValue("geometry", "openFromSystem", dialog.saveGeometry()); + medSettingsManager::instance().setValue("state", "openFromSystem", dialog.saveState()); + medSettingsManager::instance().setValue("geometry", "openFromSystem", dialog.saveGeometry()); if (!path.isEmpty()) { @@ -965,9 +964,9 @@ void medViewContainer::openFromSystem() void medViewContainer::open(const QString & path) { - QUuid uuid = medDataManager::instance()->importPath(path, false); + QUuid uuid = medDataManager::instance().importPath(path, false); d->expectedUuids.append(uuid); - connect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), + connect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), this, SLOT(open_waitForImportedSignal(medDataIndex,QUuid))); QEventLoop loop; @@ -975,7 +974,7 @@ void medViewContainer::open(const QString & path) loop.exec(); // save last directory opened in settings. - medSettingsManager::instance()->setValue("path", "medViewContainer", path); + medSettingsManager::instance().setValue("path", "medViewContainer", path); } void medViewContainer::open_waitForImportedSignal(medDataIndex index, QUuid uuid) @@ -983,7 +982,7 @@ void medViewContainer::open_waitForImportedSignal(medDataIndex index, QUuid uuid if(d->expectedUuids.contains(uuid)) { d->expectedUuids.removeAll(uuid); - disconnect(medDataManager::instance(),SIGNAL(dataImported(medDataIndex, QUuid)), + disconnect(&medDataManager::instance(),SIGNAL(dataImported(medDataIndex, QUuid)), this,SLOT(open_waitForImportedSignal(medDataIndex, QUuid))); if (index.isValid()) { @@ -1070,7 +1069,7 @@ void medViewContainer::droppedDataReady(medDataIndex index, QUuid uuid) } if (bDone4All) { - disconnect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(droppedDataReady(medDataIndex, QUuid))); + disconnect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex, QUuid)), this, SLOT(droppedDataReady(medDataIndex, QUuid))); d->oQuuidVect.clear(); } } @@ -1304,5 +1303,5 @@ void medViewContainer::printInConsole(QString message) void medViewContainer::displayMessageError(QString message) { printInConsole(message); - medMessageController::instance()->showError(message, 3000); + medMessageController::instance().showError(message, 3000); } diff --git a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.cpp b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.cpp index 75c9db5ced..b7415766a9 100644 --- a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.cpp @@ -25,19 +25,22 @@ class medViewContainerManagerPrivate QHash containers; }; -medViewContainerManager *medViewContainerManager::instance(void) +std::unique_ptr medViewContainerManager::s_instance = nullptr; + +medViewContainerManager &medViewContainerManager::instance() { if(!s_instance) - s_instance = new medViewContainerManager; - - return s_instance; + { + s_instance = std::unique_ptr(new medViewContainerManager()); + } + return *s_instance.get(); } medViewContainerManager::medViewContainerManager(void) : d(new medViewContainerManagerPrivate) { } -medViewContainerManager::~medViewContainerManager(void) +medViewContainerManager::~medViewContainerManager() { delete d; d = nullptr; @@ -59,5 +62,3 @@ medViewContainer* medViewContainerManager::container(QUuid uuid) const { return d->containers.value(uuid); } - -medViewContainerManager *medViewContainerManager::s_instance = nullptr; diff --git a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.h b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.h index abb0467319..b3ae73b29e 100644 --- a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.h +++ b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerManager.h @@ -16,6 +16,7 @@ #include +#include class QUuid; class medViewContainer; @@ -27,7 +28,8 @@ class MEDCORELEGACY_EXPORT medViewContainerManager : public QObject Q_OBJECT public: - static medViewContainerManager *instance(); + ~medViewContainerManager(); + static medViewContainerManager &instance(); medViewContainer* container(QUuid uuid) const; @@ -36,11 +38,9 @@ public slots: void unregisterContainer(medViewContainer *container); protected: - medViewContainerManager(); - ~medViewContainerManager(); + medViewContainerManager(); -protected: - static medViewContainerManager *s_instance; + static std::unique_ptr s_instance; signals: void containerAboutToBeDestroyed(QUuid uuid); diff --git a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerSplitter.cpp b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerSplitter.cpp index 51c68ad982..eaee5cdb84 100644 --- a/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerSplitter.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/viewContainers/medViewContainerSplitter.cpp @@ -132,7 +132,7 @@ medViewContainer *medViewContainerSplitter::split(Qt::AlignmentFlag alignement) void medViewContainerSplitter::split(medDataIndex index, Qt::AlignmentFlag alignement) { medViewContainer *newContainer = this->split(alignement); - newContainer->addData(medDataManager::instance()->retrieveData(index)); + newContainer->addData(medDataManager::instance().retrieveData(index)); } /** diff --git a/src/layers/legacy/medCoreLegacy/medJobManagerL.cpp b/src/layers/legacy/medCoreLegacy/medJobManagerL.cpp index 025b107ac2..192f93ea67 100644 --- a/src/layers/legacy/medCoreLegacy/medJobManagerL.cpp +++ b/src/layers/legacy/medCoreLegacy/medJobManagerL.cpp @@ -14,8 +14,6 @@ #include #include -medJobManagerL *medJobManagerL::s_instance = nullptr; - class medJobManagerLPrivate { public: @@ -23,12 +21,15 @@ class medJobManagerLPrivate bool m_IsActive; }; -medJobManagerL *medJobManagerL::instance(void) +std::unique_ptr medJobManagerL::s_instance = nullptr; + +medJobManagerL &medJobManagerL::instance() { if(!s_instance) - s_instance = new medJobManagerL; - - return s_instance; + { + s_instance = std::unique_ptr(new medJobManagerL()); + } + return *s_instance.get(); } medJobManagerL::medJobManagerL( void ) : d(new medJobManagerLPrivate) diff --git a/src/layers/legacy/medCoreLegacy/medJobManagerL.h b/src/layers/legacy/medCoreLegacy/medJobManagerL.h index eaf96e8a93..198d0ffa37 100644 --- a/src/layers/legacy/medCoreLegacy/medJobManagerL.h +++ b/src/layers/legacy/medCoreLegacy/medJobManagerL.h @@ -17,6 +17,8 @@ #include #include +#include + class medJobManagerLPrivate; class medJobItemL; @@ -36,7 +38,9 @@ class MEDCORELEGACY_EXPORT medJobManagerL : public QObject Q_OBJECT public: - static medJobManagerL *instance(); + ~medJobManagerL(); + + static medJobManagerL &instance(); /** * registerJobItem - register a job item if you want that the manager sends cancel events to them (highly suggested!) @@ -68,10 +72,8 @@ class MEDCORELEGACY_EXPORT medJobManagerL : public QObject protected: medJobManagerL(); - ~medJobManagerL(); -protected: - static medJobManagerL *s_instance; + static std::unique_ptr s_instance; private: medJobManagerLPrivate *d; diff --git a/src/layers/legacy/medCoreLegacy/medMessageController.cpp b/src/layers/legacy/medCoreLegacy/medMessageController.cpp index da9c220b73..338cab59a8 100644 --- a/src/layers/legacy/medCoreLegacy/medMessageController.cpp +++ b/src/layers/legacy/medCoreLegacy/medMessageController.cpp @@ -60,7 +60,7 @@ void medMessage::stopTimer() void medMessage::remove() { - medMessageController::instance()->remove(this); + medMessageController::instance().remove(this); } // ///////////////////////////////////////////////////////////////// @@ -149,12 +149,15 @@ void medMessageProgress::paintEvent ( QPaintEvent * event) // medMessageController // ///////////////////////////////////////////////////////////////// -medMessageController *medMessageController::instance(void) +std::unique_ptr medMessageController::s_instance = nullptr; + +medMessageController &medMessageController::instance(void) { if(!s_instance) - s_instance = new medMessageController; - - return s_instance; + { + s_instance = std::unique_ptr(new medMessageController()); + } + return *s_instance.get(); } void medMessageController::showInfo(const QString& text,unsigned int timeout) @@ -209,5 +212,3 @@ void medMessageController::remove(medMessage *message) medMessageController::medMessageController(void) : QObject() { } - -medMessageController *medMessageController::s_instance = nullptr; diff --git a/src/layers/legacy/medCoreLegacy/medMessageController.h b/src/layers/legacy/medCoreLegacy/medMessageController.h index f118a09219..adc45f6119 100644 --- a/src/layers/legacy/medCoreLegacy/medMessageController.h +++ b/src/layers/legacy/medCoreLegacy/medMessageController.h @@ -18,6 +18,8 @@ #include +#include + class medMessageControllerPrivate; // ///////////////////////////////////////////////////////////////// @@ -105,7 +107,8 @@ class MEDCORELEGACY_EXPORT medMessageController : public QObject Q_OBJECT public: - static medMessageController *instance(); + ~medMessageController() = default; + static medMessageController &instance(); public slots: void showInfo(const QString& text,unsigned int timeout=0); @@ -119,11 +122,9 @@ public slots: void removeMessage(medMessage * message); protected: - medMessageController(); - ~medMessageController() = default; + medMessageController(); -protected: - static medMessageController *s_instance; + static std::unique_ptr s_instance; private: medMessageControllerPrivate *d; diff --git a/src/layers/legacy/medCoreLegacy/medPluginManager.cpp b/src/layers/legacy/medCoreLegacy/medPluginManager.cpp index 024710eed9..1da4d2f7d9 100644 --- a/src/layers/legacy/medCoreLegacy/medPluginManager.cpp +++ b/src/layers/legacy/medCoreLegacy/medPluginManager.cpp @@ -22,20 +22,22 @@ class medPluginManagerPrivate QStringList loadErrors; }; +std::unique_ptr medPluginManager::s_instance = nullptr; + /** * @brief Gets an instance of the Plugin Manager. * * * @param void - * @return medPluginManager * a pointer to an instance of the singleton. + * @return medPluginManager & a reference to an instance of the singleton. */ -medPluginManager *medPluginManager::instance() +medPluginManager &medPluginManager::instance() { if(!s_instance) { - s_instance = new medPluginManager; + s_instance = std::unique_ptr(new medPluginManager()); } - return s_instance; + return *s_instance.get(); } void medPluginManager::initialize() @@ -155,15 +157,12 @@ medPluginManager::medPluginManager(void) : dtkPluginManager(), d(new medPluginMa * * @param void */ -medPluginManager::~medPluginManager(void) +medPluginManager::~medPluginManager() { delete d; d = nullptr; } -medPluginManager *medPluginManager::s_instance = nullptr; - - void medPluginManager::onLoadError(const QString &errorMessage) { qDebug() << "add error message to pluginManager:"; diff --git a/src/layers/legacy/medCoreLegacy/medPluginManager.h b/src/layers/legacy/medCoreLegacy/medPluginManager.h index 910c817f47..1bb0cd88ab 100644 --- a/src/layers/legacy/medCoreLegacy/medPluginManager.h +++ b/src/layers/legacy/medCoreLegacy/medPluginManager.h @@ -18,6 +18,8 @@ #include +#include + class medPluginManagerPrivate; /** @@ -29,7 +31,9 @@ class MEDCORELEGACY_EXPORT medPluginManager : public dtkPluginManager Q_OBJECT public: - static medPluginManager *instance(); + ~medPluginManager(); + + static medPluginManager &instance(); void readSettings(); void initialize(); @@ -43,10 +47,6 @@ class MEDCORELEGACY_EXPORT medPluginManager : public dtkPluginManager public slots: void onPluginLoaded(const QString& name); -protected: - medPluginManager(); - ~medPluginManager(); - protected slots: void onLoadError(const QString& errorMessage); @@ -54,7 +54,9 @@ protected slots: void allPluginsLoaded(); private: - static medPluginManager *s_instance; + medPluginManager(); + + static std::unique_ptr s_instance; private: medPluginManagerPrivate *d; diff --git a/src/layers/legacy/medCoreLegacy/medSettingsManager.cpp b/src/layers/legacy/medCoreLegacy/medSettingsManager.cpp index 6196f102bd..ebcc0c65f8 100644 --- a/src/layers/legacy/medCoreLegacy/medSettingsManager.cpp +++ b/src/layers/legacy/medCoreLegacy/medSettingsManager.cpp @@ -23,15 +23,19 @@ class medSettingsManagerPrivate QSettings settings; }; +std::unique_ptr medSettingsManager::s_instance = nullptr; + /** * instance - singleton access method, returns a singleViewContainer static instance of the manager -* @return medSettingsManager * - the manager +* @return medSettingsManager & - the manager */ -medSettingsManager * medSettingsManager::instance( void ) +medSettingsManager &medSettingsManager::instance() { if(!s_instance) - s_instance = new medSettingsManager; - return s_instance; + { + s_instance = std::unique_ptr(new medSettingsManager()); + } + return *s_instance.get(); } medSettingsManager::medSettingsManager( void ): d(new medSettingsManagerPrivate) @@ -39,23 +43,12 @@ medSettingsManager::medSettingsManager( void ): d(new medSettingsManagerPrivate) } -medSettingsManager::~medSettingsManager( void ) +medSettingsManager::~medSettingsManager() { delete d; d = nullptr; } -/** -* destroy - should be called on closing the application, to destroy the singleton -*/ -void medSettingsManager::destroy( void ) -{ - if (s_instance) { - delete s_instance; - s_instance = 0; - } -} - /** * value * @param: const QString & section @@ -126,5 +119,3 @@ void medSettingsManager::remove (const QString & section, const QString & key) d->settings.remove(key); d->settings.endGroup(); } - -medSettingsManager *medSettingsManager::s_instance = nullptr; diff --git a/src/layers/legacy/medCoreLegacy/medSettingsManager.h b/src/layers/legacy/medCoreLegacy/medSettingsManager.h index ec4d119259..dae17c0d26 100644 --- a/src/layers/legacy/medCoreLegacy/medSettingsManager.h +++ b/src/layers/legacy/medCoreLegacy/medSettingsManager.h @@ -16,6 +16,8 @@ #include +#include + class medSettingsManagerPrivate; class MEDCORELEGACY_EXPORT medSettingsManager : public QObject @@ -23,9 +25,9 @@ class MEDCORELEGACY_EXPORT medSettingsManager : public QObject Q_OBJECT public: - static medSettingsManager *instance(); + ~medSettingsManager(); - static void destroy(); + static medSettingsManager &instance(); void setValue( const QString & section, const QString & key, const QVariant & value ); @@ -37,12 +39,10 @@ class MEDCORELEGACY_EXPORT medSettingsManager : public QObject signals: void settingsChanged( const QString & ); -protected: - medSettingsManager(); - ~medSettingsManager(); - private: - static medSettingsManager *s_instance; + medSettingsManager(); + + static std::unique_ptr s_instance; medSettingsManagerPrivate *d; }; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterGroupL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterGroupL.cpp index b1c2a12fe6..1ad8df5ac8 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterGroupL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterGroupL.cpp @@ -43,7 +43,7 @@ medAbstractParameterGroupL::medAbstractParameterGroupL(QString name, QObject *pa medAbstractParameterGroupL::~medAbstractParameterGroupL() { - medParameterGroupManagerL::instance()->unregisterGroup(this); + medParameterGroupManagerL::instance().unregisterGroup(this); delete d; } diff --git a/src/layers/legacy/medCoreLegacy/parameters/medLayerParameterGroupL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medLayerParameterGroupL.cpp index 279228ee2e..c8f50a9c1d 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medLayerParameterGroupL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medLayerParameterGroupL.cpp @@ -34,12 +34,12 @@ medLayerParameterGroupL::medLayerParameterGroupL(QString name, QObject *parent, { d->pool = new medParameterPoolL(this); - medParameterGroupManagerL::instance()->registerNewGroup(this); + medParameterGroupManagerL::instance().registerNewGroup(this); } medLayerParameterGroupL::~medLayerParameterGroupL() { - medParameterGroupManagerL::instance()->unregisterGroup(this); + medParameterGroupManagerL::instance().unregisterGroup(this); QHashIterator iter(d->impactedLayers); while(iter.hasNext()) diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.cpp index fc727b1725..e7494065a1 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.cpp @@ -28,12 +28,15 @@ class medParameterGroupManagerLPrivate }; -medParameterGroupManagerL *medParameterGroupManagerL::instance() +std::unique_ptr medParameterGroupManagerL::s_instance = nullptr; + +medParameterGroupManagerL &medParameterGroupManagerL::instance() { if(!s_instance) - s_instance = new medParameterGroupManagerL(); - - return s_instance; + { + s_instance = std::unique_ptr(new medParameterGroupManagerL()); + } + return *s_instance.get(); } medParameterGroupManagerL::medParameterGroupManagerL(void) : d(new medParameterGroupManagerLPrivate) @@ -41,7 +44,7 @@ medParameterGroupManagerL::medParameterGroupManagerL(void) : d(new medParameterG d->currentWorkspace = ""; } -medParameterGroupManagerL::~medParameterGroupManagerL(void) +medParameterGroupManagerL::~medParameterGroupManagerL() { delete d; d = nullptr; @@ -169,5 +172,3 @@ void medParameterGroupManagerL::setCurrentWorkspace(QString workspace) { d->currentWorkspace = workspace; } - -medParameterGroupManagerL *medParameterGroupManagerL::s_instance = nullptr; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.h b/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.h index 0a8b1cb4a9..637d03b71e 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.h +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterGroupManagerL.h @@ -16,6 +16,8 @@ #include +#include + class medAbstractView; class medAbstractLayeredView; class medAbstractParameterGroupL; @@ -30,7 +32,9 @@ class MEDCORELEGACY_EXPORT medParameterGroupManagerL : public QObject Q_OBJECT public: - static medParameterGroupManagerL *instance(); + ~medParameterGroupManagerL(); + + static medParameterGroupManagerL &instance(); void registerNewGroup(medAbstractParameterGroupL* group); void unregisterGroup(medAbstractParameterGroupL* group); @@ -48,9 +52,8 @@ class MEDCORELEGACY_EXPORT medParameterGroupManagerL : public QObject protected: medParameterGroupManagerL(); - ~medParameterGroupManagerL(); - static medParameterGroupManagerL *s_instance; + static std::unique_ptr s_instance; private: medParameterGroupManagerLPrivate *d; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp index c153054fb4..ade720674f 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.cpp @@ -17,73 +17,63 @@ #include #include - class medParameterPoolManagerLPrivate { public: - QHash pools; - + QHash > pools; }; -medParameterPoolManagerL *medParameterPoolManagerL::instance(void) +std::unique_ptr medParameterPoolManagerL::s_instance = nullptr; + +medParameterPoolManagerL &medParameterPoolManagerL::instance() { if(!s_instance) - s_instance = new medParameterPoolManagerL; - - return s_instance; + { + s_instance = std::unique_ptr(new medParameterPoolManagerL()); + } + return *s_instance.get(); } medParameterPoolManagerL::medParameterPoolManagerL(void) : d(new medParameterPoolManagerLPrivate) { } -medParameterPoolManagerL::~medParameterPoolManagerL() -{ - for (auto it = d->pools.begin(); it != d->pools.end(); ++it) - { - delete it.value(); - } - d->pools.clear(); - - delete d; - d = nullptr; -} +medParameterPoolManagerL::~medParameterPoolManagerL() = default; // For private class forward declaration void medParameterPoolManagerL::removePool(QString poolId) { - medParameterPoolL *poolToRemove = d->pools.value(poolId); + auto poolToRemove = d->pools.value(poolId); if(poolToRemove) { poolToRemove->clear(); d->pools.remove(poolId); - delete poolToRemove; } } void medParameterPoolManagerL::linkParameter(medAbstractParameterL* parameter , QString poolId) { - medParameterPoolL *selectedPool; - if( !d->pools.keys().contains(poolId) ) { - selectedPool = new medParameterPoolL(this); + std::shared_ptr selectedPool(new medParameterPoolL(this)); selectedPool->setName(poolId); d->pools.insert(poolId, selectedPool); + selectedPool->append(parameter); + } + else + { + d->pools.value(poolId)->append(parameter); } - else selectedPool = d->pools.value(poolId); - - selectedPool->append(parameter); } void medParameterPoolManagerL::unlinkParameter(medAbstractParameterL* param) { - for(medParameterPoolL *pool : d->pools.values() ) + for(const auto& pool : d->pools.values() ) { pool->remove(param); } } -QList medParameterPoolManagerL::pools() +QList > medParameterPoolManagerL::pools() { return d->pools.values(); } @@ -91,7 +81,7 @@ QList medParameterPoolManagerL::pools() QStringList medParameterPoolManagerL::pools(medAbstractParameterL *param) { QStringList pools; - for(medParameterPoolL *pool: d->pools.values() ) + for(const auto& pool : d->pools.values() ) { if(pool->parameters().contains(param)) { @@ -101,9 +91,7 @@ QStringList medParameterPoolManagerL::pools(medAbstractParameterL *param) return pools; } -medParameterPoolL* medParameterPoolManagerL::pool(QString poolId) +std::shared_ptr medParameterPoolManagerL::pool(QString poolId) { return d->pools.value(poolId); } - -medParameterPoolManagerL *medParameterPoolManagerL::s_instance = nullptr; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h index 334956a75f..4531d3d664 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolManagerL.h @@ -17,6 +17,8 @@ #include +#include + class medParameterPoolManagerLPrivate; class medParameterPoolManagerLToolBox; @@ -29,10 +31,12 @@ class MEDCORELEGACY_EXPORT medParameterPoolManagerL : public QObject Q_OBJECT public: - static medParameterPoolManagerL *instance(); + ~medParameterPoolManagerL(); + + static medParameterPoolManagerL &instance(); - QList pools(); - medParameterPoolL* pool(QString poolId); + QList > pools(); + std::shared_ptr pool(QString poolId); QStringList pools(medAbstractParameterL *param); public slots: @@ -42,12 +46,10 @@ public slots: protected: medParameterPoolManagerL(); - ~medParameterPoolManagerL(); - static medParameterPoolManagerL *s_instance; + static std::unique_ptr s_instance; private: - medParameterPoolManagerLPrivate *d; - + const std::unique_ptr d; }; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp index 2f3871d5ac..f5a5b325b6 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medViewParameterGroupL.cpp @@ -38,14 +38,14 @@ medViewParameterGroupL::medViewParameterGroupL(QString name, QObject *parent, QS { d->pool = new medParameterPoolL(this); - medParameterGroupManagerL::instance()->registerNewGroup(this); + medParameterGroupManagerL::instance().registerNewGroup(this); connect(this, SIGNAL(groupColorChanged(QColor,QColor)), this, SLOT(updateGroupIndicators(QColor,QColor))); } medViewParameterGroupL::~medViewParameterGroupL() { - medParameterGroupManagerL::instance()->unregisterGroup(this); + medParameterGroupManagerL::instance().unregisterGroup(this); d->pool->clear(); diff --git a/src/layers/legacy/medCoreLegacy/views/interactors/medAbstractLayeredViewInteractor.cpp b/src/layers/legacy/medCoreLegacy/views/interactors/medAbstractLayeredViewInteractor.cpp index 57a0742f36..340876660e 100644 --- a/src/layers/legacy/medCoreLegacy/views/interactors/medAbstractLayeredViewInteractor.cpp +++ b/src/layers/legacy/medCoreLegacy/views/interactors/medAbstractLayeredViewInteractor.cpp @@ -52,7 +52,7 @@ medAbstractBoolParameterL* medAbstractLayeredViewInteractor::visibilityParameter { if(this->inputData()) { - QPixmap thumbnail = medDataManager::instance()->thumbnail(this->inputData()->dataIndex()); + QPixmap thumbnail = medDataManager::instance().thumbnail(this->inputData()->dataIndex()); QPushButton* thumbnailButton = d->visibilityParameter->getPushButton(); QIcon thumbnailIcon; // Set the off icon to the greyed out version of the regular icon diff --git a/src/layers/legacy/medCoreLegacy/views/medAbstractImageView.cpp b/src/layers/legacy/medCoreLegacy/views/medAbstractImageView.cpp index 7d89069782..c74e003a52 100644 --- a/src/layers/legacy/medCoreLegacy/views/medAbstractImageView.cpp +++ b/src/layers/legacy/medCoreLegacy/views/medAbstractImageView.cpp @@ -231,7 +231,7 @@ void medAbstractImageView::switchToFourViews() for(medDataIndex index : this->dataList()) { - medAbstractData *data = medDataManager::instance()->retrieveData(index); + medAbstractData *data = medDataManager::instance().retrieveData(index); if (data) { topRightContainer->addData(data); @@ -254,7 +254,7 @@ void medAbstractImageView::switchToFourViews() unsigned int linkGroupNumber = 1; QString linkGroupName = linkGroupBaseName + QString::number(linkGroupNumber); - while (medParameterGroupManagerL::instance()->viewGroup(linkGroupName)) + while (medParameterGroupManagerL::instance().viewGroup(linkGroupName)) { linkGroupNumber++; linkGroupName = linkGroupBaseName + QString::number(linkGroupNumber); diff --git a/src/layers/legacy/medCoreLegacy/views/medAbstractLayeredView.cpp b/src/layers/legacy/medCoreLegacy/views/medAbstractLayeredView.cpp index 3c8c5d5c9f..caf1d0a546 100644 --- a/src/layers/legacy/medCoreLegacy/views/medAbstractLayeredView.cpp +++ b/src/layers/legacy/medCoreLegacy/views/medAbstractLayeredView.cpp @@ -251,7 +251,7 @@ void medAbstractLayeredView::setDataList(QList dataList) { if (!dataList.contains(index)) { - medAbstractData *data = medDataManager::instance()->retrieveData(index); + medAbstractData *data = medDataManager::instance().retrieveData(index); if (data) { this->removeLayer(this->layer(data)); @@ -261,7 +261,7 @@ void medAbstractLayeredView::setDataList(QList dataList) for(medDataIndex index : dataList) { - medAbstractData *data = medDataManager::instance()->retrieveData(index); + medAbstractData *data = medDataManager::instance().retrieveData(index); if (!data || this->contains(data)) { continue; @@ -271,12 +271,12 @@ void medAbstractLayeredView::setDataList(QList dataList) unsigned int layerNumber = this->layer(data); - QList groupsLayer0 = medParameterGroupManagerL::instance()->layerGroups(this, this->layerData(0)); - QList groupsLayeri = medParameterGroupManagerL::instance()->layerGroups(this, data); + QList groupsLayer0 = medParameterGroupManagerL::instance().layerGroups(this, this->layerData(0)); + QList groupsLayeri = medParameterGroupManagerL::instance().layerGroups(this, data); if(!groupsLayer0.isEmpty() && layerNumber > 0 && groupsLayeri.isEmpty() ) { QString newGroup = groupsLayer0[0]->name() + " Layer " + QString::number(layerNumber+1); - medLayerParameterGroupL* layerGroup = medParameterGroupManagerL::instance()->layerGroup(newGroup); + medLayerParameterGroupL* layerGroup = medParameterGroupManagerL::instance().layerGroup(newGroup); if(!layerGroup) { layerGroup = new medLayerParameterGroupL(newGroup, this); @@ -482,7 +482,7 @@ void medAbstractLayeredView::write(QString &path) // Getting a working file extension // 1. find suitable writers QList allWriters = medAbstractDataFactory::instance()->writers(); - QHash possibleWriters = medDataManager::instance()->getPossibleWriters(layerData(i)); + QHash possibleWriters = medDataManager::instance().getPossibleWriters(layerData(i)); // 2. use these writers to get a suitable file extension QString fileExtension; diff --git a/src/layers/legacy/medCoreLegacy/views/medAbstractView.cpp b/src/layers/legacy/medCoreLegacy/views/medAbstractView.cpp index a77fcd14f7..9330a73bd4 100644 --- a/src/layers/legacy/medCoreLegacy/views/medAbstractView.cpp +++ b/src/layers/legacy/medCoreLegacy/views/medAbstractView.cpp @@ -319,9 +319,10 @@ void medAbstractView::setUpViewForThumbnail() QString msg = "Unable to find any current primary interactor for view " + this->identifier(); qWarning() << msg; } - else + { this->primaryInteractor()->setUpViewForThumbnail(); + } } /** diff --git a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp index 91cf9c51ac..eef54c2ae7 100644 --- a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp +++ b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp @@ -104,7 +104,7 @@ void medAbstractImageViewNavigator::updateTimeLineParameter() double sequenceFrameRate = 0; for(medDataIndex index : d->view->dataList()) { - medAbstractData *data = medDataManager::instance()->retrieveData(index); + medAbstractData *data = medDataManager::instance().retrieveData(index); if (!data) continue; diff --git a/src/layers/legacy/medImageIO/itkDataImageReaderBase.cpp b/src/layers/legacy/medImageIO/itkDataImageReaderBase.cpp index 015b2ca9c0..132415e8d2 100644 --- a/src/layers/legacy/medImageIO/itkDataImageReaderBase.cpp +++ b/src/layers/legacy/medImageIO/itkDataImageReaderBase.cpp @@ -299,7 +299,7 @@ QString itkDataImageReaderBase::convertItkKeyToMedKey(std::string& keyToConvert) } else { - const medMetaDataKeys::Key* medKey = medMetaDataKeys::Key::fromKeyName(keyToConvert.c_str()); + const medMetaDataKeys::Key* medKey = medMetaDataKeys::Key::fromKeyName(itkKey); if (medKey) { convertedKey = medKey->key(); diff --git a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp index a890d0bad0..11a1ad9163 100644 --- a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp +++ b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp @@ -104,7 +104,7 @@ void itkDataImageWriterBase::encapsulateSharedMetaData(itk::MetaDataDictionary& foreach (QString metaDataKey, data()->metaDataList()) { - if (medMetaDataKeys::Key::fromKeyName(metaDataKey.toStdString().c_str())) + if (medMetaDataKeys::Key::fromKeyName(metaDataKey)) { std::string key = convertMedKeyToItkKey(metaDataKey); itk::EncapsulateMetaData(metaDataDictionary, key, data()->metadata(metaDataKey).toStdString()); diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp index 7c422c069f..428018b6a0 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp @@ -23,8 +23,8 @@ vtkStandardNewMacro(vtkImage2DDisplay) vtkImage2DDisplay::vtkImage2DDisplay() { this->InputProducer = nullptr; - this->ImageActor = vtkImageActor::New(); - this->WindowLevel = vtkImageMapToColors::New(); + this->ImageActor = vtkSmartPointer::New(); + this->WindowLevel = vtkSmartPointer::New(); this->WindowLevel->SetOutputFormatToRGBA(); this->ColorWindow = 1e-3 * VTK_DOUBLE_MAX; this->ColorLevel = 0; diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx index cf558f0fe0..427b8b290b 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImageView2D.cxx @@ -147,7 +147,6 @@ vtkImageView2D::~vtkImageView2D() this->Command->Delete(); this->OrientationAnnotation->Delete(); - for (std::list::iterator it3 = this->DataSetWidgets.begin(); it3!=this->DataSetWidgets.end(); ++it3) { @@ -155,6 +154,8 @@ vtkImageView2D::~vtkImageView2D() (*it3)->SetImageView (nullptr); (*it3)->Delete(); } + + delete qtSignalHandler; } //---------------------------------------------------------------------------- diff --git a/src/layers/medWidgets/process/arithmetic_operation/medAbstractArithmeticOperationProcessPresenter.cpp b/src/layers/medWidgets/process/arithmetic_operation/medAbstractArithmeticOperationProcessPresenter.cpp index 5928febd7c..ff81a473c3 100644 --- a/src/layers/medWidgets/process/arithmetic_operation/medAbstractArithmeticOperationProcessPresenter.cpp +++ b/src/layers/medWidgets/process/arithmetic_operation/medAbstractArithmeticOperationProcessPresenter.cpp @@ -116,7 +116,7 @@ void medAbstractArithmeticOperationProcessPresenter::_importOutput(medAbstractJo { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/diffusion_processes/medAbstractDWIMaskingProcessPresenter.cpp b/src/layers/medWidgets/process/diffusion_processes/medAbstractDWIMaskingProcessPresenter.cpp index ac8f6d4f95..fa732d800d 100644 --- a/src/layers/medWidgets/process/diffusion_processes/medAbstractDWIMaskingProcessPresenter.cpp +++ b/src/layers/medWidgets/process/diffusion_processes/medAbstractDWIMaskingProcessPresenter.cpp @@ -109,7 +109,7 @@ void medAbstractDWIMaskingProcessPresenter::_importOutput(medAbstractJob::medJob { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionModelEstimationProcessPresenter.cpp b/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionModelEstimationProcessPresenter.cpp index 52eeb09773..b027e5975d 100644 --- a/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionModelEstimationProcessPresenter.cpp +++ b/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionModelEstimationProcessPresenter.cpp @@ -151,7 +151,7 @@ void medAbstractDiffusionModelEstimationProcessPresenter::_importOutput(medAbstr { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionScalarMapsProcessPresenter.cpp b/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionScalarMapsProcessPresenter.cpp index 3752ab395c..f3c11ac05c 100644 --- a/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionScalarMapsProcessPresenter.cpp +++ b/src/layers/medWidgets/process/diffusion_processes/medAbstractDiffusionScalarMapsProcessPresenter.cpp @@ -96,7 +96,7 @@ void medAbstractDiffusionScalarMapsProcessPresenter::_importOutput(medAbstractJo { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/diffusion_processes/medAbstractTractographyProcessPresenter.cpp b/src/layers/medWidgets/process/diffusion_processes/medAbstractTractographyProcessPresenter.cpp index cfc030c49d..3751339184 100644 --- a/src/layers/medWidgets/process/diffusion_processes/medAbstractTractographyProcessPresenter.cpp +++ b/src/layers/medWidgets/process/diffusion_processes/medAbstractTractographyProcessPresenter.cpp @@ -89,7 +89,7 @@ void medAbstractTractographyProcessPresenter::_importOutput(medAbstractJob::medJ { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/diffusion_processes/medDiffusionModelEstimationMetaProcessPresenter.cpp b/src/layers/medWidgets/process/diffusion_processes/medDiffusionModelEstimationMetaProcessPresenter.cpp index f904b3f52b..c9216a92c1 100644 --- a/src/layers/medWidgets/process/diffusion_processes/medDiffusionModelEstimationMetaProcessPresenter.cpp +++ b/src/layers/medWidgets/process/diffusion_processes/medDiffusionModelEstimationMetaProcessPresenter.cpp @@ -163,7 +163,7 @@ void medDiffusionModelEstimationMetaProcessPresenter::_importOutput(medAbstractJ { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/mask_image/medAbstractMaskImageProcessPresenter.cpp b/src/layers/medWidgets/process/mask_image/medAbstractMaskImageProcessPresenter.cpp index 9a10d6563e..7f37d80ff6 100644 --- a/src/layers/medWidgets/process/mask_image/medAbstractMaskImageProcessPresenter.cpp +++ b/src/layers/medWidgets/process/mask_image/medAbstractMaskImageProcessPresenter.cpp @@ -116,7 +116,7 @@ void medAbstractMaskImageProcessPresenter::_importOutput(medAbstractJob::medJobE { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/morphomath_operation/medAbstractMorphomathOperationProcessPresenter.cpp b/src/layers/medWidgets/process/morphomath_operation/medAbstractMorphomathOperationProcessPresenter.cpp index 676736e553..fb90a640fb 100644 --- a/src/layers/medWidgets/process/morphomath_operation/medAbstractMorphomathOperationProcessPresenter.cpp +++ b/src/layers/medWidgets/process/morphomath_operation/medAbstractMorphomathOperationProcessPresenter.cpp @@ -108,7 +108,7 @@ void medAbstractMorphomathOperationProcessPresenter::_importOutput(medAbstractJo { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/layers/medWidgets/process/single_filter/medAbstractSingleFilterOperationProcessPresenter.cpp b/src/layers/medWidgets/process/single_filter/medAbstractSingleFilterOperationProcessPresenter.cpp index 6b908793bb..de91e021b8 100644 --- a/src/layers/medWidgets/process/single_filter/medAbstractSingleFilterOperationProcessPresenter.cpp +++ b/src/layers/medWidgets/process/single_filter/medAbstractSingleFilterOperationProcessPresenter.cpp @@ -109,7 +109,7 @@ void medAbstractSingleFilterOperationProcessPresenter::_importOutput(medAbstract { if(jobExitStatus == medAbstractJob::MED_JOB_EXIT_SUCCESS) { - medDataManager::instance()->importData(d->process->output()); + medDataManager::instance().importData(d->process->output()); emit _outputImported(d->process->output()); } } diff --git a/src/plugins/legacy/LCCLogDemons/LCCLogDemonsToolBox.cpp b/src/plugins/legacy/LCCLogDemons/LCCLogDemonsToolBox.cpp index 0d8720d0f2..d0494cbff1 100644 --- a/src/plugins/legacy/LCCLogDemons/LCCLogDemonsToolBox.cpp +++ b/src/plugins/legacy/LCCLogDemons/LCCLogDemonsToolBox.cpp @@ -212,9 +212,7 @@ LCCLogDemonsToolBox::~LCCLogDemonsToolBox() dtkPlugin * LCCLogDemonsToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin("LCCLogDemonsPlugin"); - return plugin; + return medPluginManager::instance().plugin("LCCLogDemonsPlugin"); } medAbstractData * LCCLogDemonsToolBox::processOutput() diff --git a/src/plugins/legacy/diffeomorphicDemons/diffeomorphicDemonsToolBox.cpp b/src/plugins/legacy/diffeomorphicDemons/diffeomorphicDemonsToolBox.cpp index 42771918ec..041bfeaa41 100644 --- a/src/plugins/legacy/diffeomorphicDemons/diffeomorphicDemonsToolBox.cpp +++ b/src/plugins/legacy/diffeomorphicDemons/diffeomorphicDemonsToolBox.cpp @@ -146,9 +146,7 @@ bool diffeomorphicDemonsToolBox::registered() dtkPlugin* diffeomorphicDemonsToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Diffeomorphic Demons" ); - return plugin; + return medPluginManager::instance().plugin("Diffeomorphic Demons"); } void diffeomorphicDemonsToolBox::run() diff --git a/src/plugins/legacy/iterativeClosestPoint/iterativeClosestPointToolBox.cpp b/src/plugins/legacy/iterativeClosestPoint/iterativeClosestPointToolBox.cpp index 8ff2a41e79..de56005fdf 100644 --- a/src/plugins/legacy/iterativeClosestPoint/iterativeClosestPointToolBox.cpp +++ b/src/plugins/legacy/iterativeClosestPoint/iterativeClosestPointToolBox.cpp @@ -231,12 +231,12 @@ void iterativeClosestPointToolBox::run() } else { - medMessageController::instance()->showError(tr("Choose two meshes"), 3000); + medMessageController::instance().showError(tr("Choose two meshes"), 3000); } } else { - medMessageController::instance()->showError(tr("Drop two meshes in the view"), 3000); + medMessageController::instance().showError(tr("Drop two meshes in the view"), 3000); } } @@ -303,9 +303,7 @@ medAbstractData* iterativeClosestPointToolBox::processOutput() dtkPlugin* iterativeClosestPointToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin ( "Iterative Closest Point" ); - return plugin; + return medPluginManager::instance().plugin("Iterative Closest Point"); } void iterativeClosestPointToolBox::onExportTransferMatrixCheckBoxToggled(bool toggle) diff --git a/src/plugins/legacy/itkDataImage/interactors/medVtkViewItkDataImageInteractor.cpp b/src/plugins/legacy/itkDataImage/interactors/medVtkViewItkDataImageInteractor.cpp index 28c74c067b..e8d8dfbfcd 100644 --- a/src/plugins/legacy/itkDataImage/interactors/medVtkViewItkDataImageInteractor.cpp +++ b/src/plugins/legacy/itkDataImage/interactors/medVtkViewItkDataImageInteractor.cpp @@ -202,7 +202,7 @@ bool medVtkViewItkDataImageInteractor::SetViewInput(medAbstractData* data, int l { bool bRes = true; - auto *poOldConv = m_poConv; + auto *poOldConv = m_poConv; // is deleted later in method to avoid uninitialized m_poConv m_poConv = vtkItkConversionInterface::createInstance(data); if (m_poConv) @@ -220,6 +220,7 @@ bool medVtkViewItkDataImageInteractor::SetViewInput(medAbstractData* data, int l d->view2d->SetInput(poVtkAlgoOutputPort, poMatrix, layer); d->view3d->SetInput(poVtkAlgoOutputPort, poMatrix, layer); } + poMatrix->Delete(); } } else diff --git a/src/plugins/legacy/itkDataImage/writers/itkNiftiDataImageWriter.cpp b/src/plugins/legacy/itkDataImage/writers/itkNiftiDataImageWriter.cpp index 19bda5a9b2..137e0894d8 100644 --- a/src/plugins/legacy/itkDataImage/writers/itkNiftiDataImageWriter.cpp +++ b/src/plugins/legacy/itkDataImage/writers/itkNiftiDataImageWriter.cpp @@ -18,10 +18,6 @@ #include -static QString s_identifier() { - return "itkNiftiDataImageWriter"; -} - static QStringList s_handled() { return QStringList() << "itkDataImageChar3" << "itkDataImageChar4" << "itkDataImageUChar3" << "itkDataImageUChar4" @@ -51,12 +47,16 @@ QStringList itkNiftiDataImageWriter::supportedFileExtensions() const << ".nia" << ".img" << ".img.gz"; } -bool itkNiftiDataImageWriter::registered() { - return medAbstractDataFactory::instance()->registerDataWriterType(s_identifier(), s_handled(), create); +bool itkNiftiDataImageWriter::registered() +{ + return medAbstractDataFactory::instance()->registerDataWriterType(QString("itkNiftiDataImageWriter"), + s_handled(), + create); } -QString itkNiftiDataImageWriter::identifier() const { - return s_identifier(); +QString itkNiftiDataImageWriter::identifier() const +{ + return "itkNiftiDataImageWriter"; } QString itkNiftiDataImageWriter::description() const { diff --git a/src/plugins/legacy/itkFilters/itkFiltersToolBox.cpp b/src/plugins/legacy/itkFilters/itkFiltersToolBox.cpp index fb4987e592..b008cf8f3c 100644 --- a/src/plugins/legacy/itkFilters/itkFiltersToolBox.cpp +++ b/src/plugins/legacy/itkFilters/itkFiltersToolBox.cpp @@ -1157,9 +1157,7 @@ void itkFiltersToolBox::onViewClosed() dtkPlugin* itkFiltersToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin ( "ITK Filters" ); - return plugin; + return medPluginManager::instance().plugin("ITK Filters"); } medToolBox *createitkFiltersToolBox(QWidget *parent) diff --git a/src/plugins/legacy/itkFilters/itkMorphologicalFiltersToolBox.cpp b/src/plugins/legacy/itkFilters/itkMorphologicalFiltersToolBox.cpp index d9809ef5de..39e9b89789 100644 --- a/src/plugins/legacy/itkFilters/itkMorphologicalFiltersToolBox.cpp +++ b/src/plugins/legacy/itkFilters/itkMorphologicalFiltersToolBox.cpp @@ -209,7 +209,5 @@ void itkMorphologicalFiltersToolBox::roundSpinBox(bool param) dtkPlugin* itkMorphologicalFiltersToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin ( "ITK Filters" ); - return plugin; + return medPluginManager::instance().plugin("ITK Filters"); } diff --git a/src/plugins/legacy/itkProcessRegistrationOptimus/itkProcessRegistrationOptimusToolBox.cpp b/src/plugins/legacy/itkProcessRegistrationOptimus/itkProcessRegistrationOptimusToolBox.cpp index cb28e09064..423f55f1f5 100644 --- a/src/plugins/legacy/itkProcessRegistrationOptimus/itkProcessRegistrationOptimusToolBox.cpp +++ b/src/plugins/legacy/itkProcessRegistrationOptimus/itkProcessRegistrationOptimusToolBox.cpp @@ -148,9 +148,7 @@ itkProcessRegistrationOptimusToolBox::~itkProcessRegistrationOptimusToolBox(void dtkPlugin * itkProcessRegistrationOptimusToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin("itkProcessRegistrationOptimusPlugin"); - return plugin; + return medPluginManager::instance().plugin("itkProcessRegistrationOptimusPlugin"); } medAbstractData * itkProcessRegistrationOptimusToolBox::processOutput() @@ -231,7 +229,7 @@ void itkProcessRegistrationOptimusToolBox::run(void) setToolBoxOnWaitStatus(); - medJobManagerL::instance()->registerJobItem(runProcess, d->process->identifier()); + medJobManagerL::instance().registerJobItem(runProcess, d->process->identifier()); addConnectionsAndStartJob(runProcess); enableOnProcessSuccessImportOutput(runProcess, false); diff --git a/src/plugins/legacy/manualRegistration/manualRegistrationLandmarkController.cpp b/src/plugins/legacy/manualRegistration/manualRegistrationLandmarkController.cpp index 7011e78a46..80220437b2 100644 --- a/src/plugins/legacy/manualRegistration/manualRegistrationLandmarkController.cpp +++ b/src/plugins/legacy/manualRegistration/manualRegistrationLandmarkController.cpp @@ -221,7 +221,7 @@ void manualRegistrationLandmarkController::AddPoint(manualRegistrationLandmark* if (size >= 128) { - medMessageController::instance()->showError("You cannot add any more landmarks.", 3000); + medMessageController::instance().showError("You cannot add any more landmarks.", 3000); return; } @@ -278,13 +278,13 @@ int manualRegistrationLandmarkController::checkLandmarks() { if (Points_Fixed->size() != Points_Moving->size()) { - medMessageController::instance()->showError("The number of landmarks is not the same on both views", 3000); + medMessageController::instance().showError("The number of landmarks is not the same on both views", 3000); return medAbstractProcessLegacy::FAILURE; } if (!Points_Fixed->size()) { - medMessageController::instance()->showError("You didn't put any landmark!", 3000); + medMessageController::instance().showError("You didn't put any landmark!", 3000); return medAbstractProcessLegacy::FAILURE; } diff --git a/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp b/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp index 0963d25203..d02dd91a6b 100644 --- a/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp +++ b/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp @@ -162,9 +162,7 @@ bool manualRegistrationToolBox::registered() dtkPlugin* manualRegistrationToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Manual Registration" ); - return plugin; + return medPluginManager::instance().plugin("Manual Registration"); } void manualRegistrationToolBox::updateView() diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index dcafee990e..bf14d02d1d 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -758,7 +758,7 @@ void AlgorithmPaintToolBox::import() medAbstractData *output = dynamic_cast(m_maskData->clone()); copyMetaData(output, m_imageData); - medDataManager::instance()->importData(output, false); + medDataManager::instance().importData(output, false); maskHasBeenSaved = true; } @@ -1080,7 +1080,7 @@ void AlgorithmPaintToolBox::updateWandRegion(medAbstractImageView *view, QVector (m_imageData->identifier().contains("Vector"))|| (m_imageData->identifier().contains("2"))) { - medMessageController::instance()->showError(tr("Magic wand option is only available for 3D images"),3000); + medMessageController::instance().showError(tr("Magic wand option is only available for 3D images"),3000); return; } @@ -1525,9 +1525,7 @@ void AlgorithmPaintToolBox::updateMouseInteraction() dtkPlugin* AlgorithmPaintToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Algorithm Paint" ); - return plugin; + return medPluginManager::instance().plugin("Algorithm Paint"); } void AlgorithmPaintToolBox::setParameter(int channel, int value) @@ -2524,7 +2522,7 @@ void AlgorithmPaintToolBox::addViewEventFilter( medViewEventFilter *filter) { for(QUuid uuid : this->getWorkspace()->tabbedViewContainers()->containersSelected()) { - medViewContainer *container = medViewContainerManager::instance()->container(uuid); + medViewContainer *container = medViewContainerManager::instance().container(uuid); if(container) { filter->installOnView(container->view()); diff --git a/src/plugins/legacy/medBinaryOperation/medBinaryOperationToolBox.cpp b/src/plugins/legacy/medBinaryOperation/medBinaryOperationToolBox.cpp index d9b2e385ff..c3baa49464 100644 --- a/src/plugins/legacy/medBinaryOperation/medBinaryOperationToolBox.cpp +++ b/src/plugins/legacy/medBinaryOperation/medBinaryOperationToolBox.cpp @@ -112,9 +112,7 @@ bool medBinaryOperationToolBox::registered() dtkPlugin* medBinaryOperationToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Binary Operation" ); - return plugin; + return medPluginManager::instance().plugin("Binary Operation"); } medAbstractData* medBinaryOperationToolBox::processOutput() @@ -186,8 +184,8 @@ void medBinaryOperationToolBox::run() void medBinaryOperationToolBox::onSecondInputImported(const medDataIndex& index) { - dtkSmartPointer data = medDataManager::instance()->retrieveData(index); - d->dropsite->setPixmap(medDataManager::instance()->thumbnail(index).scaled(d->dropsite->sizeHint())); + dtkSmartPointer data = medDataManager::instance().retrieveData(index); + d->dropsite->setPixmap(medDataManager::instance().thumbnail(index).scaled(d->dropsite->sizeHint())); d->secondInput = data; } @@ -204,8 +202,8 @@ void medBinaryOperationToolBox::onDropSiteClicked() if (!roiFileName.isEmpty()) { - medDataManager* mdm = medDataManager::instance(); - connect(mdm, SIGNAL(dataAdded(const medDataIndex &)), this, SLOT(onRoiImported(const medDataIndex &))); - mdm->importPath(roiFileName, true, false); + medDataManager &mdm = medDataManager::instance(); + connect(&mdm, SIGNAL(dataAdded(const medDataIndex &)), this, SLOT(onRoiImported(const medDataIndex &))); + mdm.importPath(roiFileName, true, false); } } diff --git a/src/plugins/legacy/medComposerArea/CMakeLists.txt b/src/plugins/legacy/medComposerArea/CMakeLists.txt deleted file mode 100644 index 3fdec9cbac..0000000000 --- a/src/plugins/legacy/medComposerArea/CMakeLists.txt +++ /dev/null @@ -1,98 +0,0 @@ -################################################################################ -# -# medInria -# -# Copyright (c) INRIA 2013 - 2018. All rights reserved. -# See LICENSE.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even -# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -# PURPOSE. -# -################################################################################ - -project(medComposerArea) - -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) - -## ############################################################################# -## Setup version numbering -## ############################################################################# - -set(${PROJECT_NAME}_VERSION ${MEDINRIA_VERSION}) - -string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UP) -add_definitions(-D${PROJECT_NAME_UP}_VERSION="${${PROJECT_NAME}_VERSION}") - -## ############################################################################# -## Resolve dependencies -## ############################################################################# - -find_package(dtk REQUIRED) -include_directories(${dtk_INCLUDE_DIRS}) - -## ############################################################################# -## List Sources -## ############################################################################# - -list_source_files(${PROJECT_NAME} - ${CMAKE_CURRENT_SOURCE_DIR} - ) - - -## ############################################################################# -## include directories. -## ############################################################################# - -list_header_directories_to_include(${PROJECT_NAME} - ${${PROJECT_NAME}_HEADERS} - ) - -include_directories(${${PROJECT_NAME}_INCLUDE_DIRS} - ${MEDINRIA_INCLUDE_DIRS} - ) - - -## ############################################################################# -## Precompile headers -## ############################################################################# - -if(MEDINRIA_USE_PRECOMPILED_HEADERS) - add_precompiled_header(${PROJECT_NAME}_PCH - ${MEDPLUGINSPCH_H} - ${MEDPLUGINSPCH_CPP} - ${${PROJECT_NAME}_SOURCES} - ) -endif() - - -## ############################################################################# -## add library -## ############################################################################# - - -add_library(${PROJECT_NAME} SHARED - ${${PROJECT_NAME}_CFILES} - ) - - -## ############################################################################# -## Link -## ############################################################################# - -target_link_libraries(${PROJECT_NAME} - ${QT_LIBRARIES} - dtkCore - dtkLog - dtkComposer - medCore - ) - - -## ############################################################################# -## Install rules -## ############################################################################# - -set_plugin_install_rules(${PROJECT_NAME}) - diff --git a/src/plugins/legacy/medComposerArea/medComposerArea.cpp b/src/plugins/legacy/medComposerArea/medComposerArea.cpp deleted file mode 100644 index c19492e0ee..0000000000 --- a/src/plugins/legacy/medComposerArea/medComposerArea.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2018. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - - - -class medComposerAreaPrivate -{ -public: - dtkComposerWidget *composerWidget; - dtkComposerSceneNodeEditor *editor; - dtkComposerSceneModel *model; - dtkComposerSceneView *scene; - dtkComposerStackView *stack; - dtkComposerNodeFactoryView* nodes; - dtkComposerGraphView* graph; -}; - - -medComposerArea::medComposerArea(QWidget *parent): medAbstractArea(parent), - d(new medComposerAreaPrivate) -{ - d->composerWidget = new dtkComposerWidget; - QGridLayout *layout = new QGridLayout; - - layout->addWidget(d->composerWidget); - this->setLayout(layout); - - - d->composerWidget->view()->setCacheMode(QGraphicsView::CacheBackground); - - dtkComposerNodeFactoryExtension *extension(new medSubtractImageProcessComposerFactoryExtension); - d->composerWidget->factory()->extend(extension); - - - d->editor = new dtkComposerSceneNodeEditor; - d->editor->setScene(d->composerWidget->scene()); - d->editor->setStack(d->composerWidget->stack()); - d->editor->setGraph(d->composerWidget->graph()); - - d->model = new dtkComposerSceneModel; - d->model->setScene(d->composerWidget->scene()); - - d->scene = new dtkComposerSceneView; - d->scene->setScene(d->composerWidget->scene()); - d->scene->setModel(d->model); - - d->stack = new dtkComposerStackView; - d->stack->setStack(d->composerWidget->stack()); - - d->nodes = new dtkComposerNodeFactoryView; - d->nodes->setFactory(d->composerWidget->factory()); - - d->graph = new dtkComposerGraphView; - d->graph->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - d->graph->setScene(d->composerWidget->graph()); - d->graph->setVisible(false); - -// layout->addWidget(d->editor); -// layout->addWidget(d->scene); - layout->addWidget(d->nodes); -// layout->addWidget(d->graph); - -} - -medComposerArea::~medComposerArea() -{ - delete d; -} - -QString medComposerArea::title() const -{ - return "Composer"; -} - -QString medComposerArea::description() const -{ - return tr("Description of the composer area"); -} diff --git a/src/plugins/legacy/medComposerArea/medComposerArea.h b/src/plugins/legacy/medComposerArea/medComposerArea.h deleted file mode 100644 index 67b7841c71..0000000000 --- a/src/plugins/legacy/medComposerArea/medComposerArea.h +++ /dev/null @@ -1,38 +0,0 @@ -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2020. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#pragma once - -#include - -class medComposerAreaPrivate; - -class medComposerArea: public medAbstractArea -{ -public: - medComposerArea(QWidget* parent = nullptr); - ~medComposerArea(); - - virtual QString title() const; - virtual QString description() const; - -private: - medComposerAreaPrivate *d; -}; - -// /////////////////////////////////////////////////////////////////// - -inline medAbstractArea* medComposerAreaCreator(void) -{ - return new medComposerArea(); -} diff --git a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.cpp b/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.cpp deleted file mode 100644 index cdfb9686ca..0000000000 --- a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2018. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#include - -#include -#include - -void medComposerAreaPlugin::initialize(void) -{ - medGuiLayer::area::pluginFactory().record("medComposerArea", - medComposerAreaCreator); -} - -void medComposerAreaPlugin::uninitialize(void) -{ - -} - -// /////////////////////////////////////////////////////////////////// -// Plugin meta data -// /////////////////////////////////////////////////////////////////// - -DTK_DEFINE_PLUGIN(medItkSubtractImageProcess) diff --git a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.h b/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.h deleted file mode 100644 index 8863a9f036..0000000000 --- a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.h +++ /dev/null @@ -1,32 +0,0 @@ -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2018. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#pragma once - -#include - - -class medComposerAreaPlugin : public medAbstractAreaPlugin -{ - Q_OBJECT - Q_INTERFACES(medAbstractAreaPlugin) - Q_PLUGIN_METADATA(IID "fr.inria.medComposerAreaPlugin" FILE "medComposerAreaPlugin.json") - -public: - medComposerAreaPlugin(void) {} - virtual ~medComposerAreaPlugin(void) {} - -public: - void initialize(void); - void uninitialize(void); -}; diff --git a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.json b/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.json deleted file mode 100644 index f6eba730ef..0000000000 --- a/src/plugins/legacy/medComposerArea/medComposerAreaPlugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name" : "medComposerAreaPlugin", - "version" : "0.0.1", - "dependencies" : [] -} diff --git a/src/plugins/legacy/medCreateMeshFromMask/medCreateMeshFromMaskToolBox.cpp b/src/plugins/legacy/medCreateMeshFromMask/medCreateMeshFromMaskToolBox.cpp index f916321032..9af2e07df9 100644 --- a/src/plugins/legacy/medCreateMeshFromMask/medCreateMeshFromMaskToolBox.cpp +++ b/src/plugins/legacy/medCreateMeshFromMask/medCreateMeshFromMaskToolBox.cpp @@ -141,9 +141,7 @@ bool medCreateMeshFromMaskToolBox::registered() dtkPlugin* medCreateMeshFromMaskToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("Create Mesh From Mask"); - return plugin; + return medPluginManager::instance().plugin("Create Mesh From Mask"); } medAbstractData* medCreateMeshFromMaskToolBox::processOutput() diff --git a/src/plugins/legacy/medFilteringWorkspaceL/medFilteringWorkspaceL.cpp b/src/plugins/legacy/medFilteringWorkspaceL/medFilteringWorkspaceL.cpp index 8382e362ef..032433a3b0 100644 --- a/src/plugins/legacy/medFilteringWorkspaceL/medFilteringWorkspaceL.cpp +++ b/src/plugins/legacy/medFilteringWorkspaceL/medFilteringWorkspaceL.cpp @@ -145,7 +145,7 @@ void medFilteringWorkspaceL::importProcessOutput() QString generatedID = QUuid::createUuid().toString().replace("{","").replace("}",""); d->filterOutput->setMetaData ( medMetaDataKeys::SeriesID.key(), generatedID ); - medDataManager::instance()->importData(d->filterOutput); + medDataManager::instance().importData(d->filterOutput); d->outputContainer->addData(d->filterOutput); } @@ -169,5 +169,5 @@ void medFilteringWorkspaceL::open(const medDataIndex &index) { return; } - d->inputContainer->addData(medDataManager::instance()->retrieveData(index)); + d->inputContainer->addData(medDataManager::instance().retrieveData(index)); } diff --git a/src/plugins/legacy/medMaskApplication/medMaskApplicationToolBox.cpp b/src/plugins/legacy/medMaskApplication/medMaskApplicationToolBox.cpp index 233c3f10a1..008fec8c34 100644 --- a/src/plugins/legacy/medMaskApplication/medMaskApplicationToolBox.cpp +++ b/src/plugins/legacy/medMaskApplication/medMaskApplicationToolBox.cpp @@ -84,9 +84,7 @@ bool medMaskApplicationToolBox::registered() dtkPlugin* medMaskApplicationToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Mask Application" ); - return plugin; + return medPluginManager::instance().plugin("Mask Application"); } medAbstractData* medMaskApplicationToolBox::processOutput() @@ -124,8 +122,8 @@ void medMaskApplicationToolBox::run() void medMaskApplicationToolBox::importMask(const medDataIndex& index) { - d->mask = medDataManager::instance()->retrieveData(index); - d->maskDropSite->setPixmap(medDataManager::instance()->thumbnail(index).scaled(d->maskDropSite->sizeHint())); + d->mask = medDataManager::instance().retrieveData(index); + d->maskDropSite->setPixmap(medDataManager::instance().thumbnail(index).scaled(d->maskDropSite->sizeHint())); } void medMaskApplicationToolBox::clearMask(void) diff --git a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp index e5a854d3bf..e4d5db458c 100644 --- a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp +++ b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrection.cpp @@ -505,7 +505,7 @@ int medN4BiasCorrection::update(medAbstractData *inputData) { d->biasField->setData(biasFieldCropper->GetOutput()); medUtilities::setDerivedMetaData(d->biasField, d->input, "bias"); - medDataManager::instance()->importData(d->biasField, false); + medDataManager::instance().importData(d->biasField, false); } return medAbstractProcessLegacy::SUCCESS; diff --git a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrectionToolBox.cpp b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrectionToolBox.cpp index adc5fdd9fb..4bcd7d3760 100644 --- a/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrectionToolBox.cpp +++ b/src/plugins/legacy/medN4BiasCorrection/medN4BiasCorrectionToolBox.cpp @@ -176,9 +176,7 @@ bool medN4BiasCorrectionToolBox::registered() dtkPlugin* medN4BiasCorrectionToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin ( "N4 Bias Correction" ); - return plugin; + return medPluginManager::instance().plugin("N4 Bias Correction"); } medAbstractData* medN4BiasCorrectionToolBox::processOutput() diff --git a/src/plugins/legacy/medRemeshing/medRemeshingToolBox.cpp b/src/plugins/legacy/medRemeshing/medRemeshingToolBox.cpp index b1d8d5f96e..8c49588b0d 100644 --- a/src/plugins/legacy/medRemeshing/medRemeshingToolBox.cpp +++ b/src/plugins/legacy/medRemeshing/medRemeshingToolBox.cpp @@ -267,9 +267,7 @@ bool medRemeshingToolBox::registered() dtkPlugin* medRemeshingToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("Remeshing"); - return plugin; + return medPluginManager::instance().plugin("Remeshing"); } void medRemeshingToolBox::updateView() @@ -556,7 +554,7 @@ void medRemeshingToolBox::displayNewNumberOfCells(dtkSmartPointershowError(tr("Not possible to decimate more without changing the mesh topology"), 5000); + medMessageController::instance().showError(tr("Not possible to decimate more without changing the mesh topology"), 5000); d->decimateButton->setEnabled(false); } diff --git a/src/plugins/legacy/medSegmentation/medMagicWandCommand.cpp b/src/plugins/legacy/medSegmentation/medMagicWandCommand.cpp index 86939f6868..dae5f58e96 100644 --- a/src/plugins/legacy/medSegmentation/medMagicWandCommand.cpp +++ b/src/plugins/legacy/medSegmentation/medMagicWandCommand.cpp @@ -75,7 +75,7 @@ void medMagicWandCommand::paint() (this->options()->data->identifier().contains("Vector"))|| (this->options()->data->identifier().contains("2"))) { - medMessageController::instance()->showError("Magic wand option is only available for 3D images",3000); + medMessageController::instance().showError("Magic wand option is only available for 3D images",3000); return; } diff --git a/src/plugins/legacy/medVtkFibersData/interactors/medVtkFibersDataInteractor.cpp b/src/plugins/legacy/medVtkFibersData/interactors/medVtkFibersDataInteractor.cpp index 6843e230a5..e28fa3f044 100644 --- a/src/plugins/legacy/medVtkFibersData/interactors/medVtkFibersDataInteractor.cpp +++ b/src/plugins/legacy/medVtkFibersData/interactors/medVtkFibersDataInteractor.cpp @@ -593,7 +593,7 @@ void medVtkFibersDataInteractor::setBoxVisibility(bool visible) { if (d->view && d->view->orientation() != medImageView::VIEW_ORIENTATION_3D) { - medMessageController::instance()->showError("View must be in 3D mode to activate the bundling box", 3000); + medMessageController::instance().showError("View must be in 3D mode to activate the bundling box", 3000); d->manager->SetBoxWidget(false); return; } @@ -847,7 +847,7 @@ void medVtkFibersDataInteractor::saveBundlesInDataBase() QString generatedID = QUuid::createUuid().toString().replace("{","").replace("}",""); tmpBundle->setMetaData ( medMetaDataKeys::SeriesID.key(), generatedID ); - medDataManager::instance()->importData(tmpBundle); + medDataManager::instance().importData(tmpBundle); ++it; } @@ -1252,8 +1252,8 @@ void medVtkFibersDataInteractor::loadRoiFromFile() if (roiFileName.isEmpty()) return; - d->roiImportUuid = medDataManager::instance()->importPath(roiFileName,false); - connect(medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), + d->roiImportUuid = medDataManager::instance().importPath(roiFileName,false); + connect(&medDataManager::instance(), SIGNAL(dataImported(medDataIndex,QUuid)), this, SLOT(importROI(medDataIndex,QUuid)), Qt::UniqueConnection); } @@ -1267,7 +1267,7 @@ void medVtkFibersDataInteractor::importROI(const medDataIndex& index, QUuid uuid void medVtkFibersDataInteractor::importROI(const medDataIndex& index) { - dtkSmartPointer data = medDataManager::instance()->retrieveData(index); + dtkSmartPointer data = medDataManager::instance().retrieveData(index); // we accept only ROIs (itkDataImageUChar3) // TODO try dynamic_cast of medAbstractMaskData would be better - RDE @@ -1281,11 +1281,11 @@ void medVtkFibersDataInteractor::importROI(const medDataIndex& index) data->identifier() != "itkDataImageFloat3" && data->identifier() != "itkDataImageDouble3")) { - medMessageController::instance()->showError(tr("Unable to load ROI, format not supported yet"), 3000); + medMessageController::instance().showError(tr("Unable to load ROI, format not supported yet"), 3000); return; } - d->dropOrOpenRoi->setPixmap(medDataManager::instance()->thumbnail(index).scaled(d->dropOrOpenRoi->sizeHint())); + d->dropOrOpenRoi->setPixmap(medDataManager::instance().thumbnail(index).scaled(d->dropOrOpenRoi->sizeHint())); d->setROI(data); d->setROI(data); @@ -1572,7 +1572,7 @@ void medVtkFibersDataInteractor::saveCurrentBundle() QString generatedID = QUuid::createUuid().toString().replace("{","").replace("}",""); savedBundle->setMetaData ( medMetaDataKeys::SeriesID.key(), generatedID ); - medDataManager::instance()->importData(savedBundle); + medDataManager::instance().importData(savedBundle); } void medVtkFibersDataInteractor::removeCurrentBundle() diff --git a/src/plugins/legacy/medVtkView/medVtkView.cpp b/src/plugins/legacy/medVtkView/medVtkView.cpp index 049ccfbc17..b8e5a2c1c7 100644 --- a/src/plugins/legacy/medVtkView/medVtkView.cpp +++ b/src/plugins/legacy/medVtkView/medVtkView.cpp @@ -57,9 +57,6 @@ class medVtkViewPrivate { public: - // internal state - vtkImageView *currentView; //2d or 3d depending on the navigator orientation. - vtkInteractorStyle *interactorStyle2D; // views @@ -84,7 +81,6 @@ medVtkView::medVtkView(QObject* parent): medAbstractImageView(parent), d(new medVtkViewPrivate) { // setup initial internal state of the view - d->currentView = nullptr; d->interactorStyle2D = nullptr; // construct render window @@ -462,22 +458,18 @@ void medVtkView::displayDataInfo(uint layer) QImage medVtkView::buildThumbnail(const QSize &size) { - // We dont want to send things that would ending up on updating gui things. - this->blockSignals(true); int w(size.width()), h(size.height()); QImage thumbnail = d->viewWidget->grabFramebuffer(); thumbnail = thumbnail.scaledToHeight(h, Qt::SmoothTransformation); thumbnail = thumbnail.copy((thumbnail.width()-w)/2, 0, w, h); - this->blockSignals(false); return thumbnail; } void medVtkView::buildMouseInteractionParamPool(uint layer) { - medSettingsManager * mnger = medSettingsManager::instance(); - QString interaction = mnger->value("interactions","mouse", "Windowing").toString(); + QString interaction = medSettingsManager::instance().value("interactions","mouse", "Windowing").toString(); QList params; @@ -496,7 +488,7 @@ void medVtkView::buildMouseInteractionParamPool(uint layer) // add all mouse interaction params of the view in the "Mouse interaction" pool for(medBoolParameterL* param : params) { - medParameterPoolManagerL::instance()->linkParameter(param, "Mouse Interaction"); + medParameterPoolManagerL::instance().linkParameter(param, "Mouse Interaction"); connect(param, SIGNAL(valueChanged(bool)), this, SLOT(saveMouseInteractionSettings(bool))); // and activate the new inserted parameter according to what was activated in other views @@ -505,7 +497,7 @@ void medVtkView::buildMouseInteractionParamPool(uint layer) } // Deal with rubber Zoom mode. - medParameterPoolManagerL::instance()->linkParameter(d->rubberBandZoomParameter, "Mouse Interaction"); + medParameterPoolManagerL::instance().linkParameter(d->rubberBandZoomParameter, "Mouse Interaction"); } void medVtkView::saveMouseInteractionSettings(bool parameterEnabled) @@ -514,7 +506,9 @@ void medVtkView::saveMouseInteractionSettings(bool parameterEnabled) { medBoolParameterL *parameter = dynamic_cast(this->sender()); if(parameter) - medSettingsManager::instance()->setValue("interactions","mouse", parameter->name()); + { + medSettingsManager::instance().setValue("interactions","mouse", parameter->name()); + } } } @@ -525,8 +519,7 @@ void medVtkView::enableRubberBandZoom(bool enable) if(enable) { - medSettingsManager * mnger = medSettingsManager::instance(); - QString interaction = mnger->value("interactions","mouse", "Windowing").toString(); + QString interaction = medSettingsManager::instance().value("interactions","mouse", "Windowing").toString(); vtkInriaInteractorStyleRubberBandZoom * interactorStyle = vtkInriaInteractorStyleRubberBandZoom::New(); interactorStyle->AddObserver( vtkImageView2DCommand::CameraZoomEvent,d->observer,0 ); diff --git a/src/plugins/legacy/medVtkView/medVtkViewNavigator.cpp b/src/plugins/legacy/medVtkView/medVtkViewNavigator.cpp index d76e9fe8cc..21673c88c4 100644 --- a/src/plugins/legacy/medVtkView/medVtkViewNavigator.cpp +++ b/src/plugins/legacy/medVtkView/medVtkViewNavigator.cpp @@ -564,7 +564,7 @@ bool medVtkViewNavigator::setRotationAngle(double angle) foreach(medDataIndex index, d->parent->dataList()) { - medAbstractData *data = medDataManager::instance()->retrieveData(index); + medAbstractData *data = medDataManager::instance().retrieveData(index); // We only apply rotation on meshes if (data && data->identifier().contains("vtkDataMesh") ) diff --git a/src/plugins/legacy/medVtkView/medVtkViewPlugin.cpp b/src/plugins/legacy/medVtkView/medVtkViewPlugin.cpp index f91952dcfe..2c1ca257e3 100644 --- a/src/plugins/legacy/medVtkView/medVtkViewPlugin.cpp +++ b/src/plugins/legacy/medVtkView/medVtkViewPlugin.cpp @@ -18,46 +18,25 @@ #include -// ///////////////////////////////////////////////////////////////// -// medVtkViewPluginPrivate -// ///////////////////////////////////////////////////////////////// - -class medVtkViewPluginPrivate -{ -public: - // Class variables go here. -}; - -// ///////////////////////////////////////////////////////////////// -// medVtkViewPlugin -// ///////////////////////////////////////////////////////////////// - medVtkViewPlugin::medVtkViewPlugin(QObject *parent) : - dtkPlugin(parent), d(new medVtkViewPluginPrivate) -{ - -} - -medVtkViewPlugin::~medVtkViewPlugin() + medPluginLegacy(parent) { - delete d; - d = nullptr; } bool medVtkViewPlugin::initialize() { - if (!medVtkView::registered()) { qWarning() << "Unable to register medVtkView type"; } - - if (!medVtkViewNavigator::registered()) { qWarning() << "Unable to register medVtkViewNavigator type"; } + if (!medVtkView::registered()) + { + qWarning() << "Unable to register medVtkView type"; + } + if (!medVtkViewNavigator::registered()) + { + qWarning() << "Unable to register medVtkViewNavigator type"; + } return true; } -bool medVtkViewPlugin::uninitialize() -{ - return true; -} - QString medVtkViewPlugin::name() const { return "medVtkViewPlugin"; @@ -65,7 +44,7 @@ QString medVtkViewPlugin::name() const QString medVtkViewPlugin::contact() const { - return "medinria team"; + return "medInria team"; } QStringList medVtkViewPlugin::authors() const @@ -75,14 +54,6 @@ QStringList medVtkViewPlugin::authors() const return list; } -QStringList medVtkViewPlugin::contributors() const -{ - QStringList list; - list << "rdebroiz"; - - return list; -} - QString medVtkViewPlugin::version() const { return MEDVTKVIEWPLUGIN_VERSION; @@ -91,15 +62,15 @@ QString medVtkViewPlugin::version() const QString medVtkViewPlugin::description() const { return tr("View Plugin\n
" - "Bring a view based on medVtkinria"); + "Create views based on medVtkinria"); } QStringList medVtkViewPlugin::tags() const { - return QStringList() << "medVtk" << "view"; + return types(); } QStringList medVtkViewPlugin::types() const { - return QStringList() << medVtkView::s_identifier(); + return QStringList() << "medVtkView"; } diff --git a/src/plugins/legacy/medVtkView/medVtkViewPlugin.h b/src/plugins/legacy/medVtkView/medVtkViewPlugin.h index e3fc7d15c7..cedd908120 100644 --- a/src/plugins/legacy/medVtkView/medVtkViewPlugin.h +++ b/src/plugins/legacy/medVtkView/medVtkViewPlugin.h @@ -12,13 +12,11 @@ PURPOSE. =========================================================================*/ -#include +#include #include -class medVtkViewPluginPrivate; - -class MEDVTKVIEWPLUGIN_EXPORT medVtkViewPlugin : public dtkPlugin +class MEDVTKVIEWPLUGIN_EXPORT medVtkViewPlugin : public medPluginLegacy { Q_OBJECT Q_PLUGIN_METADATA(IID "fr.inria.medVtkViewPlugin" FILE "medVtkViewPlugin.json") @@ -26,23 +24,14 @@ class MEDVTKVIEWPLUGIN_EXPORT medVtkViewPlugin : public dtkPlugin public: medVtkViewPlugin(QObject *parent = nullptr); - ~medVtkViewPlugin(); virtual bool initialize(); - virtual bool uninitialize(); virtual QString name() const; virtual QString description() const; virtual QString version() const; - virtual QStringList authors() const; virtual QString contact() const; - virtual QStringList contributors() const; - - virtual QStringList tags() const; virtual QStringList types() const; - -private: - medVtkViewPluginPrivate *d; }; diff --git a/src/plugins/legacy/meshManipulation/meshManipulationToolBox.cpp b/src/plugins/legacy/meshManipulation/meshManipulationToolBox.cpp index 4e5f436aaf..35ea4785e5 100644 --- a/src/plugins/legacy/meshManipulation/meshManipulationToolBox.cpp +++ b/src/plugins/legacy/meshManipulation/meshManipulationToolBox.cpp @@ -506,7 +506,7 @@ void meshManipulationToolBox::computeData() transformFilter->Delete(); medUtilities::setDerivedMetaData(output, data, "manually modified"); - medDataManager::instance()->importData(output, false); + medDataManager::instance().importData(output, false); _outputs.append(output.data()); } } @@ -717,9 +717,7 @@ void meshManipulationToolBox::importTransform() dtkPlugin* meshManipulationToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("Mesh Manipulation"); - return plugin; + return medPluginManager::instance().plugin("Mesh Manipulation"); } void meshManipulationToolBox::enableScaling(bool state) diff --git a/src/plugins/legacy/meshMapping/meshMappingToolBox.cpp b/src/plugins/legacy/meshMapping/meshMappingToolBox.cpp index 78b959bd7e..6e91156add 100644 --- a/src/plugins/legacy/meshMapping/meshMappingToolBox.cpp +++ b/src/plugins/legacy/meshMapping/meshMappingToolBox.cpp @@ -86,9 +86,7 @@ bool meshMappingToolBox::registered() dtkPlugin* meshMappingToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("Mesh Mapping"); - return plugin; + return medPluginManager::instance().plugin("Mesh Mapping"); } medAbstractData* meshMappingToolBox::processOutput() diff --git a/src/plugins/legacy/polygonRoi/polygonLabel.cpp b/src/plugins/legacy/polygonRoi/polygonLabel.cpp index 5bd31117ac..797dca09f3 100644 --- a/src/plugins/legacy/polygonRoi/polygonLabel.cpp +++ b/src/plugins/legacy/polygonRoi/polygonLabel.cpp @@ -584,7 +584,7 @@ void polygonLabel::createMask(int label, QString &desc) output->setMetaData(medMetaDataKeys::Toolbox.key(), "PolygonROI"); output->setMetaData(medMetaDataKeys::OriginalDataUID.key(), inputData->metadata(medMetaDataKeys::SeriesInstanceUID.key())); output->setMetaData(medMetaDataKeys::OriginalDataDesc.key(), inputData->metadata(medMetaDataKeys::SeriesDescription.key())); - medDataManager::instance()->importData(output, false); + medDataManager::instance().importData(output, false); } void polygonLabel::SetMasterRoi() diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index 2907c134a8..3d7614a0ce 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -33,8 +33,8 @@ const char *polygonRoiToolBox::generateBinaryImageButtonName = "generateBinaryIm polygonRoiToolBox::polygonRoiToolBox(QWidget *parent ) : medAbstractSelectableToolBox(parent), activeDataIndex() { - medSettingsManager *manager = medSettingsManager::instance(); - QString speciality = manager->value("startup", "default_segmentation_speciality", "Default").toString(); + medSettingsManager &manager = medSettingsManager::instance(); + QString speciality = manager.value("startup", "default_segmentation_speciality", "Default").toString(); if (speciality=="Urology") { specialityPreference = 1; @@ -156,9 +156,7 @@ bool polygonRoiToolBox::registered() dtkPlugin* polygonRoiToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin ( "Polygon ROI" ); - return plugin; + return medPluginManager::instance().plugin("Polygon ROI"); } medAbstractData *polygonRoiToolBox::processOutput() diff --git a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp index 490e83179a..482e9d9e39 100644 --- a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp +++ b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp @@ -787,7 +787,7 @@ void baseViewEvent::saveAllContours() contourOutput->setData(&contoursData, 1); outputDataSet->Delete(); - medDataManager::instance()->importData(contourOutput, false); + medDataManager::instance().importData(contourOutput, false); } } @@ -1023,7 +1023,7 @@ void baseViewEvent::saveContour(polygonLabel *label) contourOutput->setData(&contoursData, 1); outputDataSet->Delete(); - medDataManager::instance()->importData(contourOutput, false); + medDataManager::instance().importData(contourOutput, false); } diff --git a/src/plugins/legacy/qtdcmDataSource/qtdcmDataSourceSerieToolBox.cpp b/src/plugins/legacy/qtdcmDataSource/qtdcmDataSourceSerieToolBox.cpp index a66dfafa7e..8fae4b6616 100644 --- a/src/plugins/legacy/qtdcmDataSource/qtdcmDataSourceSerieToolBox.cpp +++ b/src/plugins/legacy/qtdcmDataSource/qtdcmDataSourceSerieToolBox.cpp @@ -51,12 +51,6 @@ qtdcmDataSourceSerieToolBox::qtdcmDataSourceSerieToolBox(QWidget *parent) : medT this->addWidget(d->main); this->setTitle("Series preview and import"); - - // Add about plugin - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("qtdcmDataSourcePlugin"); - setAboutPluginButton(plugin); - setAboutPluginVisibility(true); } qtdcmDataSourceSerieToolBox::~qtdcmDataSourceSerieToolBox() diff --git a/src/plugins/legacy/reformat/medCropToolBox.cpp b/src/plugins/legacy/reformat/medCropToolBox.cpp index de1083426b..62a70f87e8 100644 --- a/src/plugins/legacy/reformat/medCropToolBox.cpp +++ b/src/plugins/legacy/reformat/medCropToolBox.cpp @@ -152,7 +152,7 @@ medCropToolBox::~medCropToolBox() dtkPlugin* medCropToolBox::plugin() { - return medPluginManager::instance()->plugin("Reformat"); + return medPluginManager::instance().plugin("Reformat"); } medAbstractData* medCropToolBox::processOutput() @@ -471,7 +471,7 @@ void medCropToolBoxPrivate::generateOutput() } else { - medMessageController::instance()->showError("Drop a 3D volume in the view", 3000); + medMessageController::instance().showError("Drop a 3D volume in the view", 3000); qDebug()<<__FILE__<<":"<<__LINE__<identifier(); } } @@ -541,6 +541,6 @@ void medCropToolBoxPrivate::importOutput() { for(medAbstractData *output : outputData) { - medDataManager::instance()->importData(output, false); + medDataManager::instance().importData(output, false); } } diff --git a/src/plugins/legacy/reformat/resliceToolBox.cpp b/src/plugins/legacy/reformat/resliceToolBox.cpp index b938033118..84f7232ce3 100644 --- a/src/plugins/legacy/reformat/resliceToolBox.cpp +++ b/src/plugins/legacy/reformat/resliceToolBox.cpp @@ -160,9 +160,7 @@ bool resliceToolBox::registered() dtkPlugin* resliceToolBox::plugin() { - medPluginManager* pm = medPluginManager::instance(); - dtkPlugin* plugin = pm->plugin ( "Reformat" ); - return plugin; + return medPluginManager::instance().plugin("Reformat"); } void resliceToolBox::startReformat() @@ -216,17 +214,17 @@ void resliceToolBox::startReformat() } else { - medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); + medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); } } else { - medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); + medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); } } else { - medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); + medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); } } @@ -308,7 +306,7 @@ void resliceToolBox::saveReformatedImage() generateReformatedImage(); if (d->reformatedImage && d->reformatedImage->data()) { - medDataManager::instance()->importData(d->reformatedImage, false); + medDataManager::instance().importData(d->reformatedImage, false); } } diff --git a/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp b/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp index d89544f850..c9d1636a0a 100644 --- a/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp +++ b/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp @@ -134,9 +134,7 @@ QString VarSegToolBox::s_name() dtkPlugin* VarSegToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("Variational Segmentation"); - return plugin; + return medPluginManager::instance().plugin("Variational Segmentation"); } void VarSegToolBox::updateLandmarksRenderer(QString key, QString value) @@ -209,7 +207,7 @@ void VarSegToolBox::addBinaryImage() d->output->setData(img); medUtilities::setDerivedMetaData(d->output, d->originalInput, "VarSegMask"); - medDataManager::instance()->importData(d->output, false); + medDataManager::instance().importData(d->output, false); } } @@ -248,7 +246,7 @@ void VarSegToolBox::applyMaskToImage() void VarSegToolBox::displayOutput() { medUtilities::setDerivedMetaData(d->process->output(), d->originalInput, "VarSegApplied"); - medDataManager::instance()->importData(d->process->output(), false); + medDataManager::instance().importData(d->process->output(), false); typedef itk::Image binaryType; binaryType::Pointer img = d->controller->GetBinaryImage(); diff --git a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp index 470b12d3ad..e8b99bde0a 100644 --- a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp +++ b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp @@ -270,9 +270,7 @@ void voiCutterToolBox::onViewClosed() dtkPlugin* voiCutterToolBox::plugin() { - medPluginManager *pm = medPluginManager::instance(); - dtkPlugin *plugin = pm->plugin("VOI Cutter"); - return plugin; + return medPluginManager::instance().plugin("VOI Cutter"); } medAbstractData *voiCutterToolBox::processOutput() @@ -397,7 +395,7 @@ void voiCutterToolBox::saveImage() if (d->resultData) { fillOutputMetaData(); - medDataManager::instance()->importData(d->resultData, false); + medDataManager::instance().importData(d->resultData, false); } } From e1aa63c55c570fd05e7b993b4b3d418ccb65d24e Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 30 Oct 2023 10:21:53 +0100 Subject: [PATCH 40/98] [medVtkImageInfo] proper reset of a complex structure (#1151) --- .../legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp | 2 +- .../legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp index 428018b6a0..efe7471309 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp @@ -58,7 +58,7 @@ void vtkImage2DDisplay::SetInputData(vtkImageData *pi_poVtkImage) } else { - memset(&m_sVtkImageInfo, 0, sizeof(m_sVtkImageInfo)); + m_sVtkImageInfo = medVtkImageInfo(); } } diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp index 539a2d5a22..3c5586f6e2 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp @@ -52,7 +52,7 @@ void vtkImage3DDisplay::SetInputData(vtkImageData *pi_poVtkImage) } else { - memset(&m_sVtkImageInfo, 0, sizeof(m_sVtkImageInfo)); + m_sVtkImageInfo = medVtkImageInfo(); } } From ece6f9cb42b48ff7f67737a1117ab2df67104359 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 30 Oct 2023 10:48:38 +0100 Subject: [PATCH 41/98] [Instances] adapt new singleton system to new code (#1161) --- src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp | 2 +- .../legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp index 63a4c086df..d89f896d4d 100644 --- a/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp +++ b/src/layers/legacy/medCoreLegacy/data/medAbstractData.cpp @@ -194,7 +194,7 @@ QImage medAbstractData::generateThumbnail(QSize size) { QImage thumbnail; - if(medSettingsManager::instance()->value("Browser", "thumbnail_creation_setting").toBool()) + if(medSettingsManager::instance().value("Browser", "thumbnail_creation_setting").toBool()) { if (QThread::currentThread() != QApplication::instance()->thread()) { diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp index 4674968e6b..3a537ffb17 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medActionsToolBox.cpp @@ -101,7 +101,7 @@ medActionsToolBox::medActionsToolBox( QWidget *parent /*= 0*/, bool FILE_SYSTEM auto thumbnailCreationSetting = new QCheckBox("Generate a thumbnail at import"); thumbnailCreationSetting->setToolTip("Generating a thumbnail uses RAM, so it can be useful to disable it " "if you're short of memory and have import crashes."); - auto thumbnailValue = medSettingsManager::instance()->value("Browser", + auto thumbnailValue = medSettingsManager::instance().value("Browser", "thumbnail_creation_setting", true).toBool(); // Default value to true thumbnailCreationSetting->setChecked(thumbnailValue); @@ -367,5 +367,5 @@ void medActionsToolBox::initializeItemToActionsMap() void medActionsToolBox::thumbnailSettingClicked(bool state) { - medSettingsManager::instance()->setValue("Browser", "thumbnail_creation_setting", state); + medSettingsManager::instance().setValue("Browser", "thumbnail_creation_setting", state); } From d1d8d98db71b73aa79b86f70591d6dcf10327da9 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 30 Oct 2023 10:57:55 +0100 Subject: [PATCH 42/98] [vtkImageInfo] an other way to reset the complex structure and please gcc (#1162) --- .../legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp | 2 +- .../legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp index efe7471309..337c522763 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage2DDisplay.cpp @@ -58,7 +58,7 @@ void vtkImage2DDisplay::SetInputData(vtkImageData *pi_poVtkImage) } else { - m_sVtkImageInfo = medVtkImageInfo(); + memset(reinterpret_cast(&m_sVtkImageInfo), 0, sizeof(m_sVtkImageInfo)); } } diff --git a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp index 3c5586f6e2..5fe591c4f8 100644 --- a/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp +++ b/src/layers/legacy/medVtkInria/vtkImageView/vtkImage3DDisplay.cpp @@ -52,7 +52,7 @@ void vtkImage3DDisplay::SetInputData(vtkImageData *pi_poVtkImage) } else { - m_sVtkImageInfo = medVtkImageInfo(); + memset(reinterpret_cast(&m_sVtkImageInfo), 0, sizeof(m_sVtkImageInfo)); } } From 1b2743ccd5c8498aaa4795b1e48401ac394a87fc Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Tue, 31 Oct 2023 11:59:12 +0100 Subject: [PATCH 43/98] [medToolBoxHeader] no need to delete png (#1163) --- .../legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp index bbadb0393f..0cb596e48c 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medToolBoxHeader.cpp @@ -43,8 +43,6 @@ medToolBoxHeader::medToolBoxHeader(QWidget *parent) : QFrame(parent), d(new medT medToolBoxHeader::~medToolBoxHeader(void) { - delete d->png; - delete d; d = nullptr; } From 4f41117aa8c9ee14a89099123930141d86800d82 Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 8 Nov 2023 15:42:29 +0100 Subject: [PATCH 44/98] [medCropToolBox] fix error return value (#1164) --- src/plugins/legacy/reformat/medCropToolBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/legacy/reformat/medCropToolBox.cpp b/src/plugins/legacy/reformat/medCropToolBox.cpp index 62a70f87e8..d22b72b2fd 100644 --- a/src/plugins/legacy/reformat/medCropToolBox.cpp +++ b/src/plugins/legacy/reformat/medCropToolBox.cpp @@ -496,7 +496,7 @@ int medCropToolBoxPrivate::extractCroppedImage(medAbstractData* input, int* minI } if (maxIndices[i] < 0 || minIndices[i] >= static_cast(imageSize[i])) { - return 0; + return medAbstractProcessLegacy::FAILURE; } desiredStart[i] = std::max(minIndices[i], 0); desiredSize[i] = std::min(maxIndices[i] + 1, static_cast(imageSize[i])) - desiredStart[i]; From 6041aebcb5fb205fd9102a60fc197dde797bb684 Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 8 Nov 2023 15:52:32 +0100 Subject: [PATCH 45/98] workaround for Inrimage exporting on 3.4 (#1166) --- src/layers/legacy/medImageIO/medInrimageImageIO.cpp | 8 ++++---- .../itkINRDataImageWriter/itkINRDataImageWriter.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/layers/legacy/medImageIO/medInrimageImageIO.cpp b/src/layers/legacy/medImageIO/medInrimageImageIO.cpp index 8b45a54b83..ad332e19f9 100644 --- a/src/layers/legacy/medImageIO/medInrimageImageIO.cpp +++ b/src/layers/legacy/medImageIO/medInrimageImageIO.cpp @@ -391,9 +391,9 @@ void InrimageImageIO::GetRotationAnglesFromMatrix(const vnl_matrix &rot r[2] *= -1; // determine wether r = +/- theta*n - sin_r[0] = ( rotationMatrix(2,1) - rotationMatrix(1,2) ); - sin_r[1] = ( rotationMatrix(0,2) - rotationMatrix(2,0) ); - sin_r[2] = ( rotationMatrix(1,0) - rotationMatrix(0,1) ); + sin_r.push_back( rotationMatrix(2,1) - rotationMatrix(1,2) ); + sin_r.push_back( rotationMatrix(0,2) - rotationMatrix(2,0) ); + sin_r.push_back( rotationMatrix(1,0) - rotationMatrix(0,1) ); // determine the most significant term unsigned int k = 0; @@ -941,7 +941,7 @@ ::Write(const void* buffer) vz = 1.0; /* write header information */ - sprintf(buf, "#INRIMAGE-4#{\nXDIM=%lu\nYDIM=%lu\nZDIM=%lu\nVDIM=%d\nTYPE=%s\nPIXSIZE=%i bits\n%sCPU=%s\nVX=%f\nVY=%f\nVZ=%f\nTX=%f\nTY=%f\nTZ=%f\nRX=%f\nRY=%f\nRZ=%f\n#GEOMETRY=CARTESIAN\n", + sprintf(buf, "#INRIMAGE-4#{\nXDIM=%llu\nYDIM=%llu\nZDIM=%llu\nVDIM=%d\nTYPE=%s\nPIXSIZE=%i bits\n%sCPU=%s\nVX=%f\nVY=%f\nVZ=%f\nTX=%f\nTY=%f\nTZ=%f\nRX=%f\nRY=%f\nRZ=%f\n#GEOMETRY=CARTESIAN\n", this->GetDimensions(0), this->GetDimensions(1), this->GetDimensions(2), this->GetNumberOfComponents(), type.c_str(), pixsize, scale, endianness.c_str(), vx, vy, vz, diff --git a/src/plugins/legacy/itkINRDataImageWriter/itkINRDataImageWriter.cpp b/src/plugins/legacy/itkINRDataImageWriter/itkINRDataImageWriter.cpp index 8c2e12b5e0..d0fed9a9cb 100644 --- a/src/plugins/legacy/itkINRDataImageWriter/itkINRDataImageWriter.cpp +++ b/src/plugins/legacy/itkINRDataImageWriter/itkINRDataImageWriter.cpp @@ -66,7 +66,7 @@ QStringList itkINRDataImageWriter::handled() const { QStringList itkINRDataImageWriter::supportedFileExtensions() const { - return QStringList() << ".inr" << ".inr.gz"; + return QStringList() << ".inr.gz"; } QString itkINRDataImageWriter::identifier() const { From 4f9a088f190bbbea9cc0c543b64df5bbad37fa7d Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 23 Nov 2023 10:25:30 +0100 Subject: [PATCH 46/98] [DCMTKreader] roll back on a change on static Qlist --- .../legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp index d1a75a0347..f68f5b4143 100644 --- a/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp +++ b/src/plugins/legacy/itkDataImage/readers/itkDCMTKDataImageReader.cpp @@ -160,12 +160,6 @@ void itkDCMTKDataImageReaderPrivate::threadDone(itk::DCMTKImageIO::Pointer io) if (ioThreads->size() == 0) ioPointers->clear(); - - delete ioPointers; - ioPointers = nullptr; - - delete ioThreads; - ioThreads = nullptr; } void itkDCMTKDataImageReaderPrivate::initialiseStatic() From 2ce112a28293d61be106daf58b2de6b176e67c06 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 23 Nov 2023 15:43:51 +0100 Subject: [PATCH 47/98] [SlashScreen] use directly QSplashScreen & update splash medInria logo --- src/app/medInria/main.cpp | 28 +- src/app/medInria/medApplication.cpp | 12 + src/app/medInria/medSplashScreen.cpp | 76 ----- src/app/medInria/medSplashScreen.h | 45 --- .../resources/pixmaps/medInria-splash.png | Bin 39695 -> 28283 bytes .../resources/pixmaps/medInriaLogo.svg | 292 ++++++++++++++---- 6 files changed, 244 insertions(+), 209 deletions(-) delete mode 100644 src/app/medInria/medSplashScreen.cpp delete mode 100644 src/app/medInria/medSplashScreen.h diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index d3eced0d48..21d9a629b6 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -25,8 +25,6 @@ #include #include -#include - #include #include #include @@ -90,14 +88,9 @@ int main(int argc,char* argv[]) fmt.setAlphaBufferSize(1); fmt.setStereo(false); fmt.setSamples(0); - QSurfaceFormat::setDefaultFormat(fmt); - // this needs to be done before creating the QApplication object, as per the - // Qt doc, otherwise there are some edge cases where the style is not fully applied - //QApplication::setStyle("plastique"); medApplication application(argc,argv); - medSplashScreen splash(QPixmap(":/pixmaps/medInria-logo-homepage.png")); setlocale(LC_NUMERIC, "C"); QLocale::setDefault(QLocale("C")); @@ -116,16 +109,6 @@ int main(int argc,char* argv[]) return 1; } - // Do not show the splash screen in debug builds because it hogs the - // foreground, hiding all other windows. This makes debugging the startup - // operations difficult. - - #if !defined(_DEBUG) - bool show_splash = true; - #else - bool show_splash = false; - #endif - QStringList posargs; for (int i=1;imainWindow = nullptr; + d->splashScreen = new QSplashScreen(QPixmap(":/pixmaps/medInria-splash.png"), + Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); + d->splashScreen->show(); + this->processEvents(); + this->setApplicationName("medInria"); this->setApplicationVersion(MEDINRIA_VERSION); this->setOrganizationName("inria"); @@ -98,6 +104,12 @@ void medApplication::setMainWindow(medMainWindow *mw) QVariant var = QVariant::fromValue((QObject*)d->mainWindow); this->setProperty("MainWindow",var); d->systemOpenInstructions.clear(); + + // Wait until the app is displayed to close itself + if (d->splashScreen) + { + d->splashScreen->finish(d->mainWindow); + } } void medApplication::redirectMessageToSplash(const QString &message) diff --git a/src/app/medInria/medSplashScreen.cpp b/src/app/medInria/medSplashScreen.cpp deleted file mode 100644 index aebb682cc9..0000000000 --- a/src/app/medInria/medSplashScreen.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2019. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#include - -#include -#include -#include -#include - -class medSplashScreenPrivate -{ -public: - QPixmap pixmap; - int alignment; - QColor color; -}; - -//////////////////////////////////////////////////////////////////////////// -medSplashScreen::medSplashScreen(const QPixmap& thePixmap) - : QWidget(0, Qt::SplashScreen |Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint) - , d(new medSplashScreenPrivate) -{ - // Graphic - d->pixmap = thePixmap; - d->color = Qt::white; - - // Geometry - d->alignment = Qt::AlignBottom|Qt::AlignLeft; - setFixedSize(d->pixmap.size()); - - QRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height()); - - // Get back the previous screen used to display the application - int currentScreen = 0; - QVariant currentScreenQV = medSettingsManager::instance().value("medMainWindow", "currentScreen"); - if (!currentScreenQV.isNull()) - { - currentScreen = currentScreenQV.toInt(); - - // If the previous used screen has been removed, initialization - if (currentScreen >= QApplication::desktop()->screenCount()) - { - currentScreen = 0; - } - } - - // Move the Splash screen at the center of the chosen screen - move(QApplication::desktop()->screenGeometry(currentScreen).center() - r.center()); -} - -medSplashScreen::~medSplashScreen() -{ - delete d; - d = nullptr; -} - -//////////////////////////////////////////////////////////////////////////// -void medSplashScreen::paintEvent(QPaintEvent* pe) -{ - Q_UNUSED(pe); - - QPainter aPainter(this); - aPainter.drawPixmap(rect(), d->pixmap); - aPainter.setPen(d->color); -} diff --git a/src/app/medInria/medSplashScreen.h b/src/app/medInria/medSplashScreen.h deleted file mode 100644 index f5eb5fc9a8..0000000000 --- a/src/app/medInria/medSplashScreen.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -/*========================================================================= - - medInria - - Copyright (c) INRIA 2013 - 2020. All rights reserved. - See LICENSE.txt for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. - -=========================================================================*/ - -#include -#include - -class medSplashScreenPrivate; - -/** - * @brief create Splashcreen widget with translucent background. - * - * not based on QSplashScreen which does not support this feature. - Almost all features from the QSplashscreen are replicated, including messages refreshed before a QApplication event loop is running. - * - */ -class medSplashScreen : public QWidget -{ - Q_OBJECT -public: - /** - * @brief Creates a medSplashScreen. - * - * There are no parent widgets since it is started before the event loop. - * - * @param pixmap - */ - medSplashScreen(const QPixmap& pixmap); - ~medSplashScreen(); - -private: - void paintEvent(QPaintEvent* pe); - - medSplashScreenPrivate * d; -}; diff --git a/src/app/medInria/resources/pixmaps/medInria-splash.png b/src/app/medInria/resources/pixmaps/medInria-splash.png index 19b660507ef658fb8cab0ec9a2f8b5655c66135a..522ce39876a5f300ce555a7e05b1cf2896d2367f 100644 GIT binary patch literal 28283 zcmcF~g;N~e6Dodo|~4Fm7AxTizR}mrzeN4Bhb~{?7Jn0lZ$o6nFuKY z0yTnyw1l=-=1G>P7y0~rD8$v;>Zw|%p8np%`R}`^Lv}axJP;}BMF_UMB-$HEjGqWU z<39gTN2EzY$U~a-ZD8haX8yP4k{oF$uII8^-;d8Rh5&dv?pyBH zT_@vrw?oN)c)V={E!!RW5VLpN%B?vtFXC7l*afmSS@SQN7#>3@jUfCEK&26f#w#J~V=mzKu5sp;g0)19>s|haVFz!23TP*IrhDyVRHeajEgzR*18y*Zu0SQBk_(sM&U^!J$MN~u)u(Z`7LCBdkXW2P+_LF=dwOiuF|8@I4PM;ZVX!}QT8g#}zHql` zJE!qE6+fO@I6f_PA=9aAyYHxaJEO5{P+@JD6nuMb>3g)SZf{WT>iSgQX3rD>&R+%D zmuIJ(T@$Xh@S_Ei9WAfk^?2Hk?sw7e^5?cezHXdi(9 z5!_NKFm+dr{xc83|DPunY;UzW$c63xJ(wnH&=nuce);5kM}QWh@rrDMIUd}VGXop|Z&j}h3k-;Ql?m5_V|JQ3UYVc^;(?Qef_Pw3>Q`|#> z%Qq#$ncCLO$D-YKXB7hd!N0OK7``=TRRF&5q@F^a&1`?BBLVe%|3|z-o*(kCzrXH; zgQf!HP(K&Dyc^~eqrY7S14K_h_D`6FwMy=9ZEe+OQIC3(xfK##?>fP(`^ecl9cfey z6u_&1j8DGzeSv>)kT74ltXi@WcI+z3VBg*Yi&d@WOQYDydLsewxZE(~R zavaJe=QX{sa`^;pc)h1xc?I(qK}Gk3p83Zd9ZL6WcX{9ke>C1Eh@M(sXicI|VE)8J zb_`9BCYhW6n(wT{eg4N8{-a6HrIx+3=u-Ef(eto-WWD|O0s%emY}=C?tJb;bm zncit+sQ5db{ypdQM)*XJs+PZ&4nrcSuBgyF+G2lk-$;q&_E-F-cNs6QzW$&6}1v1P;t z7g7#x5UAIh#ZU25^4Hw~)eKrFNygh{8~i6iW3SE_WU}ZgniBU-teF@o+7bmpOa(Aa z=#=e^7c#kb{*BNDs&e3uo1^#TIsTs+Apvh9-w zhUQ&>^_c*9SS3P<$A5OGCo{eCVwto)hM|Cu8BF6S%xgtK>VP6CgDwgsUp{2CHVdJ( zDA%dmYYTO|x`k~_Ppr_7@Ws&;voXi&cEuCAo+MHr6S3P-i3i9Tt%T{b=Z zJIf|&N_`#elshkS*;T}C4n`JvClRTKOe2N!37_(do@3$Hzpa0%>1X~))JMN8gow)l z>kSg?uX)<^7V>DU6K|0Zgf|(vx1vlpSr3&v{V*%epus6n(+w^Q|7|?o)%5cZ)vf-f z|IghazRq|xu|ty_)Ly-5VP@(tBMoEi$CKJs`cj?Lc4GAS`p7cxG_FZPNDO#_nm4KE zr?;BVjys2TQ$O*6sfA(=^MXfAtCJ!Dg~K7^p}G0m=QCiV=x)|? zckXmAo8yP4nG{~#RRdMm0f*~ZBWIu~d=;>lIE1&7q~09x5^I!HtE}p5F-Sf%x-xff zG4yX2ZT;-Igf{h99?sDBJD@0`Wr;8tcQIH}CeqISv1W?evl6Ll)U_#hSe!};Xh~Rb zTm8ePXN!y;{G)8@c781IkmN|GCeCsDzTjG7P;rDT3B)NrFy(WK6GGvF0Ks*B!hC8T z?{|=shEZ3$$XSGXJLYFG_9RNJvf$GD?6N<2LQ-$)2gV*>YV<^a4ADX;IAQ6?St7_9 zqtsuSlx?jJorPtG48M~i>y7-eelIbbw^wC-E1VNJ)Ovg#PdS*QV$(0EA21rSqTBE` zC$GDy)a`#shPJtF+X6VHnk4!_{VTkkaT|56sa zdB$0rx7?}X@c2D7CDrI_03JjH%G4x8W(e#pmLQ5%m@Bwo4Jj)eSBkAFB1oZc8O{b5 z&ooX@p~vppm(TLAcVQ+PbHI2&yJmOnM8M)sm8FpnkIj07;ImZvHPM!F)#EUKdM>sx zF=6vNGI}e0s;I5AuijI`vI$h8GFE$X?QJe+*@jOutD^8PyV+vWA%k7%@oqIILHwEE z)Mn4bgVI&pD;1O(O(SWoHrt)vYt9sPzRg>O!8A-|$%Km-t2yWQ2ztHAFN%7)?y*?< z5}3N|X(YAQF?mEg`p3Mro5i@BEXqdrZ757r#2k}=Ak03gdR76~N%X`I#n{!^mIrL1 z&;5N0Nzw5^Se2o#5|TMwc@N3s0B+;tWh@qkPUiQs5J#PCd~8QZ!3`?TAG5|a5DT5; z1Sl|8=EK=XWu!pdpKH6OcQW_UzaKMN8RmS-|cqK+8&s?6+lh=R;-BW@;Ck$ zPRmCb#v@SC2a@mqLcC(d4DEP?W@5JAXO8msbsQrx1I|rwOlz0jO1cx;g(qCxNrp892ZQuvcsOohUsox{6#aOPxeN+z~!hj?`MydSscsF5r zj3aU-fD3qiFOg1#shRVtw)nD(KUl3__rDoH?FoW$8+4!;<0taPaPg|egSm&vdt%P;1=1;R|MHcBk0WtL zEHahf(26znQo_xM4d=4)Vs{GybZZzo>}i&vrbjoGhw^}?J9Q`s*}WK7GC%Gg-&ot- zzR%97+VH{gS^Q+6Z$!$jYdFg`N{icZ{Vf`~TMQDWaLsaTvg*GuQP%=hHSggUhuy2L zg1!M2HY|F9^`9E6;aA6;<{I;)^#vXUf{FQV^W6E%bn6(u#(7-W{(e;Z({oM}CSH37 z%d((_r7BiaiTb)eSrL0hD1MeQ9?-PYsgZ>G-H>+TsZ#f+e_=&M4tjVIW1C&I|EV=S z;q_KxuGV;g|I~t~zob*{Im_DJ>d_+UpecZBJb~NKmqN9=j#A7QrR3Wih|&EA(W_!P zi#aN)xXHt|)X$dWy`m4(gZJ^`~=uT9I9{-k1`tsmXlRqFZ;tr5>VG^oX z@5j%lwp!+T-O~&4{4}zix0yWXd_rE{D7>yG+SZoCbG}ZteAr7y>~-kCeg5d$#Eji| zMQLbc)QogqjYsDI8W?Zce?^KF4cQSey&CuT z)J$#Q8rjDwWnye`;d;KpO%eCQ%8C_>Khs^0!URbOYxmrVmuEwX<{PXN+<0vVhvho0 z5M3XN`=*XN9aEyQoDnyu1ho-#CSGI(h$#yN740qGUGjaAZoj2-^pdxn@kSXc?&amT zDUNpF?^0dV#;Yi^g5Id{VL=o$&R@|54`GFqIO2JOeD~p5QCin4vrIiyS;~jXQ2R{2 zWN0^w%+_hi?MVD~;nPpKhR3kk!o6F@#xf*Jd|JPbR(l2(P1(KU`^Jglp(?%49?Qxn z^gK;?HL&WAy{#DM7+=Ei5wb+Q#pCO3C?lO(gZ`KestZ_H z5AA#>R2{V5muJ|jR4gu2-Tyum8LuIDVXye=l(=bTBUDwk`omo9@32*P>SWRRfwACO zp7qY9l;=FK*ncbzFJRH#lCN>$~MKefJ<@YzX| zaTK>kFX(9Qo$r2^{=?#EW5Wfo6V$8uzUB>O5h}CH{LA8TzL~kj9B0i|fm5-DE*H^k zhUeJHQO4+Ag5SMamR~Bn;l8Kya?If;XV%x39QAnGJ!6kI1WGA!)9I=y5^0vW{P;1* z^F^#Kf6Q!VrLHO_%lxh_xkqv*HwSC4Z(9-_qUs*n7|Z-&7=EfA4sH%wNblH5Tz75P zUI-BY$&&Aaq$8Cs&?y<*KkRGn@f`{|sv$&3<~7B43G4s+r}QIZ(`8uvn?OFEYVr^z zO2hNvlm7Qbs>T=(h6Uo1`{|O15}0~KpnoEp?}k;@gAexr)8XP2NmQhu3th0&5D*)hXq=IUnz4cxt~L zwoD`nKw9V!4wrdq?}U;PHVf#?p`U>#D|`=%9dFge?|}}e#54HyR)~l(&5!gqPiSW_ zb4j^O7M&c4!o7gudhzU%iekSX)%{PzBPEL|?4(^9{!_f89N(xLYpSn=Z0O02T{-WZXA$i7FY!(Ng0=mgDHC7) zqA}$n>$nv#eaz|$f#qI@J5p|{Kmd_Z5P`42ryfXum-;{!8l`VD5~dg>mnfYW?8k~O zte~Pqg@KGMA6!Nh)^q;V>UH0nsRTI3tRqkikX$=h|Ih4bt=YvtD2G=yPE%G>fC}Zdhe3v z3wCm!(k32h%679QxkSE<7MAt%&7`)9r5WGj@NvPbkg+?^>6 z&$q;xH2$z%G*va`mzz>|yH1y`f|ouI53^{m4|GScd;HzlRVCvEhE1DJP<}t};mJ6s zv5(Bqz!BQb;b8fp7V6D`48&Zm*uz<9cT!Zs@2`D(mK*ZhT&&`1tBNXzmS~DFZwR@w+CBsyY9>IoKLqnXgX8!W5P(uHX`oD^HTSlMqHrn4yqu5* zhI17T{I1{#FXX6#gH$YL2!~umgqx#SVphTCG~{qKpM+wumMr|~X$13nRa#ihG5)FQ zf@GgIW#Ae7DzCy4{8o`I`1`DwVdJk0p&nY@rig*So{+~(m0sryro1Iyrb}XJmEhVu zCT;>aCd&?SJy_@x_OTUmJ!?y*=X^#ntqGV*;oQi`2vFM%b;1$aew&~AjKk-Kua9vD zQo>-*#m;m~xEZmeBOG5e{ui|$DQ;AXDC%SWPEwB+4)1HI?bug7M)dO7vrR+RI0hD1 zS<6BiN;{tb#w~6*(L$kKs)^3L)HhF;wzy9R?peQYkR^CHf1A)Fy$7*ssJ)6Mc`1{c zddUwGPFQV(@8@>h zzG!Y#!WsMCv`5NgDCa^N=empdXAWWVj4n3YBzOAnQ}O-Q0e`pD#2L12B9w!_&Hkym zT8PfW3y63R(ubysYZ3tqrzbf7$)U@{7on28SGsDzl=@H%Uutnsm-j#A@jI$h#5zu; zCb!iJ&LYe3V^>0!QnmD7CMtGxbUz)efd4uVrA*S_4&c|j_Vk)GqfYx2SjrRt)gmU^ z^^(NHjCG}MiL_9$C=~o|St3BO^h<%CFj!NDN5a+Yq?T-kXPHhxeD>(sS%Z;ItFP4R z-Y-yq*!h2agaC1+L#5N`Y4Ln2HinMg2a+AT%3Hc3_}JsIm& z0jqo0`7m$=%6;4nv8|!;4ko4lt@ufoGv8|F*bRIyU-_o5%ovdIW0y3w)h}}16FuJb zjIgc~?zT2`j98;e9@DP<_Zg#@!&Z@oa5RrS`fWPc6R&}SjXnuZoA8KFMJ&o)ENKm~ znMw|jaZcNS1xY1~X}^&oVKm)6lkN;ihkvE^l1{X(9K7+qAeG!sioGE4WDt8|wG)G4 zlZA1n%bgI3=N(Jn9dyF-++N4|zU~j{RQFaJN?DyL!=qNME(fFAk~1wO=dIeal&@?| zV)?zv?BOGT7S2DOiT}3b6pgN_WYzi9@8~H`|78-U^yH!+Bn66e`tvDj#_@NNv4#wN zA<;zf-rZwX-ClD zqZfw!Shix{SlNx(0kM^+8(G~F=`*O8o3~uOc@v7_R^lHMm!&FfYGoAI9b<*jsi=Oo z4#uLaxR&uc!oC-X#s_u2Td;Eb8Xz?eiu&6gaM(m*KlDFts- za_o9pVjZ$%*W*@Xiuzmw)@A@=+3T~p@#Qp_&F9e!l<5+aMCmMYIB>0ZyHRBz%+Eqh zqu&%{;lM{CmpDx-_u;j^TJW~%M)-;L(*0cJdA7xUD>#*Fiui3aV$6=UD`>~sRAmrj z4tdcjY(oJ5wni-Pr}p$?{fTm7%mlHl#z1-~ ztLtThs{@0^@9agS%{Rft)GBDl_Gl_w`Ie*gjP&kiK;DDZ{lCUuTooVy+MS& z9$4OnU}eCyC9a3qfz{Gj4h7I5g+@$VmM(%I<}*`}xq$^KnN21)8szt-GzvNmkrUlq ziTGNqV2Y7H`}M1v(4kxU3jOXJ8}sCc;f7eQyK2TEZC%aRlb)t5W%bOuGVe><=Z}M2 zJ5ks4TE+^89KP~vL|Seyi?ixKCJObQm&Dy(;1siA8&dAZTjc^#6Gcq1n3=!AO0^Jb z?)l{Y{mH_oyg@D4WyYL{dyU6dbm_#}cid>fVWc=28shhCI7#2C`gWqLvz|}*UZ77) z;j%two=6|Ey~BQ;V!Pm{%)I;gt9_qpN~YtCGgwVT&I;mO6ytN{n|5|`8XB-GFTD-GJsYR@4oa!MqC^EQ-9rP>E$x28u~cK?2bw%9-zD1~%>;9N*J zmKSi#;wGr*Y|Cui;ZaZs)b%Se{-gxRi-^#W86X~@FSTKG1B7d=k`TO4G&WGRn^>Id zdN$3>RQbB0J~m)9A%$82~i+VkH zea8ng_c4;$jjE&Lh8U1g6#F8@Sy?qRy;;CTJFs;wpy#3`zMzN{OY@){{MyrPP`Nch zr}IT!_ytd==h0LJpt1{`+)TSnJM(|!q;J0#Y^N0bX@5#u$LNWMS6w5`Tw!ro)cK}c^RL<@q`FKJtS|j%q)1^bQ_?Nf zpDUOq;ZV@l5bx9b%LD15@eB#SO}0n!@A#&xwavwQ)}c&o(jKh7L#f4c zPN>k$b$@NjvZ2$}RjU8S?2(Vdm?44C^^IgA!g7aPMF$vV!?pQS>M|A*D^>y{SJU8X zqUsKxRhQdkg$*p0p=T+7x&}csZg=;W40z6|T8!p^zuOnr=eX*Hs380oMj$;pN$C-OtHnPey5SnMk=7NtVy!$}h5 z1BDcx22WVq_$sCj3q7!zNdOUivkZy#6!ivXN6+=eWx38XqwS+G#@_QFlUa&H+Qr>kEkb z&9J#y@SWRsYxi1X8rLNaJ!MhO874veicoh2rM+74=yM!_rw;|$gvg)o+--7!-J z{+No}WU9eZOQmEB+!Z1%R+hA5sY4Q3^TDPRN~xC1k={jdnmRAVvl#BTZWPDr6|%K4eBv%s&JBtTKKMernBxu zGapV!v^(oU`K1qB536uj_vKk&?ir51g_7$vp0G&)1}SP;g^A4!`!sjZZH&)X8H!3TlHnt&ePJcp(Ua>-jKlM*d?8-+p-(<#v}LPOqA-Ak7|u}J=IC4( ziTUdcns$x;_X=3ZiyrRp;n-3;(wh|K%NecKK4}G<=72{X@P{w>mbcq5I$L2q+Fuxm zv6bTRV$Z26If8FW&vde)=ZWu4atqSj=j!hVdT4?>{*!4bGK~;~_R}`1v?CVMlq5V; z!PIt=91VaUe()q9C*RrE4BkNEz+u#FN-&ZV-Mg6n^@P>?3EfSKoi7n0Gid$`6q^_D z$awSA6M2D2&T`22g}6-cviu7G;DJ>rM=l=b-EKcX zrKLb^cN-W`)%6o0%X2L>cXEWv>;n0!o9+oSf3!RFgH8Fmcl^BCTFDoGJ`k!-D=I85 zi%SuZXrd+aZ6%bB^te$AwL5)2j-9RHt+xM7`$7n;VH`POm~P}av=_EZz7XkT9fl3F z$;XUcSMoXa{9PGvD-N&}Mrw3PW$5|X*+Ic7$bV>y*RqmJpAvhq?8(}2{72{7(#k*A zeA4I~GaF1?P#CpUL51CLzhO1&&M(#X5;!&N32TY=z3a<2-r57Zdm^x`2WCVqNJlYz z7{0v@9}Eu5Z6N7Xti_fD^%mgkS1m?l$R{3(6ARXQy%{e0@JTuhVT4;9sH5@pmqpCy z+#NS2QCyQ+a%#-bxZ3zVyVq;2bg6!G9L>E*-$)CCrbvKkJJol=NE5&G$2QYLFMt#_ zji7y0q3#!iuhu;M!17Cv;}8MOU%w)4D)l6#Xl2$SVRzbl)On2detQp&HfJz8{U|=nVq+Vnw7@hEtzaTKO=l3 z|Hr~su-EucqQG6O2i2m)A7LdAyws&9a*4Jj6dG2*W#kUt3p2wJyF~u$EUy-*(7-1$ z$P|2W*b+YPWM8pduhgBtX_{*D{;87#jNGt~5K-f!9yMW{q2g&*QTlRSuk(<ZKqIgxFseo zndKqaXwYbV!TX#F^znCdFm2%PWdp{A4BH`q^*(ReZY1i>QECoh#FS+XqQcyKMB8!q zO3&a=&D*))_G`02h20ET!Szo(R19*^=4CELXq!`Klo|2O>NWzynpfI{ZPy)ndj7C! z(N_YaZ<{;TxzO%CSX3|HpMTZJDqk*!g1!>uM}PQ=k6E^C@R2guhF7rwl5?*vNz?e# zq=#ecwlHO^!2X8Pf{)Gxx~ml1!P|DKA-n$d&k}8oM-{wJVsIJtpivRKP;I2#_tNv- zjiGJ+akHLF2eyfCdL37)uF-Iq=2WbHzX-7{X|^M~KM7UH7*LUkxIiaXzV&rVXmn7< zksN1J;HP=lVgTn_6mht(16A7id>pciuZ{r~^M1!^kT*dFcy9Q%as|`GA^HZ1pGYHu z#D|`B91WGdZnOBGl7CK4d;bMB?N5oo$?z*hmD%qt z@so1F0=Dg=n9>_l zrr}Qac!vJvs`=TVP>%1J(l5 zVy-y(KoIHlKrug>%6(QZj%%njaFk|>t5n2HDVF{jE@ugi{)dY6#zs$HqE$(t(RSLl zB+C-c0$rAM{#DMs*cZ5J7+|2kOMr;a)_WxAUC>>fjwKZrEjHB%MBy-!8P{+ljn;P) zoTQx9e70J!PDis=Y^n1d$)~?|Tk)uEa=~&t|Lo-{r1%yERzHtYe#%xS$KiG|A_Fcr znk4L+M{S-MW`qa2ts}aHI9A+#Ss;B zQai-en#p!&weiKrs$|_?O&hh{#>Ap7VKg0heN4$3Ff(2s?Z~@72Q!gdtW-8SOPV>I5`F%Qwk6ZiiTB1+R?ZE`=)DRIWhS4f-XYBaC>5ID zj@z14xK|dJ4#P<%Z2X(`AoM?QQVFaVeKjL;NJ&DfkXr^3p`P9ftKj4F>Z}W zaYLk4V5q38Z9{8X*JFZFdsyT4SNZig3t$8|h4$?-6f(6hp!?B;YNby9b~%&{_N?VV zikvNvvr-q1z8W4v)m1Ub|Br5^fGFqnDZm4;!;~=pg^u*-QXg6IN+}*>e>HgR86-JW zWu*LdOf1YS!47b7de-4zgAXZHH<$o(FUnI=c4_k)+RGXDcqFK-qN1AgqI3zoV626t&(yapw*=6k$4))gZJ-wDnIDph|&Q|12I1H?)Rd=^`~2gH5vk>G~5 z>>p%tcI%)AB53fzN&ajON-o)aW8XLxdS;eWN&A7BrY9?qJuUaWXB5KS$N*`a&H0;3 zle#b?n(s%=ZUgj`R4fLd_egce<=>AM8`Di?0C|!FVV0Y7iyLz_XFpV#v}671J&T z*Aj;FhiA-Ja3a(V>_YGBFaMsN;!KlvRFg8M4!j4Y`8`pU84Bu#0+MM{PPo@+14c{Td0+!S@8^2tkuV6q-7$dBA0_x2~cslIR4r|t@hwrHtRsEj=j3Wm_ zoz7?bd5*B_d@!6AE~{qMLyVi`!SkalUp$rc{0DJGp7R&P9ar);>y+FbZ+kIqU8UfX z4Q&OiT9K`}$~lkSE&gP!c-7K`Gbj~1rMPN+{8(?z)UaOYomxo#Z&8#TFd9K#&;X?R zuc4TPaNM{Jh0~wv3c40IvAgv5fTOQndM0Dph@VEQ;9y?G5&CUa-}kyvVO869ecNk0;yqG#6Y=T<&hQ5#yJ^4TmqQoh zj3y05;@iL;LSY;^U^F*-XOVGOm0pZ-TTo4Pk9=&~p2wvPnJLX(CcXWlQZ0gqO1>_Bp;a`yr3zVg{Uoyw zVcyG#{U$wnaq9Ak7#f>t+!W;oO%=QZU?Vm(GFm}!kg!BiU=L`To|DiOljcJV@5@KD zoJYCK7-3C>Og)U{D}|NtFCSA-2u^9Oa^jH!+?pTm0Sct1&ylJs0qrUuGv}SyHhG+M z%S%5Xv=O*E+p#px>IHith7HC(1eNAjf4ik%UHGxae_mW2{GJd`?8Rf=EysZN@^K9y zd>oAV0{w=uEcM*+LJVA5&zC%YzW8om)^>*t*JiY7kkt8k$-?A2`|bzGKgmW-hzg7p z@G!$y)xxC!<@)nP_y$S0FkDrDdmbjLF}+Yg&6{j+A8p(g)enThPsv&8c+6VQ1;V|Ez!&*$50j_DEm6(hEOXM|Z<*Wt3@W1Y$Ys((HA!?=PcB4{~oSz){M(AOTr zE@2(Wv~FZq? z4*S9Po=dY|WGsJ8wi|mRpUl|%_-$paR2!QvjZsQnzz)zaV}!g4(yl;ZgK`JIl<0Y! zA|_HM?LA?Y0exsW9!+|P1|E{Dgi-Y-zb4@XbpCdY_Xt^IN!(C&O>2HDBU=IXG({u3L9iI3z7DFlGn!_{15k42! z;m8F15ok778fHk^{maC{&`~a#L5ma>q&A|=O(lRl)K41s8I=)dP}7_0Hy@`+dGKb~ zLzLeG@w}U(gKzhSw#4e&zt1!t#uqmA$!rYUHKNK}yC`tMg43&RjE8*ZHJxlr01~E1 z^o=f+y5~NpJCKT$QAeupW6vTk8}9iKe6D^e{jmEMLX^uX=hR5}d#JxIIePS+m>{)}pd81`LGSaUjgfE^?hYOt9HbI~h z;7Z1TjGuw$vje1^y2q6AN4j`VlLQodEt>BNq&a<*NwR$TGP1K@+f_SXCYz%5yX3G1 zulz2fmTK0D`>gJNHtMdvMafovXskf8Y^*Q>>PZcv!QH(_M*Mg=iI(N3alKeUadh6L zSR&@#@It|b{`{m$?X@~Na5CNVvb!ijkgV=Ekfr(3q2=zenaN%bl*?1;;m+u1&k7^T zBzJ=KBIg1;C9HXV$(}O^8-H)R$@!lD>m@*P%}4RunevHt(C2;bh$k#u;|s5E%zuZ9 zQByqF$u(isHTe!UU|H)~z1Qa&8@a&aa~EJ7^y;Ov)*)yItY(7%+#p`z{cNkURn)=v z!?OjAecitTV&mtV!=zdXdVC7Jt*UL3^q3~W1sI z+Mp7p8a?5UO5Pmq&p*g;*S{MxyCgBHM*C~>p`nq^zAd7ypRz--fKRrOHJ-;Q{$N*) zd2;AQ;8;1zxUJC`D#d6-x0z!C&g(^vGvqEg?e%zu{!(pgeP=eyE8-Tc8t|!Z&Hn3lw8z& zudn>3pph*4yz^I5(oF4lj@N2Y;_rjz`P|Gp;;>QxMNg@9EDfV7~5QtVxKhF0p@xe z=XKoe;EE!H1GxOBLNym~Y?5WXr54)3)8*w$-9Qf{C70qZ#pDfY1` zY_Mn9VR@dwf^f{n^>rhbGj1b(pWjq>-sR*dS+H*9R%{1=BLzg6{4@Q#D;-N>!E_M=dz9wR*vT+x>nqtjvW|xA*8Fg@xz>7=(iw76WzK`%-qi6bwgdy zru;^2A`<}CZuP-;@GRs#$PoVz6|MuGKn@joh7q7^0qw#A@UJFxxdH2ys{;>C^^cmu zawY`R*X77Lv-6p5VV#NfThGPbHO3y+k6nS&YobBR*4TCTrZg@Gmp~a>r{8rR^sXH; zCAh!4pVUNY(3kIL0IakVuRGWs#9>K3(ggEX`o?LRu>#bQk!1S1K{`5cX&b|?$W zJyv)&2Igq>3m<(|=zN*-+5OUrRB?~$#x3jkY7O2Ye7pmwR0fQnd-BDz=adU;+Kcwx zCvU{}IKCpDF7b+78M3E@9ysM_>pFB$Pe!_`w zsBygfKoz*FvBml7ns{3o$!9}19r;QtySQgPq9Pc5+YcAW51M-8W-(XFSCS3ZH5!g0 z#-qwEnh1WdZmMt+rggv-V9XfjWF|otyz>zDUB(9f`P9hNXi4*v+Jg z+G0NIpHVo!+6u7-*11|x;K52S6RPwJTru3Y4%b8bXJmGG4F@W_3vfn*6GwL_*oiQm zov_vx4v`SHVjH`ZH&0TKddZ;C%|5Z8**KSFR2EWBkJUV;;ZZ3wXc_n*Yb!fx_BPIt zOAaUu?v4;ZBw+s78on!^r8@O6H&5ZA4nFGn5Z`cSIWbzN{?;>V&;!iTP z68#hi%;vbQnw{QjDI~+_EV9R?qZtU;L(f|FM~!zq*EmkEt5Nc}vp78zC!Cx>alEN@ zVI{&1+~frCTnFE-LXa4r6)V^K(yJyFc&9F=EFIFD^k3v~q0p-OyELH6_H4`116gdd ziTbZ8g;d3hum*#%1g~x9tw|>zn082HxXG2L?cuA1RiJ8Lmu}NbC$~ zWTvbp?`(qA&F8UDr{Y7-9Z+LhmAx2Tk(yYsV#TJOU#d06$Yjrx z#1RPwX~FvawI_e)d-`t(g|nN|vO)sVzSeXLf`%hFOK{+AAzcoDT6+*5n5!a} zIA9ucl)8)&ZJ)=|_SnSFdfX|)gpQ=GA#1t4A}YQ!_$>iMDY!?~_r*%<}zX&&Xzta`Ml`XB82Dl=!OhLbq7N zx};opv6#$)mKu`j_OMB;{2}^nAiYj?v)1(N6bL>;`u%X|gXz0WYtl5D{{R!`qqKK9J7SQb(o%FsPo$_-3;B=Z! zSK`%g44@*65y0ZQia&ZA`kM1_FIx7JS~ll%TbYvr6Yu)_d3{>%#bbc9(YnH4t@|l= zeWt+`AsYup9`1|L;!3W?fogq;6Y;>QW&OnTmvS0Wdb@4V<&{n72FOtEkh}jx(I*8ad+V zNJN5;bhBx+780-S*Qc`Xh4fC02%SFlZh4v(w0K;kdJL$R{PuvHK1i9dl&I#z|BBOl z-HUWv_a+}T@m0MS*>g)1a&4bD=p2kay{#`|>d{VhC^aN|@$<#sjFDzDZSZw*_^ZJC zRk!h_Q%i0eyuG876Jy*Gu@f5Ws1K7yeLjHEdG$N3xa+Wq-da-lKKfJ{>TMC4#tlL5 znFwsf@YSlnQDR`VuxtK&FXK#l{QVmrow!fTPT{`0$7^vcIvnc0|J!G{ZazBp7zIZt z6J(wCZ!h05KSHC~$?`=4sSY^}ARi3DHNmRXes^E0$YKz}Jxy~rsTS={7D9C2^}?BQ zEUuAsQx;?JAWTf&Q6JpjQh+ntf5L+RocdJxv#<6MqxWR}LqVfbM^6sir!zdoagQ79 z35o0Ptgln`z;upnmQaildC03^?ZTgLzVvQ$%oZtZblruSQyfp~D@7&Lz3EQf7O{8} zxuZ*$m9KWBRO=7E{;PO3LuW*$QhirNoe#4g-E+@BA3h~E=wIEjR%UV~$Lqq9m+x1l z8FUh{5Xx^ngjnl+ZV;(WG^#UbN$k6+O3zU)!#xjnI$-NdzVg7I<^nj{9)`I=^j;vU zIlE&xSeXNKK&O%WsTf=ya|U?)VEm>!kA1UkS&Q*uc-%{{SjG zIvn(`5R{f-EIzKP&M=H6H27~%UdWhzCjw08Vcm$I6g#j5cbMLWlFK+M2w}vo%WYLP z>#sP*Y{Jl z4#9iK1}}4EpMU3H!#J|@pfBuP>??MKW*$_Zr=fuaQG3?;cSl8YYaZ1L!_6UYuFge& z{&~a5o3Yn_R0?}4%D|^-@D#`b0zRPI5Zoex_pskc_`=9 zun|q9&*KWhOm#P(iXX&dJC?PYcd5cYBAT%3=L{IEA3Uc&EgS{}-NH_@ynmB_Svwam zNm=)8R%S>X?vC@PM^pHLWbsAk4S@!}%pcaH5488UCIoE|X`K)*sBfhpHKH~ajjO0k*W1Dt9i^5cN0`{P?4&Lg*sel08( zl4HQU{>5_z^TU>B2r8&Uw79;W7Y3>EJWl$CNhWpaNrk)i1qoq@%~;;Uah8#?VE)qvpC_$dT4G zmzdwYwfyrnRpAy_z2>(815`qyfj!tgW_m8UeMgM2-`f6 z`r7TVUR^%$1u+emn^AA|!SCRP%X(_UBq9cuQ6rqSdWTNX1LAXw8O(HnfP^y6_=#3hx1z?VTw_nm2X z`F8%Eg*Eb9NO6AKgc6={)YZbwR(m20*R0EaZnUSqu85d+vz6+`3i1X^(SxPgza2#M`vkN2Jq{@MW(r9^7EKIr zyCY`l^wT5+XLLzfZ;f_1-yfzmL$>Ji)8cW*iKwz_wa;NUsVl7cVip=9lZf`Bc1bSB zEh3)of9f&YGsL6CYDEk2{Zu;4s62Ju&E^V8u1HCNDj=vk%oC%wSTTO;f0}~adYW(w zHdRi?xXN!G5-gWEU#G|Du*Q4tNfE)ee(IdjScEt|K7+s$ry)TP@hj(2oyo!3{4g>swzul=O)MQX$F~fKQST22*<-{xA0x=|!0QWWP$HSz=$xOwwjH&}c zIAumRBz;%TmXYy#`^P|q&eSwp`@W4T;g+$WqTctqN%6_aVO(SaU|ikMSw%h>v;9b8 z&9oKKp04G1l~BdcRmTi?6nxo2YZ@JWbd}$IB~JGP;RxY|sq@hc)k!@x_4j?Z_?_X+0Y9OT|?A!|jYvhn^+rQYzR!hJ zwMts{6Nme9>&(^JY~%XVz$_4qWJ#71z^&>iL26C6P;pvY>O-F+WU_#5{~P46b+Xie&$9c3c;)M z4~Y)1Ej>iS2D|g^>rL|jZn1pq&wsh^@+g}2=O|;!9!O&xdZa@F?i+gWSW0`}%xoBS z>+5H(#n{$mPO2P|?mM#=UJ<{0CZI{du?Bf%9up&ygZ{jM7^IL~R+lIo_xlgcdihp` zn!NBUP&W#e+4^r`HXdVsltE?+hW&JHCh=3h%DH1j7kB}FuNp_}{o>l~w)o;BE#hQ4SW45VM2KIbGeAi?U$aMS0Mb@vrO2^0as-Ycg>CI?0oi_ zbbHYQVIZ&b*MXs{RFk6n=v2DU4hQZQ#uI(Wl?D`>1NY2jzb9w9Fo%Gsu)43#C)x>s zuJpy7ShQ3hk~kaTADT;diRW9l-&QjP`D+LXX>$Z|rRk>CjOjwXfIM1{)Z-$N5>~pe zsI^av@HvG>yK6zwjv;@X66msAvKMbQA7&GCgp|A|Klg}BRqtJG6hmTCM`o(Y@p2vc z+H)xeGW`X?NO384=N}zHd9>D5;2qMDQS684h%Mo8o@o6+fImF_3CWF*e_9zDtTyy@@_&ZEU{vmA-;_4%!5y+hxM>Mb;2nHNu7%A>hLVZiF` zsEZ+KT=n`*nx9y@>*a&C)+U7<8PjBlb}ViOgec!i=-$LLj`9sKAGgvTvzqJ%enqhp zrjtd(BF1rZ-Zk{~O}pyhq^21Gc3AU5-E(U2@_~Vh*v7p~(8U_hA*ha-qRt+Bc-i`E z32hn=N@Jz?w@MoiN016VQ>$M2`&_Js2EFsR;;F%<{`DPe|zZ=I6rzV!1e- zn$$jn_clSBh?#HEwa!_CJ_w>hpDIUp%ojSPPnbLNGM}wOghNKm4!=g!@p@D2A5t;^ zZa*cB8r!Xi*v}A@kJ}8{>x8KUp@7b44$ll&}hk3 zntS$j9iS*vx|bWGr}599gBaILM`+rxVtC#8DDVo+&U|)>BFM=sE%BC1z&lR@5pP$3 zk@YqNmp%<(*G)SP&q0FNqqzP#M4eM3Ph1`;^^ycY_WH)0Mj`xTC zb~FcZNB)k;<_}f(f%@jQNd?lGD<> zva7ij#AP^>vn82PW(VzY;pfcS2hyx+NbuKQQ~@CR6!L~k1cHlBg@Scog2enH8^5rG zw@o{OF24?IuT7h<{2jF=Zpn1(z!-MMh~GK@Cb)jN9@i4|C2HB9Eb@=IGAhT;q@ zebry9VzWt1)Uw(eCT#C|G-*0;#f{kLM|Ei6BYS|#)`>mNc2XAufX)_nU$#*n+QNVd zqIAPwkr(k#>rZ9Oq8OS3CkZq4`qzg8)7^v*0L(W4oAV z`UhbP^$+ZyO;n_cYUB<}vR&w|q^z@PGv2%B$t2cS1*6Xy^CvFu>1D%T{nX5oh1`Ba zwSdCcA1QMsV>08_++!XMI`hiMFNhj`7vj8q$6mcP5S(;1HXE*dqe%;Rm)vXodXb>v z*Mf2p_wAA$yX8EFWbl$VdM_43I9j2eWIFl#wLn}Mf90(o&qyQKTBC5hH>hDR zf1t#3CH$(AmPd_{*Y4vm25nMKb$7i(|1@HtdPIpCM3ep4!WI^d{*Et*%4yd^K7s5NEWA4s0tL9K9=p({wv^s1Zh{3td{NoHbXKI6OT5 zcC(VKZX0)QO5%*2z?A5s<_gQmPm>2@Qkw(aDBu(iCx6>3La8nA5%Zz=HX}eS$fXK* zf04I`h>c{GC5W=I;oe?i{#?U-+`X2et#TV&*fD**k@G8vHwW#Vow=9Dsqk+Fqz63P z9IzpPy|z(p$qmetzFv5bxyLM08vM$QU8jR`5n8hoGifvDpKtWgv>!hiS#CHEm-PL( z9Sz}qE*Bptn09g|#3`Hov%%mKaDlUpO7rp_Nl(quqy3rzlIQ#UHJK5-Dx{R1{-iP$ zexy0;Sqb}M@XRJ5_Z63nmvEZ(GH+ zzL|$f%~oP)0cWs(>L!gA3sUMXN(P5yWL0LAN*8_y9P8}r60DU-YLB^7(xWtTjf)zNs{C4o?DYg8AwA%Y>hnIUnf7?=fgkvk8=aH_>6A*w@Lmw}Lg zSFpjR>{hlVzr35i{~bixNV}Q(BRMPlLTd#uMigBH#o9yxUQr)PA4$*8lF|oByOjG$ zyC(gndf(zF-!%%dKdfQ{(%kRdF_b@wpb{_PrVyX=LJ?JBLggAL)GLbhS|3`d3dGHi z)ah@|)!}=K>Scr_>PhJ{BnsC2Rm0XYy+KvAF5tf)POjr!-Yh1ICaPA|ezV#9_q3B~ zIZ2y4|HS*a+`tV!H(v`u3ujfyH>OaWvsi^D3L6i)GH*at7FR1p+o@NQ-v^Z$vH*02 zZ8+!^JuykNpCCzJeYjgkG*Q5R(z~UiY{*W(0K8m%=Y#KPMm9TeOh5jm*kFGsrQFFG zBfeb%r+J+g#u64S&OirEt}@I6wrjbct>1+W-|*CvAp)E=6j0QdAQjv&i6K-c`h0G1=o0O&UrJQUlu{ENiw8Sir7#hbEDIb5 zT4p=z&`MU9-DyT$C>MJ_EbIu3_yqU?? zmL=zPFk|!?6ixS>Nj3pHn*${GTid>{}XR`Qz~dL^FN%>bvV zT}D)%jF!Es5=Bf^G3oA(=qCdwK}7%;2JN}@@M~-Y&L}y0EM}??d5!jaDUhjES7vWu zy5)NU&HG9L_B|!bj&D4nHb|v`ketle`q2&L&DA~O_VV)b5VFI6U?GI;zW$_}{Yu_| z+(>?!X3B=mb&2LunJ=O5oEVRC&`1N>XeYY6P<`}Oj!O1Y6 zz5kfRp1E%UG%X1~I;C!4mfSY;c^HT@(Us@q{G}S{1wGrNWT;?<$2>5Y7x(uap$LHG zVYnDX-1DC98ZKeEE@3zQp(dR`rk=y3K;6<`SjvcXs$1Nu zaut|_Yf_Z^i6-d_n`sCO5>WEZ$G+Oo^wN<_;6dA(yP}`l69#|O{NoQU-OF`TsChba z^F0#9nap&IEp9WW`+6>IZe%5mc1K25q(OHw@K-K6n?TukZ*MPjjM(bm@#U$S}-CjsvOQB5ZLQwhlPa=eq0b0 zG$Wku$;;a6P9EA+N#i`^%_Z+D{k|(q`Ap{dK4|HCD|+AcPVvUn76W<1MDdL^WH&pD z<7_>s-kFvh&*{DY)bGJS&v`?+c%weKuTyYD6_GK3NlOvC(~|tne2hx90RV~TFC3F( zvS)%?hR+?KpE@Nr=5JPCuCLTsll;?-0pEW|TVl*vX4#&;7Bly(hm`EPW)iV1H+^Iw zn~DM3NIdWFm<1K@yEqt;mppsC^iG(8bTH8Y&nT@Mle<-Z30f=UZcr6XhX-B2?|O1G z@u8+@w>ci@(Pb&<<#;_YGy&3D^tU3vYOfNkzBK_=ZQIv z#bC`<35L0;kQLbDnWhsAam~#H^k7Xnj38r_ZD@91cc*T_Tlj8Mo67Pc0OH-0qVBX@ z$1B7bXpKICH>T4kek0Ldd#T6&11FrztkO;^+HUgKd^a#W4SWROBh(}IDw=lA^o)`5 zMIYmBET2ayi?6#M2NaBNC{fv-2Ud!8ZrRdoKg-ic8K#a+Fy7{{C6M1RQ>bVJ$Hz;^ zvnzF42QrCp&C=rA^)|mBxSmIT_0YlY~T~zT@yrpC8|+)g|w|;4TJD&*JMp zu;CjPzVsOZ(26}0yHf%;5~kui&S+Xjzx2GCqd8zzw;bbOoAD01 zUE0$bDTo4FR(gAAhY(NYvY%f>#Cd}x{)i(e&O$}N9IAqFLFHwh;%n3D7peL{1P}jM zy*fg}*7K?W5k4+_>FwR#8CP<&C1fS>hG}TgTM-e6FQpIYbd1oDtvl5hN78ebfD0U$ z;8VYAa_lkfQxcy&8dd0@Xmv!>gG=6o_rmAsJ-6yl86O%IN4D$MGYA>WRtB(t!SgAP zn@GL6iV4QpgK93|h5e0MsqpkzdCHXE)EVcR2O)F4Dr&PmGMw2TR#hlmgSz*S(4klR zTVE$`wl!|D%m-WfzEqS-_%pUVq)cNYnF49Dv!u)}`#S?eWR-7qc!d{fk}N}@hCWK% zH6o1t^x;qL?|m^1WUr#zNdQ@`7Bi<1dzRN7ZlL)nxe>3|Xv4*1o9(!fK85um)|2aN_T2u~)|M0e;1_$ldBA917ukx+WSWP6q^oRzZoI5>3z49r<1!)C z4Krje`bIqtP>wF$>+{9h4a&f(Gu&L{dyodAx`=AOArv@rl`JW{<^$F{&2OaglSU)c zPjqM@%8A~AMjUS^oMtdT#AG9~WYu{rX(pQPh^DDbTZxV zL1X!>NRf0T!l4TC>@9jY{2BddhVa^(A!vqxK(PS;i-PcU+5YXB$OO@uk@J7Mm2v6w$0KuuL zxu~YP>r%m$gIj<0?{>osezk|vR>*8xH{96x=m|`#I}(1-BOoC76=#`KDU8tDCu~Ge zQ6;Fn^^_7X`(^QZEMe^N{_WKJ2|wRid8o=N^=Hh?Y9NU_!{fk-&0?K%n)P}MaToQa z)gQs7x?09s{zhc4R*3g&a^&YB>{FiyMXk3x)uehVa7fEBpxG^0zW%q!{t`;#o(&A7 zwtRd-jj&r9dSs{~PZ^D4pmSqCNT`~2it_MvSv3t27P0oA>Z}cd&xw5--H=56x!Clm z$+3l4VStHdBg!nKBDy$X`b#Y?y204mUI(wDLL=5JPdXArM5qWKQn(c!4-OmoA!afp0Jm&g@0{7c;KtSz|v;8^XT!nSC z8%pfNQZ!<=U_E(CY;}uVxZX4Dkt`D| z7n55o?IzbBk{d6jrDYBxbhP&;w>N|^uFQlH-~cCWahulMz?Fjwoh4{Yp7a)_P4&j=2Rm6h8GOg8=@H^zTG~z>gt-9i%=Qh{IH$)7^!V zEd}=PCEO+K51B!$rd8y)CUB|ESdch~17+)*+&&vt41v7w2FXIML^G}$guhVXA{ z-;V_fK3fj+`d^|J0U;7+LL-xzjLvEeG-g7_HdfCF2A;$aC!}wzgvpHk@jxi`ysw5B zcX}U!0s;c9`O0hfZd*#q%MR^*l}$L9j9+(`2j7*-jN+@Q?pezv5qkd$JMrl@LkOz< zefLOF5prP~^}nLbPdpSegugrpl~3Uk;|3u&nPwGQ!5{&bVapWDVW7&~g}p^ty;X&Y zMtg?MS;Ww{U+x=`02vkqXk-lBae8;3-R-a|PP76+w0HuUz`Ff7x7F=J_s&_ic(Lo%@kOWX|-l$Tl z2uB0nzHv}L$AzT799suMi^2XjW+a1|I&ujy*#j4|mz!qpzIT)|e&o-S@NFtt3}kzM zLwTQNG(NcSFVBq1sI}Rg)iKTgGM8Oa@St8Z#q28&W1gwZm0@=D z9!yjM;BFNR>YZMZoJhdRt*PSy&h`q|jEceJA`)#T6y;Vb*ZCy|?% zh>KDQgzoD>QpsFUC_gs-tUj|0N)Vdi*;0@KwWV$oP{Tx!a(}|)qx3-&;9dO#cEY~6 zz;d1HTM5=31NezAP~z@;2pV3x4K}bH8u;^Xb?p~i!J?4^1Nzbp&p16BcJnX5?~AeZ zI|FUJ9S8_$!G{OuI9sE_VM(%eW^>Ms%szw&STC+4&_4-h_PwONJv}{L4VNKw>ajFu#z_OF>Jl8ASow#b3E@l-4Bwv9AqJjHHbcb8y!wTO)H}0pV$ob>#~67 z5Ql!NW53Go&Tgh616?Lh)+3yJQdKpcq9t-hK+TYxv)_At8_FKSaqXGF?)!YJYA_L31YBC7pJssCi!LOWB}z@S_Z+Q zK-%9cMP^6X9d!F`Syaq&K{H{kEupVcEL4hes!kn^@)KTJ5)JMNbJq{|k?d}?XT1eH? zS0xupa22|1PL3k8aF|-Uk8Qx98@JjoqCy?O<_TJ^iMy%=26_ zfBq{Ci!(B9|Aa@1p>=TeIU?ZO!fHcf@A<2eIAe=nTLd^XHkT6}Ey}o&{nZU`)Z{>m zDd3p-!e*Oz$ZbW?vQJ(A>Y5*?Jmc0}AMQJxxeKHMM^owALZEGVC@bkllnj5os}Mj| zIwH}}q-g455Gf>JT8E?anR}kuCv3oxt_EBF7eI*2wi(ANw*;M&Uq~1XDb&~)-pcJl zgiEX7#~c!h4s+O<%C5L_yPMdm00h^pVc{pnsVct=tpc}nMPbJ+Y)e>4iOG-R45R#p zP*W4=LbNj|(7A;3_Ry{g(D++-CtKQ6HuC)=`)%j2Nx zAaN@frnnc6l3MvHXa?zeV|dKUr?1J(i&p3}pn?VU*B`IGo_cz=2LA~z~a+W5KX0c^~%^B_G`S&*GwAWvdQw^RCJ?k{)>e+{`B6!Zh~ zSIiHpq>~D-)L)2n2(dYG-62*GkHD8WH;p#UrL<~GC`-4Kekyl|BDcxUP?G%$kK0`l zAxV>*X%FeMi1M_r4>^&+%o_}+KMCeH?yMSGSgGULnXyX?AP<(p5+^6fA##{#S@g=r zFLdnwO@@x`W*{LEJPQ0?t!>wiIfjik>)y|d)c5g=s{!tJxRabmL2zzpzHRWG{PL+P ze%6(HaphLs`C<};7+GUo1_^&pDl+=s!6TP@K+LyXc*58Ea1sYq3*fG|pmX{kN^Y6M(;zOilms#Me8~@+#}rH{w{>61c)FpaDRJ#u>0m<899L*3CT^=@B5y7;tJ-xbBP_ zdSEaBg3HFD@_04K4H-B$2r#JdebSk8URwwebcETDaa)Z2NSThWc!KZ{?E`^%k%R&_ zVZ?tg>$9rcJbns;QM>DAR&Uec?uF8_1dSaASVY^BNwr3@kh}R%km%}pi$!f(8FZgv>_~SGrl)MC zq~C5OSI|u$L)<}ieF!8g2rXQWy9vq18C-LcD_26D@nP8oeBFb>NmYu{cBPSxn7lXz zXo3Wz2t3s9&*tIHmR7CnXXk%gUi4asVF1Kbl>wCvCf(adHcpjjf?>G;q#B4|Z zR5~*#`|ogR;vllrvWQ7?$n;r|h5Y3<2(t{Gs%;t(e;SGaFUVvMpO@Fn2gUgy|{2nCF3lq5xV19z!v@{iTZAYDCsmf3Vsm zoYR3)yS?OA?^9YA6JKRD_S32}7o@%+kzV+1uQ&8VU&Rd<52M3ocJp23IA+ttiV(;4 zC$AuNU}9pQt?ZbS41?%adEF8BhjG-DiB$uY9Gz4Qk*t>&FNDv-x!GV0aeZT>nudl1 z_#HDdvyfgRg_(THM71v%2);t3{#`kHqbYUPbMzylqr5v&?05p6EO9b@6^#{G$!116 zIB_yCgHFXICFB?pD*F10R#tT2gHAZOxV9f&5MZEE3nL1xXbYkjMr1n zP-XO5UWdnK3g6-iY#k?zsQNBv*N&ZIWo${XO*YENXT=w)ZoZvABax@d7Qut$eJ>Tt!zZKY-V)S9*Ja>^oq5oeF7q z?yQImEB`l`)eo_TNJkGw;Xr{}1DI{T`^3%3ujdzZe(yiFaWYnva1At}H;CL3&LqtztlTQ@k2d1gvwI*wMHH9!#+R0l;-%F4=)sD3H~m8=Z`YIdXKl>d-?yDs3U(2p52)jL3foF)ws?X{9KD_(6> zq+-X1fNK4nGx@vApa=wFMU|_`NdVR-<`Z*sn{wqyl~r(MOkS8Q)~7|Kvl~S59r%t= zD?yVFad6kZ-5uzBY|oTBpIL7|S+9zMk9U@aN=DC5P#NdhKDr?1ZxhC3=-vgAWfbNkWKf)M^ z$;E+qBZdir_3q@-%~;&m5VA`y#4N^>bM3PPN@aHcWI~bV@29m4~2CNG7x5g^=eKSnbLb%Z~TX|3>tgs zjr|C~#j=3YN&A^NgS}ksoBm z^^qkdEt;?#8aIs}}nHY2Ly3<^tN4lOmM|}D92>R5*ZpE-rx1_?M z@v30e&=*URQd*tG7Ar332QP=#Z=^&Db!|oG$&|n=Rc-r`f%HZJKs)SDN`=jtr#n8|16E%DB9w<+cJn_d zY`gam%eDSFD@3)QkV^Uh&@Io29NFuoiTe}GsM_Dz(8+>LLT(I89qzD6BjcKMs(3;< zY~n^xQ&3QGwC|31>{wC@9GMD;3`t*bW8)O5tEXkl6+TIq+>d>+GeJ7$i);5VX$vj1 zd7_izF4pV43&aY`N=17Lf9iGkPXa*VH$g{bA2w46eP*Nj^x?KuczaC=e?pe@vtRVY zT`evSSf2RwKy+%#p!Fxowv%D*y=#P2yq;7Z({R_O7k?QNX}qdUJ+jboVRTt;%W|7V z^yJ)X4NS*-cbc8$A^{%odbu_X@|d9ILe?7__Yt=%4;3DuyUo}zz*Q}1L>U!v8JGgR z7ixH2PMvZ`eiLb}Y6Pxk+yA35nclv|{bz=sv!`c6|4KA-rBPheZ|hnf{|-nu&^icLPZX1Tw`3-fSBe!1(nBGy*gJ@EVbLi{rbq$akcO*DNK2XGS#?**aS&+spu zZ!QquRd`%}_qd(?Biq@LlUA&2^N%<0kkeKj*L#sY(A(e74oz*A zC)UD6k&WQyn%ZwfuPyEkYg-F?_m@Kco;mt{pY)T?j<@-#`Btj(E@YMEez@IwJ?!%P ogIewm8J69n#D(}7?>->Tx++qQtCA_f2ggCkN-9ZIiWvs~56V~Ja{vGU literal 39695 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 image/svg+xml \ No newline at end of file + sodipodi:cx="364.64285" /> From a729e606a2c63578ffed38f1c8eab4c76a517651 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 23 Nov 2023 15:51:46 +0100 Subject: [PATCH 48/98] [Splash] correction of a square on the logo --- .../resources/pixmaps/medInria-splash.png | Bin 28283 -> 27931 bytes .../resources/pixmaps/medInriaLogo.svg | 26 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/app/medInria/resources/pixmaps/medInria-splash.png b/src/app/medInria/resources/pixmaps/medInria-splash.png index 522ce39876a5f300ce555a7e05b1cf2896d2367f..4197c3e69339e69ce5a7bfde440adcedf86bb9f2 100644 GIT binary patch literal 27931 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 literal 28283 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 diff --git a/src/app/medInria/resources/pixmaps/medInriaLogo.svg b/src/app/medInria/resources/pixmaps/medInriaLogo.svg index 65302f681e..31afca08b7 100644 --- a/src/app/medInria/resources/pixmaps/medInriaLogo.svg +++ b/src/app/medInria/resources/pixmaps/medInriaLogo.svg @@ -695,12 +695,12 @@ id="filter3082-6-4" inkscape:label="Drop shadow" width="1.0916864" - height="1.0894557" + height="1.1742913" x="-0.0323599" - y="-0.031572611"> Date: Thu, 23 Nov 2023 15:58:18 +0100 Subject: [PATCH 49/98] [Splash] no need to test here splashScreen variable --- src/app/medInria/medApplication.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/medInria/medApplication.cpp b/src/app/medInria/medApplication.cpp index f192e0b7e2..e15c0b9334 100644 --- a/src/app/medInria/medApplication.cpp +++ b/src/app/medInria/medApplication.cpp @@ -106,10 +106,7 @@ void medApplication::setMainWindow(medMainWindow *mw) d->systemOpenInstructions.clear(); // Wait until the app is displayed to close itself - if (d->splashScreen) - { - d->splashScreen->finish(d->mainWindow); - } + d->splashScreen->finish(d->mainWindow); } void medApplication::redirectMessageToSplash(const QString &message) From 86c19558c93f28b952ed9f0fd29c35e856d519ae Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Fri, 24 Nov 2023 10:25:02 +0100 Subject: [PATCH 50/98] [3.4] update release notes with current enhancement (#1169) --- RELEASE_NOTES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index dae16b6f61..9876b41b2a 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -3,6 +3,12 @@ medInria 3.4: - N4 Bias Correction process can stop now if the user wants to quit the application, it will stop at some algorithm checkpoints - Reslice toolbox: changing the dimension of spacing parameters does not zoom one of the views - Paint toolbox: the first click on the view quickly displays the drawing, instead of waiting for the mask to be created +- Update loading window logo at application start, and speed up the start removing text display +- Fix a bug with inrimage data type +- We have done some work to avoid memory leaks to get a more stable application +- Solve some graphical problem with settings widgets +- Add a setting in Browser to deactivate or activate the computation of thumbnail at import. Deactivation uses less RAM, if needed +- Avoid a crash importing wrong version of VTK files medInria 3.3: - Fix mirrored DICOM images problem From 223b930e7de5953b2da0eae01dd4ac36dff6aa29 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Tue, 28 Nov 2023 14:40:56 +0100 Subject: [PATCH 51/98] [Close] 4D+3D data in view crash at app closing --- .../medCoreLegacy/parameters/medAbstractParameterL.cpp | 2 -- .../medCoreLegacy/parameters/medDoubleParameterL.cpp | 8 +++++++- .../legacy/medCoreLegacy/parameters/medDoubleParameterL.h | 1 + .../medCoreLegacy/parameters/medTimeLineParameterL.cpp | 1 - 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp index 3885558a5d..2d3882a2f3 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp @@ -26,8 +26,6 @@ class medAbstractParameterLPrivate QString toolTip; bool hide; - - ~medAbstractParameterLPrivate() {delete label;} }; medAbstractParameterL::medAbstractParameterL(QString name, QObject *parent): diff --git a/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.cpp index 36d102b06f..28aa137721 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.cpp @@ -226,7 +226,7 @@ QLabel* medDoubleParameterL::getValueLabel() d->valueLabel->setText(QString::number(m_value, 'g', 2)); this->addToInternWidgets(d->valueLabel); - connect(d->valueLabel, SIGNAL(destroyed()), this, SLOT(removeInternSlider())); + connect(d->valueLabel, SIGNAL(destroyed()), this, SLOT(removeInternLabel())); } return d->valueLabel; @@ -249,6 +249,12 @@ void medDoubleParameterL::removeInternSlider() d->slider = nullptr; } +void medDoubleParameterL::removeInternLabel() +{ + this->removeFromInternWidgets(d->valueLabel); + d->valueLabel = nullptr; +} + int medDoubleParameterL::convertToInt(double value) { return static_cast((value-d->min)/d->step); diff --git a/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.h b/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.h index d48d855e15..30896081c2 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.h +++ b/src/layers/legacy/medCoreLegacy/parameters/medDoubleParameterL.h @@ -48,6 +48,7 @@ public slots: private slots: void removeInternSpinBox(); void removeInternSlider(); + void removeInternLabel(); void setSliderIntValue(int value); diff --git a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp index 0c799382af..d885558396 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp @@ -276,7 +276,6 @@ void medTimeLineParameterL::setDuration(const double& timeDuration) d->timeBetweenFrames = 0; d->timeLine->setDuration(timeDuration*1000); - } void medTimeLineParameterL::setFrame(int frame) From 4f7bae964fc6364b9b035a5d1d2c8928589509d5 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 29 Nov 2023 09:08:19 +0100 Subject: [PATCH 52/98] [Thumbnail] no need to warn if no thumbn since the thumbnail setting added --- .../medCoreLegacy/database/medAbstractDatabaseImporter.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp index a8595d8e6e..6712c9e2cf 100644 --- a/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medAbstractDatabaseImporter.cpp @@ -699,10 +699,7 @@ QString medAbstractDatabaseImporter::generateThumbnail ( medAbstractData* medDat qWarning("medAbstractDatabaseImporter: Cannot create directory: %s", qPrintable(pathToStoreThumbnail)); } - if ( ! thumbnail.save ( fullThumbnailPath, "PNG" )) - { - qWarning("medAbstractDatabaseImporter: Saving thumbnail to %s failed.", qPrintable(fullThumbnailPath)); - } + thumbnail.save(fullThumbnailPath, "PNG"); medData->addMetaData ( medMetaDataKeys::ThumbnailPath.key(), thumbnailPath ); From 63be31ac8c59f743292aec668f8bb6573476e4cf Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Wed, 29 Nov 2023 14:33:49 +0100 Subject: [PATCH 53/98] [Splash] add closing attribute to delete variable --- src/app/medInria/medApplication.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/medInria/medApplication.cpp b/src/app/medInria/medApplication.cpp index e15c0b9334..c8ff8ae130 100644 --- a/src/app/medInria/medApplication.cpp +++ b/src/app/medInria/medApplication.cpp @@ -52,6 +52,7 @@ medApplication::medApplication(int & argc, char**argv) : d->splashScreen = new QSplashScreen(QPixmap(":/pixmaps/medInria-splash.png"), Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); + d->splashScreen->setAttribute(Qt::WA_DeleteOnClose, true); d->splashScreen->show(); this->processEvents(); From a3e8c780c3b5728d68e4ef0b7528720f78a20924 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 30 Nov 2023 16:38:36 +0100 Subject: [PATCH 54/98] [Histogram] switch QObject+QGraphicsItem to QGraphicsObject --- .../medCoreLegacy/gui/lookUpTables/medClutEditor.cpp | 8 ++++---- .../legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.cpp b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.cpp index 76248dcf78..0f1cf730c9 100644 --- a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.cpp @@ -52,7 +52,7 @@ class medClutEditorVertexPrivate medClutEditorVertex::medClutEditorVertex(QPointF value, QPointF coord, QColor color, QGraphicsItem * parent) - : QObject(), QGraphicsItem(parent) + : QGraphicsObject(parent) { d = new medClutEditorVertexPrivate; d->value = value; @@ -91,7 +91,7 @@ medClutEditorVertex::medClutEditorVertex(QPointF value, QPointF coord, medClutEditorVertex::medClutEditorVertex( const medClutEditorVertex & other, QGraphicsItem * parent) - : QObject(), QGraphicsItem( parent ) + : QGraphicsObject(parent) { if ( parent == nullptr ) { @@ -431,7 +431,7 @@ class medClutEditorTablePrivate medClutEditorTable::medClutEditorTable(const QString & title, QGraphicsItem *parent) - :QObject(), QGraphicsItem(parent) + : QGraphicsObject(parent) { d = new medClutEditorTablePrivate; d->title = title; @@ -444,7 +444,7 @@ medClutEditorTable::medClutEditorTable(const QString & title, } medClutEditorTable::medClutEditorTable(const medClutEditorTable & table) - :QObject(), QGraphicsItem( static_cast< QGraphicsItem *>( table.parentItem() ) ) + : QGraphicsObject(static_cast< QGraphicsItem *>(table.parentItem())) { d = new medClutEditorTablePrivate; d->title = table.title(); diff --git a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h index 5bf1c7ef4e..d52cfb2415 100644 --- a/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h +++ b/src/layers/legacy/medCoreLegacy/gui/lookUpTables/medClutEditor.h @@ -26,11 +26,9 @@ class medAbstractImageView; class medClutEditorVertexPrivate; -// TODO use QGraphicsObjectItem noobs. -class MEDCORELEGACY_EXPORT medClutEditorVertex : public QObject, public QGraphicsItem +class MEDCORELEGACY_EXPORT medClutEditorVertex : public QGraphicsObject { Q_OBJECT - Q_INTERFACES(QGraphicsItem) public: medClutEditorVertex(QPointF value, QPointF coord, QColor color = Qt::white, QGraphicsItem *parent = nullptr); @@ -82,10 +80,9 @@ private : // ///////////////////////////////////////////////////////////////// class medClutEditorTablePrivate; -class MEDCORELEGACY_EXPORT medClutEditorTable : public QObject, public QGraphicsItem +class MEDCORELEGACY_EXPORT medClutEditorTable : public QGraphicsObject { Q_OBJECT - Q_INTERFACES(QGraphicsItem) public: medClutEditorTable(const medClutEditorTable & table); From 6dd792742799267238ec8f2ef7676da9a12a7f50 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 14 Dec 2023 13:02:29 +0100 Subject: [PATCH 55/98] [Thumbnail] remove a warning reading data --- .../legacy/medCoreLegacy/database/medDatabaseReader.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp index 4695242aaa..9eca4a1679 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp @@ -184,10 +184,6 @@ medAbstractData* medDatabaseReader::run() QString fullThumbnailPath(medStorage::dataLocation() + thumbnailPath); QFileInfo fullThumbnailPathInfo(fullThumbnailPath); - if (!fullThumbnailPathInfo.exists()) - { - qWarning("No thumbnail found at path: %s", qPrintable(fullThumbnailPath)); - } medMetaDataKeys::SeriesThumbnail.add (medData, fullThumbnailPath); medMetaDataKeys::PatientID.set ( medData, patientId ); From 29ed55eef7c596ea5e665ba4e13332d8588425a3 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Fri, 8 Dec 2023 13:58:32 +0100 Subject: [PATCH 56/98] [Parameters] solve mem leaks --- .../parameters/medAbstractParameterL.cpp | 3 +- .../parameters/medParameterPoolL.cpp | 9 +-- .../parameters/medTimeLineParameterL.cpp | 1 + .../parameters/medTimeLineParameterL.h | 2 + .../medAbstractImageViewNavigator.cpp | 56 ++++++++++--------- .../views/navigators/medAbstractNavigator.cpp | 1 + .../medVtkViewItkDataImageNavigator.cpp | 5 ++ 7 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp index 2d3882a2f3..a2fe8914ea 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medAbstractParameterL.cpp @@ -22,9 +22,8 @@ class medAbstractParameterLPrivate public: QString name; QList internWidget; - QLabel *label; + QPointer label; QString toolTip; - bool hide; }; diff --git a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolL.cpp index efc27b96a9..cf0e218573 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medParameterPoolL.cpp @@ -106,13 +106,14 @@ void medParameterPoolL::remove(medAbstractParameterL* parameter) disconnectParam(parameter); - QHashIterator it(d->pool); - + QMutableHashIterator it(d->pool); while(it.hasNext()) { it.next(); - if(it.value() == parameter) - d->pool.remove(it.key(), it.value()); + if(it.value() && it.value() == parameter) + { + it.remove(); + } } } diff --git a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp index d885558396..46666eb992 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp +++ b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.cpp @@ -147,6 +147,7 @@ medTimeLineParameterL::medTimeLineParameterL(QString name, QObject *parent): medTimeLineParameterL::~medTimeLineParameterL() { + emit aboutToBeDestroyed(); delete d; } diff --git a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.h b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.h index 899df9abbd..8f01cf0445 100644 --- a/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.h +++ b/src/layers/legacy/medCoreLegacy/parameters/medTimeLineParameterL.h @@ -73,6 +73,8 @@ public slots: void playing(bool isPlaying); void timeChanged(double time); + void aboutToBeDestroyed(); + private slots: void updateTime(double time); void updateFrameLabel(); diff --git a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp index eef54c2ae7..6758f9b5d2 100644 --- a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp +++ b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractImageViewNavigator.cpp @@ -47,12 +47,12 @@ medAbstractImageViewNavigator::medAbstractImageViewNavigator(medAbstractView *pa } connect(this, SIGNAL(currentTimeChanged(double)), d->view, SIGNAL(currentTimeChanged(double))); - connect(d->view, SIGNAL(layerAdded(uint)), this, SLOT(updateTimeLineParameter())); - connect(d->view, SIGNAL(layerRemoved(uint)), this, SLOT(updateTimeLineParameter())); + connect(d->view, SIGNAL(currentLayerChanged()), this, SLOT(updateTimeLineParameter()), Qt::UniqueConnection); } medAbstractImageViewNavigator::~medAbstractImageViewNavigator() { + delete d->timeLineParameter; delete d; } @@ -98,36 +98,42 @@ void medAbstractImageViewNavigator::setCurrentTime (const double &time) void medAbstractImageViewNavigator::updateTimeLineParameter() { - bool viewHasTemporalData = false; - - double sequenceDuration = 0; - double sequenceFrameRate = 0; - for(medDataIndex index : d->view->dataList()) + if (d->timeLineParameter) { - medAbstractData *data = medDataManager::instance().retrieveData(index); - if (!data) - continue; + bool viewHasTemporalData = false; + + double sequenceDuration = 0; + double sequenceFrameRate = 0; - if(data->hasMetaData("SequenceDuration") && data->hasMetaData("SequenceFrameRate")) + for(medDataIndex index : d->view->dataList()) { - double sd = data->metadata("SequenceDuration").toDouble(); - sequenceDuration = (sequenceDuration < sd) ? sd : sequenceDuration; + medAbstractData *data = medDataManager::instance().retrieveData(index); + if (!data) + continue; - double sf = data->metadata("SequenceFrameRate").toDouble(); - sequenceFrameRate = (sequenceFrameRate < sf) ? sf : sequenceFrameRate; + if(data->hasMetaData("SequenceDuration") && data->hasMetaData("SequenceFrameRate")) + { + double sd = data->metadata("SequenceDuration").toDouble(); + sequenceDuration = (sequenceDuration < sd) ? sd : sequenceDuration; - viewHasTemporalData = true; + double sf = data->metadata("SequenceFrameRate").toDouble(); + sequenceFrameRate = (sequenceFrameRate < sf) ? sf : sequenceFrameRate; + + viewHasTemporalData = true; + } + } + if(viewHasTemporalData) + { + unsigned int numFrames = (unsigned int)round(sequenceDuration * sequenceFrameRate); + d->timeLineParameter->setNumberOfFrame(numFrames); + d->timeLineParameter->setDuration(sequenceDuration); + d->timeLineParameter->show(); + } + else + { + d->timeLineParameter->hide(); } } - if(viewHasTemporalData) - { - unsigned int numFrames = (unsigned int)round(sequenceDuration * sequenceFrameRate); - d->timeLineParameter->setNumberOfFrame(numFrames); - d->timeLineParameter->setDuration(sequenceDuration); - d->timeLineParameter->show(); - } - else - d->timeLineParameter->hide(); } void medAbstractImageViewNavigator::toXMLNode(QDomDocument *doc, QDomElement *currentNode) diff --git a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractNavigator.cpp b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractNavigator.cpp index 1b2b7b158e..ef0696d98f 100644 --- a/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractNavigator.cpp +++ b/src/layers/legacy/medCoreLegacy/views/navigators/medAbstractNavigator.cpp @@ -46,6 +46,7 @@ medAbstractNavigator::medAbstractNavigator(medAbstractView *parent): medAbstractNavigator::~medAbstractNavigator() { + delete d->toolBoxWidget; delete d; } diff --git a/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp b/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp index 2f2d299d4e..41d2a923ef 100644 --- a/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp +++ b/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp @@ -114,6 +114,11 @@ medVtkViewItkDataImageNavigator::medVtkViewItkDataImageNavigator(medAbstractView medVtkViewItkDataImageNavigator::~medVtkViewItkDataImageNavigator() { + for(medAbstractParameterL *parameter : d->parameters) + { + delete parameter->getLabel(); + } + delete d; } From a88b70fe3a6618b468b97e18840cb85c51af69c7 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Fri, 19 Jan 2024 09:48:08 +0100 Subject: [PATCH 57/98] [Closing] no need to delete manually labels here --- .../navigators/medVtkViewItkDataImageNavigator.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp b/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp index 41d2a923ef..2f2d299d4e 100644 --- a/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp +++ b/src/plugins/legacy/itkDataImage/navigators/medVtkViewItkDataImageNavigator.cpp @@ -114,11 +114,6 @@ medVtkViewItkDataImageNavigator::medVtkViewItkDataImageNavigator(medAbstractView medVtkViewItkDataImageNavigator::~medVtkViewItkDataImageNavigator() { - for(medAbstractParameterL *parameter : d->parameters) - { - delete parameter->getLabel(); - } - delete d; } From 39bef3bfcd5176fcddfeb8adc546361a1e40fe7a Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Thu, 14 Dec 2023 10:27:55 +0100 Subject: [PATCH 58/98] [PolygonROI] ROI/repulsor crashs and interactor problems (#807) --- .../polygonRoi/toolboxes/polygonRoiToolBox.cpp | 5 ++++- .../legacy/polygonRoi/viewevent/baseViewEvent.cpp | 15 ++++++--------- .../legacy/polygonRoi/viewevent/baseViewEvent.h | 1 + .../polygonRoi/viewinteractors/polygonRoi.cpp | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index 3d7614a0ce..80b3ce3e21 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -395,6 +395,10 @@ void polygonRoiToolBox::clickClosePolygon(bool state) { viewEventHash[activeDataIndex]->onSelectContainer(); } + else if (!viewEventHash.empty()) + { + viewEventHash.values().first()->getCurrentView()->selectedRequest(true); + } } saveBinaryMaskButton->setEnabled(state); saveContourButton->setEnabled(state); @@ -465,7 +469,6 @@ void polygonRoiToolBox::clear() pMedToolBox->clear(); pMedToolBox->setEnabled(false); - } QList polygonRoiToolBox::getITKImageDataInSelectedView(medAbstractView *view) diff --git a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp index 482e9d9e39..789b1c084e 100644 --- a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp +++ b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp @@ -47,10 +47,13 @@ baseViewEvent::baseViewEvent(medAbstractImageView *iView, polygonRoiToolBox *too // interactors interactorStyleRepulsor = vtkInriaInteractorStylePolygonRepulsor::New(); + originalInteractor = view2d->GetInteractor(); } baseViewEvent::~baseViewEvent() { + activateRepulsor(false); + delete crossPosition; removeViewInteractor(); manageTickVisibility(false); @@ -59,13 +62,7 @@ baseViewEvent::~baseViewEvent() delete label; } labelList.clear(); - cursorState = CURSORSTATE::CS_DEFAULT; - isRepulsorActivated = false; - if (interactorStyleRepulsor) - { - interactorStyleRepulsor->Delete(); - interactorStyleRepulsor = nullptr; - } + currentLabel = nullptr; currentView = nullptr; } @@ -701,14 +698,14 @@ void baseViewEvent::activateRepulsor(bool state) } } } - else + else if (cursorState == CURSORSTATE::CS_REPULSOR) { cursorState = CURSORSTATE::CS_DEFAULT; vtkInteractorStyleImageView2D *interactorStyle2D = vtkInteractorStyleImageView2D::New(); globalVtkLeftButtonBehaviour = view2d->GetLeftButtonInteractionStyle(); interactorStyle2D->SetLeftButtonInteraction(vtkInteractorStyleImageView2D::InteractionTypeNull); view2d->SetInteractorStyle(interactorStyle2D); - view2d->SetupInteractor(view2d->GetInteractor()); // to reinstall vtkImageView2D pipeline + view2d->SetupInteractor(originalInteractor); interactorStyle2D->Delete(); } } diff --git a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h index 363e596fa7..b023d44cb8 100644 --- a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h +++ b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h @@ -120,6 +120,7 @@ private slots: QList copyNodesList; double savedMousePosition[2]; dtkSmartPointer contourOutput; + vtkRenderWindowInteractor* originalInteractor; void leftButtonBehaviour(medAbstractView *view); bool rightButtonBehaviour(medAbstractView *view, QMouseEvent *mouseEvent); diff --git a/src/plugins/legacy/polygonRoi/viewinteractors/polygonRoi.cpp b/src/plugins/legacy/polygonRoi/viewinteractors/polygonRoi.cpp index fcdb297a0c..7136aa852f 100644 --- a/src/plugins/legacy/polygonRoi/viewinteractors/polygonRoi.cpp +++ b/src/plugins/legacy/polygonRoi/viewinteractors/polygonRoi.cpp @@ -302,7 +302,7 @@ void polygonRoi::manageVisibility() } else { - if (d->view->GetSlice() == getIdSlice()) + if (isInCurrentSlice()) { On(); } @@ -520,7 +520,7 @@ bool polygonRoi::pasteContour(QVector &nodes) bool polygonRoi::isInCurrentSlice() { - return (d->view->GetSlice() == getIdSlice()); + return (static_cast(d->view->GetSlice()) == getIdSlice()); } void polygonRoi::setCurrentSlice() From cae309c0dfbf4a57c513a77a8bf60659cd31b981 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Wed, 8 Nov 2023 08:56:34 +0100 Subject: [PATCH 59/98] [4D data] fix Cropping, Variational Seg and VoiCutter toolboxes (#782) * [4D data] fix Cropping, Variational Seg and VoiCutter toolboxes * [CropTlbx] use proper return value --- .../medAbstractSelectableToolBox.cpp | 10 +++ .../legacy/reformat/medCropToolBox.cpp | 63 ++++++++++++------- src/plugins/legacy/reformat/medCropToolBox.h | 2 + .../variationalSegmentation/varSegToolBox.cpp | 4 +- .../legacy/voiCutter/voiCutterToolBox.cpp | 13 ++-- 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp index cec771a695..26982db74f 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp @@ -39,6 +39,16 @@ dtkPlugin* medAbstractSelectableToolBox::plugin() return nullptr; } +QList > medAbstractSelectableToolBox::processOutputs() +{ + QList> outputs; + if (auto toolboxOutput = processOutput()) + { + outputs.append(toolboxOutput); + } + return outputs; +} + void medAbstractSelectableToolBox::setSelectorToolBox(medSelectorToolBox *toolbox) { d->selectorToolBox = toolbox; diff --git a/src/plugins/legacy/reformat/medCropToolBox.cpp b/src/plugins/legacy/reformat/medCropToolBox.cpp index d22b72b2fd..49187fbf18 100644 --- a/src/plugins/legacy/reformat/medCropToolBox.cpp +++ b/src/plugins/legacy/reformat/medCropToolBox.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -66,7 +67,6 @@ class medCropToolBoxPrivate void normalizedViewportToWorld(double *viewportPoint, double *worldPoint); void applyOrientationMatrix(double *worldPointIn, double *WorldPointOut); void applyInverseOrientationMatrix(double *worldPointIn, double *worldPointOut); - void generateOutput(); template int extractCroppedImage(medAbstractData *input, int *minIndices, int *maxIndices, medAbstractData **output); void replaceViewWithOutputData(medAbstractWorkspaceLegacy &workspace); void importOutput(); @@ -143,6 +143,7 @@ medCropToolBox::medCropToolBox(QWidget* parent) d->view = nullptr; d->view2D = nullptr; d->view3D = nullptr; + enableButtons(false); } medCropToolBox::~medCropToolBox() @@ -157,10 +158,10 @@ dtkPlugin* medCropToolBox::plugin() medAbstractData* medCropToolBox::processOutput() { - if (d->view) - { - d->generateOutput(); - } + applyCrop(); + + // This value can be tested in pipelines to check if the process went well. + // We'll retrieve all the output data directly from the view. return d->outputData.value(0); } @@ -188,12 +189,14 @@ void medCropToolBox::updateView() medAbstractData *data = view->layerData(i); if(!data || data->identifier().contains("vtkDataMesh") || !data->identifier().contains("itkDataImage") //avoid medVtkFibersData also - || data->identifier().contains("itkDataImageVector")) + || data->identifier().contains("itkDataImageVector") + || (qobject_cast(data)->Dimension() != 3)) { handleDisplayError(medAbstractProcessLegacy::DIMENSION_3D); d->view = nullptr; d->view2D = nullptr; d->view3D = nullptr; + enableButtons(false); return; } } @@ -216,6 +219,8 @@ void medCropToolBox::updateView() d->inverseOrientationMatrix->Invert(); d->view2D->GetInteractorStyle()->AddObserver(vtkImageView2DCommand::CameraMoveEvent, d->cropCallback); + + enableButtons(true); } d->updateBorderWidgetIfVisible(); @@ -227,15 +232,24 @@ void medCropToolBox::updateView() d->view = nullptr; d->view2D = nullptr; d->view3D = nullptr; + enableButtons(false); } } +void medCropToolBox::enableButtons(bool wantToEnable) +{ + d->applyButton->setEnabled(wantToEnable); + d->saveButton->setEnabled(wantToEnable); +} + void medCropToolBox::applyCrop() { if (d->view) { - d->generateOutput(); - d->replaceViewWithOutputData(*getWorkspace()); + if (generateOutput() == medAbstractProcessLegacy::SUCCESS) + { + d->replaceViewWithOutputData(*getWorkspace()); + } } } @@ -445,36 +459,39 @@ void medCropToolBoxPrivate::applyInverseOrientationMatrix(double *worldPointIn, std::copy(homogeneousVector, homogeneousVector + 3, worldPointOut); } -void medCropToolBoxPrivate::generateOutput() +int medCropToolBox::generateOutput() { + int res = medAbstractProcessLegacy::FAILURE; + double minBoxCorner[3], maxBoxCorner[3]; int minIndices[3], maxIndices[3]; - getMinAndMaxBoxCorners(minBoxCorner, maxBoxCorner); - applyOrientationMatrix(minBoxCorner, minBoxCorner); - applyOrientationMatrix(maxBoxCorner, maxBoxCorner); - view3D->GetImageCoordinatesFromWorldCoordinates(minBoxCorner, minIndices); - view3D->GetImageCoordinatesFromWorldCoordinates(maxBoxCorner, maxIndices); + d->getMinAndMaxBoxCorners(minBoxCorner, maxBoxCorner); + d->applyOrientationMatrix(minBoxCorner, minBoxCorner); + d->applyOrientationMatrix(maxBoxCorner, maxBoxCorner); + d->view3D->GetImageCoordinatesFromWorldCoordinates(minBoxCorner, minIndices); + d->view3D->GetImageCoordinatesFromWorldCoordinates(maxBoxCorner, maxIndices); - outputData.clear(); + d->outputData.clear(); - for (unsigned int layer = 0; layer < view->layersCount(); layer++) + for (unsigned int layer = 0; layer < d->view->layersCount(); layer++) { - medAbstractData *imageData = view->layerData(layer); + medAbstractData *imageData = d->view->layerData(layer); medAbstractData *output = nullptr; - if (DISPATCH_ON_3D_PIXEL_TYPE(&medCropToolBoxPrivate::extractCroppedImage, - this, imageData, minIndices, maxIndices, &output) - == medAbstractProcessLegacy::SUCCESS) + res = DISPATCH_ON_3D_PIXEL_TYPE(&medCropToolBoxPrivate::extractCroppedImage, + d, imageData, minIndices, maxIndices, &output); + if (res == medAbstractProcessLegacy::SUCCESS) { - outputData.append(output); + d->outputData.append(output); } else { - medMessageController::instance().showError("Drop a 3D volume in the view", 3000); - qDebug()<<__FILE__<<":"<<__LINE__<identifier(); + handleDisplayError(res); + break; } } + return res; } template diff --git a/src/plugins/legacy/reformat/medCropToolBox.h b/src/plugins/legacy/reformat/medCropToolBox.h index 9ef2eb5b76..666ea53245 100644 --- a/src/plugins/legacy/reformat/medCropToolBox.h +++ b/src/plugins/legacy/reformat/medCropToolBox.h @@ -49,6 +49,8 @@ public slots: protected: void showEvent(QShowEvent *event); virtual void clear(); + int generateOutput(); + void enableButtons(bool wantToEnable); private: medCropToolBoxPrivate* const d; diff --git a/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp b/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp index c9d1636a0a..3552c89d07 100644 --- a/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp +++ b/src/plugins/legacy/variationalSegmentation/varSegToolBox.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -281,7 +282,8 @@ void VarSegToolBox::updateView() medAbstractData *data = d->currentView->layerData(i); if(!data || data->identifier().contains("vtkDataMesh") || !data->identifier().contains("itkDataImage") //avoid medVtkFibersData also - || data->identifier().contains("itkDataImageVector")) + || data->identifier().contains("itkDataImageVector") + || (qobject_cast(data)->Dimension() != 3)) { handleDisplayError(medAbstractProcessLegacy::DIMENSION_3D); d->currentView = nullptr; diff --git a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp index e8b99bde0a..40affe00de 100644 --- a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp +++ b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -181,7 +182,8 @@ void voiCutterToolBox::updateView() medAbstractData *data = d->currentView->layerData(i); if(!data || data->identifier().contains("vtkDataMesh") || !data->identifier().contains("itkDataImage") //avoid medVtkFibersData also - || data->identifier().contains("itkDataImageVector")) + || data->identifier().contains("itkDataImageVector") + || (qobject_cast(data)->Dimension() != 3)) { handleDisplayError(medAbstractProcessLegacy::DIMENSION_3D); d->currentView = nullptr; @@ -275,13 +277,12 @@ dtkPlugin* voiCutterToolBox::plugin() medAbstractData *voiCutterToolBox::processOutput() { - if (!d->resultData) + if (d->resultData) { - return d->currentView->layerData(d->currentView->currentLayer()); + fillOutputMetaData(); + return d->resultData; } - - fillOutputMetaData(); - return d->resultData; + return nullptr; } void voiCutterToolBox::activateButtons(bool param) From bd076a25c583bcc80978a769fafe85d7b66a2bbc Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Fri, 19 Jan 2024 14:06:22 +0100 Subject: [PATCH 60/98] [ProcessOutputs] adapt methods to processoutputs --- .../gui/toolboxes/medAbstractSelectableToolBox.cpp | 1 + .../medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp index 26982db74f..54bce7de7a 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.cpp @@ -14,6 +14,7 @@ #include #include #include +#include class medAbstractSelectableToolBoxPrivate { diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h index 1d22014adf..a997fa45de 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h @@ -14,6 +14,7 @@ #include #include +#include class medAbstractData; class medSelectorToolBox; @@ -31,7 +32,7 @@ class MEDCORELEGACY_EXPORT medAbstractSelectableToolBox : public medToolBox virtual dtkPlugin* plugin(); virtual medAbstractData *processOutput() = 0; - + virtual QList> processOutputs(); void setSelectorToolBox(medSelectorToolBox *toolbox); public slots: From bd070bde5c1365e17542f6cafde710a9ca510ba9 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 6 Nov 2023 08:44:43 +0100 Subject: [PATCH 61/98] [manualRegistration] solve top views size problem (#781) --- .../legacy/manualRegistration/manualRegistrationToolBox.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp b/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp index d02dd91a6b..db9a4614b5 100644 --- a/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp +++ b/src/plugins/legacy/manualRegistration/manualRegistrationToolBox.cpp @@ -464,7 +464,7 @@ void manualRegistrationToolBox::constructContainers(medTabbedViewContainers* tab d->leftContainer = tabContainers->insertNewTab(0,"ManualRegistration"); tabContainers->setCurrentIndex(0); - d->leftContainer->addData(d->currentView->layerData(0));// + d->leftContainer->addData(d->currentView->layerData(0)); qobject_cast(d->leftContainer->view())->windowLevelParameter(0)->setValues(windowing0); d->bottomContainer = d->leftContainer->splitHorizontally(); @@ -472,12 +472,13 @@ void manualRegistrationToolBox::constructContainers(medTabbedViewContainers* tab qobject_cast(d->bottomContainer->view())->windowLevelParameter(0)->setValues(windowing0); d->bottomContainer->addData(d->currentView->layerData(1)); qobject_cast(d->bottomContainer->view())->windowLevelParameter(1)->setValues(windowing1); - tabContainers->containersInTab(0); d->rightContainer = d->leftContainer->splitVertically(); d->rightContainer->addData(d->currentView->layerData(1)); qobject_cast(d->rightContainer->view())->windowLevelParameter(0)->setValues(windowing1); + tabContainers->adjustContainersSize(); + d->leftContainer->setUserSplittable(false); d->rightContainer->setUserSplittable(false); d->bottomContainer->setUserSplittable(false); From 5d9c5d36ee1dd6a37780976642871c1f5c67743d Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 11 Oct 2023 09:17:34 +0200 Subject: [PATCH 62/98] [polygonRoi] process output returned nullptr (#772) --- src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index 3d7614a0ce..c5f79ff14f 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -161,7 +161,7 @@ dtkPlugin* polygonRoiToolBox::plugin() medAbstractData *polygonRoiToolBox::processOutput() { - return nullptr; + return processOutputs()[0]; } void polygonRoiToolBox::updateView() From a6821469d40a617da7ebd6a6adf0af6a519706e0 Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 18 Oct 2023 11:11:22 +0200 Subject: [PATCH 63/98] [polygonRoi] fix processsOutput crash when no roi (#777) --- src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index c5f79ff14f..1c570c9d49 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -161,7 +161,7 @@ dtkPlugin* polygonRoiToolBox::plugin() medAbstractData *polygonRoiToolBox::processOutput() { - return processOutputs()[0]; + return processOutputs().value(0); } void polygonRoiToolBox::updateView() From 32320598134159427dd85a5b568a3d12f7d64f27 Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 11 Oct 2023 08:18:16 +0200 Subject: [PATCH 64/98] Remove annotation data when destroying paint toolbox (#771) --- .../legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index bf14d02d1d..d09bbc05ea 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -507,6 +507,7 @@ AlgorithmPaintToolBox::AlgorithmPaintToolBox(QWidget *parent ) : AlgorithmPaintToolBox::~AlgorithmPaintToolBox() { setOfPaintBrushRois.clear(); + m_imageData->removeAttachedData(m_maskAnnotationData); } medAbstractData* AlgorithmPaintToolBox::processOutput() From 3baee54302e526d942d0ace7913aeb859ff72258 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 16 Oct 2023 15:01:29 +0200 Subject: [PATCH 65/98] [Paint] crash at app closing if no painting (#776) --- .../legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index d09bbc05ea..6f27c0ed5a 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -507,7 +507,11 @@ AlgorithmPaintToolBox::AlgorithmPaintToolBox(QWidget *parent ) : AlgorithmPaintToolBox::~AlgorithmPaintToolBox() { setOfPaintBrushRois.clear(); - m_imageData->removeAttachedData(m_maskAnnotationData); + + if (m_imageData && m_maskAnnotationData) + { + m_imageData->removeAttachedData(m_maskAnnotationData); + } } medAbstractData* AlgorithmPaintToolBox::processOutput() From f4de5f3b85a05ffb081a398b79c07e8eddbbf5f8 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Thu, 19 Oct 2023 11:06:44 +0200 Subject: [PATCH 66/98] [PolygonRoi] avoid spam of warning if contour outside image (#775) * [PolygonRoi] avoid spam of warning if contour outside image * [PolygonRoi] loop index from 0 --- .../legacy/polygonRoi/polygonLabel.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plugins/legacy/polygonRoi/polygonLabel.cpp b/src/plugins/legacy/polygonRoi/polygonLabel.cpp index 797dca09f3..a3d98f0936 100644 --- a/src/plugins/legacy/polygonRoi/polygonLabel.cpp +++ b/src/plugins/legacy/polygonRoi/polygonLabel.cpp @@ -566,15 +566,18 @@ void polygonLabel::createMask(int label, QString &desc) { for(int j=(int)bounds[by]; j<=(int)bounds[by+1]; j++) { - QRgb val = imagePolygon.pixel(i,j); - - if (val==qRgb(255,255,255)) + if (i>=0 && i=0 && jSetPixel(index,label+1); + QRgb val = imagePolygon.pixel(i,j); + + if (val==qRgb(255,255,255)) + { + UChar3ImageType::IndexType index; + index[x]=i; + index[y]=j; + index[z]=polygon.second; + m_itkMask->SetPixel(index,label+1); + } } } } From 84cd34d8c5db2c2c633ee9fc25ed9945bb27bf13 Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Thu, 30 Nov 2023 14:03:00 +0100 Subject: [PATCH 67/98] [Reslice] proper release of RAM (#774) * [Reslice] proper release of RAM * [Reslice] copy view data before destruction * [Reslice] in case user drops 2D image in view (we never know...) * [vtkImage2DDisplay] vtkSmartPointer instead of Delete * [Reslice] use std::array and avoid a loop * [ResliceToolBox] use std::array and avoid a loop * [view] put back previous behavior, if needed it'll be changed by medInria * [view] rm deleted variable set to nullptr as in original PR * [merge] prepare rebase --- .../legacy/reformat/medResliceViewer.cpp | 19 +- .../legacy/reformat/medResliceViewer.h | 12 +- .../legacy/reformat/resliceToolBox.cpp | 198 +++++++++--------- src/plugins/legacy/reformat/resliceToolBox.h | 8 +- 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/src/plugins/legacy/reformat/medResliceViewer.cpp b/src/plugins/legacy/reformat/medResliceViewer.cpp index 7cedc4ca4f..c5e04aedf4 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.cpp +++ b/src/plugins/legacy/reformat/medResliceViewer.cpp @@ -17,12 +17,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include @@ -36,13 +36,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include class medResliceCursorCallback : public vtkCommand { @@ -122,7 +122,10 @@ class medResliceCursorCallback : public vtkCommand reformatViewer->getImagePlaneWidget(0)->GetInteractor()->GetRenderWindow()->Render(); } - medResliceCursorCallback() {} + ~medResliceCursorCallback() + { + reformatViewer = nullptr; + } medResliceViewer *reformatViewer; }; @@ -140,7 +143,10 @@ medResliceViewer::medResliceViewer(medAbstractView *view, QWidget *parent): medA selectedView = 2; int *imageDims = view3d->GetMedVtkImageInfo()->dimensions; - outputSpacingOrSize = view3d->GetMedVtkImageInfo()->spacing; + + // Copy view information before it's destroyed, allowing to use outputSpacingOrSize later. + double *spacing = view3d->GetMedVtkImageInfo()->spacing; + memcpy(outputSpacingOrSize.data(), spacing, 3*sizeof(double)); viewBody = new QWidget(parent); @@ -167,7 +173,7 @@ medResliceViewer::medResliceViewer(medAbstractView *view, QWidget *parent): medA } // Position of the new views in tab - QGridLayout *gridLayout = new QGridLayout(parent); + QGridLayout *gridLayout = new QGridLayout(); gridLayout->addWidget(views[2], 0, 0); gridLayout->addWidget(views[3], 0, 1); gridLayout->addWidget(views[1], 1, 0); @@ -286,6 +292,7 @@ medResliceViewer::~medResliceViewer() planeWidget[i] = nullptr; } vtkViewData = nullptr; + inputData = nullptr; } QString medResliceViewer::identifier() const @@ -384,7 +391,7 @@ void medResliceViewer::saveImage() // Apply resampling in mm if (reformaTlbx->findChild("bySpacingOrSize")->currentText() == "Spacing") { - reslicerTop->SetOutputSpacing(outputSpacingOrSize); + reslicerTop->SetOutputSpacing(outputSpacingOrSize.data()); } reslicerTop->Update(); diff --git a/src/plugins/legacy/reformat/medResliceViewer.h b/src/plugins/legacy/reformat/medResliceViewer.h index 4ad3386fed..e0d807a128 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.h +++ b/src/plugins/legacy/reformat/medResliceViewer.h @@ -12,18 +12,18 @@ PURPOSE. =========================================================================*/ +#include "resliceToolBox.h" + +#include +#include + #include #include -#include - #include -#include - #include -#include #include #include @@ -85,7 +85,7 @@ public slots: QWidget *viewBody; QVTKOpenGLWidget *views[4]; dtkSmartPointer inputData; - double *outputSpacingOrSize; + std::array outputSpacingOrSize; unsigned char selectedView; vtkImageView3D *view3d; vtkSmartPointer vtkViewData; diff --git a/src/plugins/legacy/reformat/resliceToolBox.cpp b/src/plugins/legacy/reformat/resliceToolBox.cpp index 84f7232ce3..22ef73a45a 100644 --- a/src/plugins/legacy/reformat/resliceToolBox.cpp +++ b/src/plugins/legacy/reformat/resliceToolBox.cpp @@ -11,6 +11,7 @@ =========================================================================*/ #include "resliceToolBox.h" +#include "medResliceViewer.h" #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -31,31 +31,38 @@ class resliceToolBoxPrivate QComboBox *bySpacingOrSize; QLabel *helpBegin; medDoubleParameterL *spacingOrSizeX, *spacingOrSizeY, *spacingOrSizeZ; - medAbstractLayeredView *currentView; medResliceViewer *resliceViewer; dtkSmartPointer reformatedImage; QWidget *reformatOptions; - medVtkImageInfo* imageInfo; + std::array spacing; + std::array dimensions; + medAbstractData *originalImage; }; resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox (parent), d(new resliceToolBoxPrivate) { - // Fill the toolBox - QWidget *resliceToolBoxBody = new QWidget(this); - d->b_startReslice = new QPushButton("Start Reslice", resliceToolBoxBody); + QWidget *resliceToolBoxBody = new QWidget(); + this->addWidget(resliceToolBoxBody); + + QVBoxLayout *resliceToolBoxLayout = new QVBoxLayout(resliceToolBoxBody); + + d->b_startReslice = new QPushButton("Start Reslice"); d->b_startReslice->setCheckable(false); d->b_startReslice->setObjectName("startReformatButton"); - d->b_stopReslice = new QPushButton("Stop Reslice", resliceToolBoxBody); + resliceToolBoxLayout->addWidget(d->b_startReslice); + + d->b_stopReslice = new QPushButton("Stop Reslice"); d->b_stopReslice->setCheckable(false); d->b_stopReslice->setObjectName("stopReformatButton"); - d->b_saveImage = new QPushButton("Save Image", resliceToolBoxBody); + + d->b_saveImage = new QPushButton("Save Image"); d->b_saveImage->setCheckable(false); d->b_saveImage->setObjectName("saveImageButton"); // User can choose pixel or millimeter resample QHBoxLayout * resampleLayout = new QHBoxLayout(); - QLabel *bySpacingOrSizeLabel = new QLabel("Select your resample parameter ", resliceToolBoxBody); - d->bySpacingOrSize = new QComboBox(resliceToolBoxBody); + QLabel *bySpacingOrSizeLabel = new QLabel("Select your resample parameter "); + d->bySpacingOrSize = new QComboBox(); d->bySpacingOrSize->setObjectName("bySpacingOrSize"); d->bySpacingOrSize->addItem("Spacing"); d->bySpacingOrSize->addItem("Size"); @@ -64,8 +71,7 @@ resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox connect(d->bySpacingOrSize, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(switchSpacingAndDimension(const QString&))); // Spinboxes of resample values - QWidget *spinBoxes = new QWidget(resliceToolBoxBody); - QVBoxLayout *spacingSpinBoxLayout = new QVBoxLayout(resliceToolBoxBody); + QVBoxLayout *spacingSpinBoxLayout = new QVBoxLayout(); QHBoxLayout *spacingSpinBoxLayoutX = new QHBoxLayout(); spacingSpinBoxLayoutX->setAlignment(Qt::AlignCenter); @@ -102,52 +108,43 @@ resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getLabel()); spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutZ); - - spinBoxes->setLayout(spacingSpinBoxLayout); - - QVBoxLayout *resliceToolBoxLayout = new QVBoxLayout(resliceToolBoxBody); - d->helpBegin = new QLabel("Drop a data in the view and click on 'Start Reslice'.", resliceToolBoxBody); + + d->helpBegin = new QLabel("Drop a data in the view and click on 'Start Reslice'."); d->helpBegin->setObjectName("helpBegin"); d->helpBegin->setStyleSheet("font: italic"); resliceToolBoxLayout->addWidget(d->helpBegin); - QLabel *help1 = new QLabel("To change the windowing left click on a 2D view.", resliceToolBoxBody); - QLabel *help2 = new QLabel("To reset axes in a view press 'o'.", resliceToolBoxBody); - QLabel *help3 = new QLabel("To reset windowing in a view press 'r'.\n", resliceToolBoxBody); + QLabel *help1 = new QLabel("To change the windowing left click on a 2D view.\n" + "To reset axes in a view press 'o'.\n" + "To reset windowing in a view press 'r'.\n" + ); help1->setStyleSheet("font: italic"); - help2->setStyleSheet("font: italic"); - help3->setStyleSheet("font: italic"); - resliceToolBoxLayout->addWidget(d->b_startReslice); - - d->reformatOptions = new QWidget(resliceToolBoxBody); + d->reformatOptions = new QWidget(); QVBoxLayout *reformatOptionsLayout = new QVBoxLayout(d->reformatOptions); d->reformatOptions->setLayout(reformatOptionsLayout); d->reformatOptions->hide(); resliceToolBoxLayout->addWidget(d->reformatOptions); reformatOptionsLayout->addWidget(help1); - reformatOptionsLayout->addWidget(help2); - reformatOptionsLayout->addWidget(help3); reformatOptionsLayout->addLayout(resampleLayout); - reformatOptionsLayout->addWidget(spinBoxes); + reformatOptionsLayout->addLayout(spacingSpinBoxLayout); reformatOptionsLayout->addWidget(d->b_saveImage); reformatOptionsLayout->addWidget(d->b_stopReslice); resliceToolBoxBody->setLayout(resliceToolBoxLayout); - this->addWidget(resliceToolBoxBody); // Connections connect(d->b_startReslice,SIGNAL(clicked()),this,SLOT(startReformat())); connect(d->b_stopReslice,SIGNAL(clicked()),this,SLOT(stopReformat())); d->resliceViewer = nullptr; - d->currentView = nullptr; + d->originalImage = nullptr; } resliceToolBox::~resliceToolBox() { delete d->resliceViewer; d->resliceViewer = nullptr; - d->currentView = nullptr; + d->originalImage = nullptr; delete d; d = nullptr; @@ -165,52 +162,55 @@ dtkPlugin* resliceToolBox::plugin() void resliceToolBox::startReformat() { - if (d->currentView && getWorkspace()) + if (getWorkspace()) { - medAbstractData* data = d->currentView->layerData(d->currentView->currentLayer()); - bool is3D = false; + auto tabbedContainers = getWorkspace()->tabbedViewContainers(); + auto container = tabbedContainers->getFirstSelectedContainer(); + auto view = qobject_cast(tabbedContainers->getFirstSelectedContainerView()); - // Toolbox does not work with meshes or vector images - if (data->identifier().contains("itkDataImage") && - !data->identifier().contains("Vector")) + if (view) { - if (dynamic_cast(data)->Dimension() == 3) - { - is3D = true; - } + medAbstractData* data = view->layerData(view->currentLayer()); + bool is3D = false; - if (d->currentView->layersCount() && is3D) + // Toolbox does not work with meshes or vector images + if (data->identifier().contains("itkDataImage") && + !data->identifier().contains("Vector")) { - d->helpBegin->hide(); - d->reformatOptions->show(); - d->b_startReslice->hide(); - - d->resliceViewer = new medResliceViewer(d->currentView, - getWorkspace()->tabbedViewContainers()); - d->resliceViewer->setToolBox(this); - getWorkspace()->tabbedViewContainers()->setAcceptDrops(false); - medViewContainer * container = getWorkspace()->tabbedViewContainers()->insertNewTab(0, "Reslice"); - getWorkspace()->tabbedViewContainers()->setCurrentIndex(0); - container->setDefaultWidget(d->resliceViewer->viewWidget()); - - connect(d->resliceViewer,SIGNAL(imageReformatedGenerated()), - this,SLOT(saveReformatedImage()), Qt::UniqueConnection); - connect(container, SIGNAL(viewRemoved()), - this, SLOT(stopReformat()), Qt::UniqueConnection); - connect(d->spacingOrSizeX, SIGNAL(valueChanged(double)), - d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); - connect(d->spacingOrSizeY, SIGNAL(valueChanged(double)), - d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); - connect(d->spacingOrSizeZ, SIGNAL(valueChanged(double)), - d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); - connect(d->b_saveImage, SIGNAL(clicked()), - d->resliceViewer, SLOT(saveImage()), Qt::UniqueConnection); - - d->reformatedImage = nullptr; - - // close the initial tab which is not needed anymore - getWorkspace()->tabbedViewContainers()->removeTab(1); - updateView(); + if (dynamic_cast(data)->Dimension() == 3) + { + is3D = true; + } + + if (view->layersCount() && is3D) + { + d->helpBegin->hide(); + d->reformatOptions->show(); + d->b_startReslice->hide(); + + d->resliceViewer = new medResliceViewer(view, container); + d->resliceViewer->setToolBox(this); + + container->removeView(); + container->setDefaultWidget(d->resliceViewer->viewWidget()); + + connect(d->resliceViewer,SIGNAL(imageReformatedGenerated()), + this,SLOT(saveReformatedImage()), Qt::UniqueConnection); + connect(d->spacingOrSizeX, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->spacingOrSizeY, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->spacingOrSizeZ, SIGNAL(valueChanged(double)), + d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); + connect(d->b_saveImage, SIGNAL(clicked()), + d->resliceViewer, SLOT(saveImage()), Qt::UniqueConnection); + + d->reformatedImage = nullptr; + } + else + { + medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); + } } else { @@ -222,34 +222,30 @@ void resliceToolBox::startReformat() medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); } } - else - { - medMessageController::instance().showError(tr("Drop a 3D volume in the view"), 3000); - } } void resliceToolBox::stopReformat() { - if (getWorkspace()) + if (getWorkspace() && d->resliceViewer) { d->bySpacingOrSize->setCurrentIndex(0); // init resample parameter d->helpBegin->show(); d->reformatOptions->hide(); d->b_startReslice->show(); - getWorkspace()->tabbedViewContainers()->removeTab(0); - getWorkspace()->tabbedViewContainers()->insertNewTab(0, getWorkspace()->name()); - getWorkspace()->tabbedViewContainers()->setCurrentIndex(0); + auto tabbedContainers = getWorkspace()->tabbedViewContainers(); + auto container = tabbedContainers->getFirstSelectedContainer(); + container->initializeDefaultWidget(); - if (d->currentView) + if (d->originalImage) { - getWorkspace()->tabbedViewContainers()->getFirstSelectedContainer()->addData(d->currentView->layerData(0)); + container->addData(d->originalImage); } disconnect(d->resliceViewer); delete d->resliceViewer; - d->resliceViewer = nullptr; + QList toolBoxes = getWorkspace()->toolBoxes(); for (int i = 0; i < toolBoxes.length(); i++) { @@ -265,20 +261,26 @@ void resliceToolBox::clear() void resliceToolBox::updateView() { + // Even if updateView is called because of an empty view, we want to keep the original data, + // so the toolbox can be reset to the original one. medAbstractView* view = this->getWorkspace()->tabbedViewContainers()->getFirstSelectedContainerView(); - - d->currentView = nullptr; if (view) { medAbstractLayeredView *layeredView = qobject_cast(view); - if (dynamic_cast(layeredView->layerData(layeredView->currentLayer()))) + if (auto data = dynamic_cast(layeredView->layerData(layeredView->currentLayer()))) { - d->currentView = layeredView; + if (data->Dimension() == 3) + { + d->originalImage = data; + + // Copy original image information and display them + vtkImageView2D *view2d = static_cast(layeredView->backend())->view2D; + auto imageInfo = view2d->GetMedVtkImageInfo(); + memcpy(d->spacing.data(), imageInfo->spacing, 3*sizeof(double)); + memcpy(d->dimensions.data(), imageInfo->dimensions, 3*sizeof(int)); - // Get the original image information and display it - vtkImageView2D *view2d = static_cast(d->currentView->backend())->view2D; - d->imageInfo = view2d->GetMedVtkImageInfo(); - displayInfoOnCurrentView(); + displayInfoOnCurrentView(); + } } } } @@ -287,17 +289,15 @@ void resliceToolBox::displayInfoOnCurrentView() { if (d->bySpacingOrSize->currentIndex() == 0) // Spacing { - double *spacing = d->imageInfo->spacing; - d->spacingOrSizeX->setValue(spacing[0]); - d->spacingOrSizeY->setValue(spacing[1]); - d->spacingOrSizeZ->setValue(spacing[2]); + d->spacingOrSizeX->setValue(d->spacing[0]); + d->spacingOrSizeY->setValue(d->spacing[1]); + d->spacingOrSizeZ->setValue(d->spacing[2]); } else // Dimension { - int *dimension = d->imageInfo->dimensions; - d->spacingOrSizeX->setValue(dimension[0]); - d->spacingOrSizeY->setValue(dimension[1]); - d->spacingOrSizeZ->setValue(dimension[2]); + d->spacingOrSizeX->setValue(d->dimensions[0]); + d->spacingOrSizeY->setValue(d->dimensions[1]); + d->spacingOrSizeZ->setValue(d->dimensions[2]); } } diff --git a/src/plugins/legacy/reformat/resliceToolBox.h b/src/plugins/legacy/reformat/resliceToolBox.h index 2dcfe76f03..8fb2c7c8b8 100644 --- a/src/plugins/legacy/reformat/resliceToolBox.h +++ b/src/plugins/legacy/reformat/resliceToolBox.h @@ -23,10 +23,10 @@ class resliceToolBoxPrivate; * "startReformatButton" : QPushButton\n * "stopReformatButton" : QPushButton\n * "saveImageButton" : QPushButton\n - * "bySpacingOrDimension" : medComboBox\n - * "SpacingX" : QDoubleSpinBox\n - * "SpacingY" : QDoubleSpinBox\n - * "SpacingZ" : QDoubleSpinBox\n + * "bySpacingOrDimension" : QComboBox\n + * "SpacingOrSizeX" : medDoubleParameterL\n + * "SpacingOrSizeY" : medDoubleParameterL\n + * "SpacingOrSizeZ" : medDoubleParameterL\n * "helpBegin" : QLabel */ class REFORMATPLUGIN_EXPORT resliceToolBox : public medAbstractSelectableToolBox From fdaa4955b48dd8b49d742a1acdd43a4b77c3b33e Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 11 Oct 2023 11:00:36 +0200 Subject: [PATCH 68/98] [polygonRoi] hide activation label (#773) --- src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index 3d7614a0ce..1736acacbc 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -59,6 +59,7 @@ polygonRoiToolBox::polygonRoiToolBox(QWidget *parent ) : activateTBButton->setCheckable(true); activateTBButton->setObjectName("closedPolygonButton"); connect(activateTBButton,SIGNAL(toggled(bool)),this,SLOT(clickClosePolygon(bool))); + connect(activateTBButton, &QAbstractButton::toggled, [=] (bool state) { explanation->setVisible(!state); }); interpolate = new QCheckBox(tr("Interpolate between contours")); interpolate->setToolTip("Interpolate between master ROIs"); From 45ab11cf7056941a0d567a184d4b9327d85d6b9d Mon Sep 17 00:00:00 2001 From: paulineMig Date: Fri, 19 Jan 2024 16:24:32 +0100 Subject: [PATCH 69/98] Assume 0 for missing origin --- src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index b34d4ac430..78b93a400f 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -687,6 +687,11 @@ double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) double DCMTKImageIO::GetPositionFromPrincipalAxisIndex(int index, int principalAxisIndex) { std::string s_position = this->GetMetaDataValueString("(0020,0032)", index); + if ( s_position == "" ) + { + itkWarningMacro ( << "Tag (0020,0032) (ImageOrigin) was not found, assuming 0.0/0.0/0.0" << std::endl); + return 0.0; + } // Convert string metadata to vector of double std::stringstream lineStream(s_position); From 4f373693ea08b51354014b984d7440e969a8535c Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 2 Oct 2023 10:55:49 +0200 Subject: [PATCH 70/98] [Paint] solve problems switching between tools (#770) * [Paint] solve problems switching between tools * Update medAlgorithmPaintToolBox.cpp : && instead of && * [Paint] separate condition for add and erase --- .../medAlgorithmPaintToolBox.cpp | 111 ++++++++++-------- .../medAlgorithmPaintToolBox.h | 4 +- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index bf14d02d1d..1af963f228 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -450,8 +450,8 @@ AlgorithmPaintToolBox::AlgorithmPaintToolBox(QWidget *parent ) : dataButtonsLayout->addWidget(m_clearMaskButton); layout->addLayout(dataButtonsLayout); - connect (m_strokeButton, SIGNAL(pressed()), this, SLOT(activateStroke ())); - connect (m_magicWandButton, SIGNAL(pressed()),this,SLOT(activateMagicWand())); + connect (m_strokeButton, SIGNAL(toggled(bool)), this, SLOT(activateStroke(bool))); + connect (m_magicWandButton, SIGNAL(toggled(bool)), this, SLOT(activateMagicWand(bool))); connect (m_clearMaskButton, SIGNAL(pressed()), this, SLOT(clear())); connect (m_applyButton, SIGNAL(pressed()),this, SLOT(import())); @@ -545,61 +545,63 @@ void AlgorithmPaintToolBox::updateMagicWandComputation() } } -void AlgorithmPaintToolBox::activateStroke() +void AlgorithmPaintToolBox::activateStroke(bool checked) { m_wandInfo->hide(); - if ( this->m_strokeButton->isChecked() ) + if (!checked) { deactivateCustomedCursor(); // Deactivate painting cursor this->m_viewFilter->removeFromAllViews(); m_paintState = (PaintState::None); updateButtons(); - return; } - setPaintState(PaintState::Stroke); - updateButtons(); - this->m_magicWandButton->setChecked(false); - addViewEventFilter(m_viewFilter); - addBrushSize_shortcut->setEnabled(true); - reduceBrushSize_shortcut->setEnabled(true); + else + { + this->m_magicWandButton->setChecked(false); + setPaintState(PaintState::Stroke); + updateButtons(); + addViewEventFilter(m_viewFilter); + addBrushSize_shortcut->setEnabled(true); + reduceBrushSize_shortcut->setEnabled(true); - setCurrentView(currentView); + setCurrentView(currentView); - if (!m_imageData) - { - this->setData(currentView->layerData(0)); - } - if (!m_imageData) - { - qWarning() << "Could not set data"; - return; - } + if (!m_imageData) + { + this->setData(currentView->layerData(0)); + } + if (!m_imageData) + { + qWarning() << "Could not set data"; + return; + } - if(!currentView->contains(m_maskAnnotationData)) - { - m_maskAnnotationData->setMetaData("SeriesDescription", "mask"); - currentView->addLayer(m_maskAnnotationData); - for(medAbstractInteractor* interactor : currentView->layerInteractors(getWorkspace()->getSelectedLayerIndices()[0])) + if(!currentView->contains(m_maskAnnotationData)) { - if (interactor->identifier() == "medAnnotationInteractor") + m_maskAnnotationData->setMetaData("SeriesDescription", "mask"); + currentView->addLayer(m_maskAnnotationData); + for(medAbstractInteractor* interactor : currentView->layerInteractors(getWorkspace()->getSelectedLayerIndices()[0])) { - for(medAbstractParameterL* parameter : interactor->linkableParameters()) + if (interactor->identifier() == "medAnnotationInteractor") { - if (parameter->name() == "Opacity") + for(medAbstractParameterL* parameter : interactor->linkableParameters()) { - qobject_cast(parameter)->setValue(0.4); + if (parameter->name() == "Opacity") + { + qobject_cast(parameter)->setValue(0.4); + } } } } + + // Update Mouse Interaction ToolBox + currentView->setCurrentLayer(0); + getWorkspace()->updateMouseInteractionToolBox(); } - // Update Mouse Interaction ToolBox - currentView->setCurrentLayer(0); - getWorkspace()->updateMouseInteractionToolBox(); + activateCustomedCursor(); // Add circular cursor for painting } - - activateCustomedCursor(); // Add circular cursor for painting } void AlgorithmPaintToolBox::activateCustomedCursor() @@ -656,21 +658,23 @@ void AlgorithmPaintToolBox::deactivateCustomedCursor() currentView->viewWidget()->setCursor(Qt::CrossCursor); } -void AlgorithmPaintToolBox::activateMagicWand() +void AlgorithmPaintToolBox::activateMagicWand(bool checked) { - if ( this->m_magicWandButton->isChecked() ) + if (!checked) { this->m_viewFilter->removeFromAllViews(); m_paintState = (PaintState::None); newSeed(); // accept the current growth updateButtons(); - return; } - setPaintState(PaintState::Wand); - updateButtons(); - this->m_strokeButton->setChecked(false); - addViewEventFilter(m_viewFilter); - deactivateCustomedCursor(); + else + { + this->m_strokeButton->setChecked(false); + setPaintState(PaintState::Wand); + updateButtons(); + addViewEventFilter(m_viewFilter); + deactivateCustomedCursor(); + } } void AlgorithmPaintToolBox::updateMagicWandComputationSpeed() @@ -830,6 +834,10 @@ void AlgorithmPaintToolBox::updateView() } } } + else + { + showButtons(false); + } } void AlgorithmPaintToolBox::setButtonsDisabled(bool disable) @@ -1484,7 +1492,6 @@ void AlgorithmPaintToolBox::updateButtons() m_strokeLabelSpinBox->hide(); m_colorLabel->hide(); m_removeSeedButton->hide(); - return; } else { @@ -1832,8 +1839,17 @@ void AlgorithmPaintToolBox::clearMask() m_redoStacks->remove(currentView); } - showButtons(false); resetToolbox(); + + if (m_addButton->isVisible()) + { + m_addButton->setEnabled(false); + } + + if (m_eraseButton->isVisible()) + { + m_eraseButton->setEnabled(false); + } } void AlgorithmPaintToolBox::resetToolbox() @@ -1842,15 +1858,10 @@ void AlgorithmPaintToolBox::resetToolbox() if ( this->m_strokeButton->isChecked() ) { - m_paintState = (PaintState::None); - updateButtons(); m_strokeButton->setChecked(false); } else if ( this->m_magicWandButton->isChecked() ) { - m_paintState = (PaintState::None); - newSeed(); // accept the current growth - updateButtons(); m_magicWandButton->setChecked(false); } diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.h b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.h index 34ace97e97..7ef6004733 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.h +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.h @@ -124,10 +124,10 @@ class MEDALGORITMPAINT_EXPORT AlgorithmPaintToolBox : public medAbstractSelectab public slots: void updateView(); - void activateStroke(); + void activateStroke(bool checked); void activateCustomedCursor(); void deactivateCustomedCursor(); - void activateMagicWand(); + void activateMagicWand(bool checked); void updateMagicWandComputationSpeed(); void copyMetaData(medAbstractData *output, From d05d0963e301ebe66df0fee66366cfe26e6fb8dc Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Thu, 28 Sep 2023 14:24:56 +0200 Subject: [PATCH 71/98] [Paint] allow to forceHide the Save button (#767) --- .../medAlgorithmPaint/medAlgorithmPaintToolBox.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp index 1af963f228..157412976d 100644 --- a/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp +++ b/src/plugins/legacy/medAlgorithmPaint/medAlgorithmPaintToolBox.cpp @@ -1466,16 +1466,10 @@ bool AlgorithmPaintToolBox::isMask2dOnSlice() void AlgorithmPaintToolBox::showButtons( bool value ) { - if (value) - { - m_applyButton->show(); - m_clearMaskButton->show(); - } - else - { - m_applyButton->hide(); - m_clearMaskButton->hide(); - } + bool forceHide = m_applyButton->property("forceHide").toBool(); + m_applyButton->setVisible(value && !forceHide); + + m_clearMaskButton->setVisible(value); } void AlgorithmPaintToolBox::updateButtons() From 47d6ea25d6508f9882caafb91c229d3cbf85879c Mon Sep 17 00:00:00 2001 From: jcastelneau Date: Tue, 26 Sep 2023 11:16:30 +0200 Subject: [PATCH 72/98] [Polygon] adapt to change of instance --- .../toolboxes/medAbstractSelectableToolBox.h | 2 +- .../legacy/polygonRoi/polygonLabel.cpp | 6 +++--- src/plugins/legacy/polygonRoi/polygonLabel.h | 2 +- .../toolboxes/defaultLabelToolBox.cpp | 3 +++ .../toolboxes/polygonRoiToolBox.cpp | 20 ++++++++++++++++--- .../polygonRoi/toolboxes/polygonRoiToolBox.h | 1 + .../polygonRoi/viewevent/baseViewEvent.cpp | 14 ++++++++----- .../polygonRoi/viewevent/baseViewEvent.h | 4 ++-- 8 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h index a997fa45de..b65bb4c633 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h @@ -14,7 +14,7 @@ #include #include -#include +#include class medAbstractData; class medSelectorToolBox; diff --git a/src/plugins/legacy/polygonRoi/polygonLabel.cpp b/src/plugins/legacy/polygonRoi/polygonLabel.cpp index a3d98f0936..a7dc6a467a 100644 --- a/src/plugins/legacy/polygonRoi/polygonLabel.cpp +++ b/src/plugins/legacy/polygonRoi/polygonLabel.cpp @@ -415,11 +415,11 @@ QVector polygonLabel::getContoursAsNodes() return contours; } -void polygonLabel::createMask(int label, QString &desc) +medAbstractData *polygonLabel::createMask(int label, QString &desc) { vtkImageView2D *view2d = getView2D(); if (!view2d) - return; + return nullptr; QList> polygons; for (polygonRoi *roi : d->rois) @@ -587,7 +587,7 @@ void polygonLabel::createMask(int label, QString &desc) output->setMetaData(medMetaDataKeys::Toolbox.key(), "PolygonROI"); output->setMetaData(medMetaDataKeys::OriginalDataUID.key(), inputData->metadata(medMetaDataKeys::SeriesInstanceUID.key())); output->setMetaData(medMetaDataKeys::OriginalDataDesc.key(), inputData->metadata(medMetaDataKeys::SeriesDescription.key())); - medDataManager::instance().importData(output, false); + return output; } void polygonLabel::SetMasterRoi() diff --git a/src/plugins/legacy/polygonRoi/polygonLabel.h b/src/plugins/legacy/polygonRoi/polygonLabel.h index 6bfa05112a..3fb64dc545 100644 --- a/src/plugins/legacy/polygonRoi/polygonLabel.h +++ b/src/plugins/legacy/polygonRoi/polygonLabel.h @@ -60,7 +60,7 @@ class POLYGONROIPLUGIN_EXPORT polygonLabel : public QObject void deleteNode(double X, double Y); void deleteContour(); void removeAllTick(); - void createMask(int label, QString &desc); + medAbstractData *createMask(int label, QString &desc); void SetMasterRoi(); vtkSmartPointer getContoursAsPolyData(int label); diff --git a/src/plugins/legacy/polygonRoi/toolboxes/defaultLabelToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/defaultLabelToolBox.cpp index 0edccdfc66..2875f91def 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/defaultLabelToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/defaultLabelToolBox.cpp @@ -32,12 +32,15 @@ defaultLabelToolBox::defaultLabelToolBox(QWidget *parent): labels = new QListWidget; labels->setSelectionMode(QAbstractItemView::SingleSelection); labels->setContentsMargins(0,0,0,0); + labels->setObjectName("labels"); plusButton = new QPushButton(QIcon(":/pixmaps/plus.png"), ""); plusButton->setMaximumSize(QSize(20,20)); + plusButton->setObjectName("plusBttn"); minusButton = new QPushButton(QIcon(":/pixmaps/minus.png"), ""); minusButton->setMaximumSize(QSize(20,20)); + minusButton->setObjectName("minBttn"); auto listLabelLayout = new QVBoxLayout(); layout->addLayout(listLabelLayout); diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index 6f47bd0957..f6832620db 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -27,6 +27,7 @@ #include #include #include +#include const char *polygonRoiToolBox::generateBinaryImageButtonName = "generateBinaryImageButton"; @@ -96,6 +97,7 @@ polygonRoiToolBox::polygonRoiToolBox(QWidget *parent ) : contoursActionLayout->addLayout(repulsorLayout); saveLabel = new QLabel("Save segmentations as:"); + saveLabel->setObjectName("saveLabel"); auto saveButtonsLayout = new QHBoxLayout(); saveBinaryMaskButton = new QPushButton(tr("Mask(s)")); saveBinaryMaskButton->setToolTip("Import the current mask to the non persistent database"); @@ -165,6 +167,17 @@ medAbstractData *polygonRoiToolBox::processOutput() return processOutputs().value(0); } +QList > polygonRoiToolBox::processOutputs() +{ + QList > outputMasks; + for (baseViewEvent *event1 : viewEventHash.values()) + { + auto masks = event1->saveMasks(); + outputMasks.append(masks); + } + return outputMasks; +} + void polygonRoiToolBox::updateView() { medTabbedViewContainers *tabs = this->getWorkspace()->tabbedViewContainers(); @@ -427,9 +440,10 @@ void polygonRoiToolBox::interpolateCurve(bool state) void polygonRoiToolBox::saveBinaryImage() { - for (baseViewEvent *event1 : viewEventHash.values()) + QList> outputMasks = processOutputs(); + for (auto output : outputMasks) { - event1->saveMask(); + medDataManager::instance().importData(output, false); } } @@ -621,4 +635,4 @@ void polygonRoiToolBox::showHelp() const const QString explanation = main + shortcut + contextual; msgBox.setText(explanation); msgBox.exec(); -} \ No newline at end of file +} diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.h b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.h index 2dbafb8750..06f409013e 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.h +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.h @@ -54,6 +54,7 @@ MED_TOOLBOX_INTERFACE("Polygon Roi", static bool registered(); dtkPlugin* plugin() override; medAbstractData *processOutput() override; + QList > processOutputs() override; void drawCross(double *position); void eraseCross(); diff --git a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp index 789b1c084e..e45a6e55d2 100644 --- a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp +++ b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.cpp @@ -457,7 +457,7 @@ bool baseViewEvent::rightButtonBehaviour(medAbstractView *view, QMouseEvent *mou auto saveMaskAction = new QAction("Mask", saveMenu); connect(saveMaskAction, &QAction::triggered, [=](){ - saveMask(closestManager); + medDataManager::instance().importData(saveMask(closestManager), false); }); saveMenu->addAction(saveMaskAction); @@ -710,15 +710,17 @@ void baseViewEvent::activateRepulsor(bool state) } } -void baseViewEvent::saveMask() +QList> baseViewEvent::saveMasks() { + QList> outputMasks; for (polygonLabel *label : labelList) { if (!label->getRois().empty()) { - saveMask(label); + outputMasks.append(saveMask(label)); } } + return outputMasks; } void baseViewEvent::saveAllContours() @@ -952,11 +954,13 @@ bool baseViewEvent::isActiveContourInSlice() return retVal; } -void baseViewEvent::saveMask(polygonLabel *manager) + dtkSmartPointer baseViewEvent::saveMask(polygonLabel *manager) { int label = labelList.indexOf(manager); QString desc = createMaskDescription(manager); - manager->createMask(label, desc); + auto outputMask = manager->createMask(label, desc); + outputMask->setMetaData("LabelName", manager->getName()); + return outputMask; } QString baseViewEvent::createMaskDescription(polygonLabel *label) diff --git a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h index b023d44cb8..df75fff95b 100644 --- a/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h +++ b/src/plugins/legacy/polygonRoi/viewevent/baseViewEvent.h @@ -57,7 +57,7 @@ class baseViewEvent : public medViewEventFilter void updatePosition(QString name, int position); void setEnableInterpolation(bool state); void activateRepulsor(bool state); - void saveMask(); + QList > saveMasks(); void saveAllContours(); void removeViewFromList(medAbstractImageView *iView); @@ -91,7 +91,7 @@ protected slots: private slots: void deleteNode(polygonLabel *manager, const double *mousePos); void deleteContour(polygonLabel *manager); - void saveMask(polygonLabel *manager); + dtkSmartPointer saveMask(polygonLabel *manager); void saveContour(polygonLabel *label); void copyContour(polygonLabel *manager); From a5d34cc1ce5f09bb835b2ec0b17c3f5122361ce8 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Mon, 5 Feb 2024 12:04:28 +0100 Subject: [PATCH 73/98] [PolygonROI] pipeline adaptation - put back extension to include --- .../medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h index b65bb4c633..a997fa45de 100644 --- a/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h +++ b/src/layers/legacy/medCoreLegacy/gui/toolboxes/medAbstractSelectableToolBox.h @@ -14,7 +14,7 @@ #include #include -#include +#include class medAbstractData; class medSelectorToolBox; From c7aa75ae8aa120775a1c13cf02c7fce0f21db1a0 Mon Sep 17 00:00:00 2001 From: fcollot Date: Mon, 4 Sep 2023 09:57:23 +0200 Subject: [PATCH 74/98] Compile VTK Python wrappings (with pyncpp's Python) --- superbuild/CMakeLists.txt | 14 +-- superbuild/patches/VTK.patch | 134 +++++++++++++++++++++++--- superbuild/projects_modules/VTK.cmake | 22 +++++ 3 files changed, 147 insertions(+), 23 deletions(-) diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index cfd289c714..9ee6b6bc1f 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -65,7 +65,13 @@ if (NOT WIN32) list(APPEND external_projects ffmpeg) endif() endif() - + +if (USE_Python) + list(APPEND external_projects + pyncpp + ) +endif() + list(APPEND external_projects VTK ITK @@ -83,12 +89,6 @@ if (USE_DTKIMAGING) ) endif() -if (USE_Python) - list(APPEND external_projects - pyncpp - ) -endif() - list(APPEND external_projects medInria ) diff --git a/superbuild/patches/VTK.patch b/superbuild/patches/VTK.patch index ab16ead65b..2ba761e78c 100644 --- a/superbuild/patches/VTK.patch +++ b/superbuild/patches/VTK.patch @@ -1,16 +1,3 @@ -From e048bf336f8c2efc1e6b6e57c5fc899b2e6f6402 Mon Sep 17 00:00:00 2001 -From: MERLE Mathilde -Date: Tue, 20 Sep 2022 16:11:42 +0200 -Subject: [PATCH] VTK - ---- - CMake/VTKGenerateExportHeader.cmake | 6 +++++- - IO/Movie/module.cmake | 1 + - IO/Movie/vtkOggTheoraWriter.cxx | 4 +++- - Rendering/Qt/vtkQtLabelRenderStrategy.cxx | 1 + - Rendering/Qt/vtkQtStringToImage.cxx | 1 + - 5 files changed, 11 insertions(+), 2 deletions(-) - diff --git a/CMake/VTKGenerateExportHeader.cmake b/CMake/VTKGenerateExportHeader.cmake index 9a7a76386e..f71969ae54 100644 --- a/CMake/VTKGenerateExportHeader.cmake @@ -86,6 +73,121 @@ index 549ffbe874..a7c726e4f9 100644 #include #include #include --- -2.25.1 - +diff --git a/Wrapping/PythonCore/PyVTKMethodDescriptor.cxx b/Wrapping/PythonCore/PyVTKMethodDescriptor.cxx +index 2b0d443537..e18525a470 100644 +--- a/Wrapping/PythonCore/PyVTKMethodDescriptor.cxx ++++ b/Wrapping/PythonCore/PyVTKMethodDescriptor.cxx +@@ -186,7 +186,7 @@ PyTypeObject PyVTKMethodDescriptor_Type = { + sizeof(PyMethodDescrObject), // tp_basicsize + 0, // tp_itemsize + PyVTKMethodDescriptor_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +diff --git a/Wrapping/PythonCore/PyVTKNamespace.cxx b/Wrapping/PythonCore/PyVTKNamespace.cxx +index 71ee2a3516..1c5f85c3d4 100644 +--- a/Wrapping/PythonCore/PyVTKNamespace.cxx ++++ b/Wrapping/PythonCore/PyVTKNamespace.cxx +@@ -49,7 +49,7 @@ PyTypeObject PyVTKNamespace_Type = { + 0, // tp_basicsize + 0, // tp_itemsize + PyVTKNamespace_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +diff --git a/Wrapping/PythonCore/PyVTKReference.cxx b/Wrapping/PythonCore/PyVTKReference.cxx +index 943ac71080..6f3e0130a8 100644 +--- a/Wrapping/PythonCore/PyVTKReference.cxx ++++ b/Wrapping/PythonCore/PyVTKReference.cxx +@@ -1010,7 +1010,7 @@ PyTypeObject PyVTKReference_Type = { + sizeof(PyVTKReference), // tp_basicsize + 0, // tp_itemsize + PyVTKReference_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +@@ -1067,7 +1067,7 @@ PyTypeObject PyVTKNumberReference_Type = { + sizeof(PyVTKReference), // tp_basicsize + 0, // tp_itemsize + PyVTKReference_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +@@ -1124,7 +1124,7 @@ PyTypeObject PyVTKStringReference_Type = { + sizeof(PyVTKReference), // tp_basicsize + 0, // tp_itemsize + PyVTKReference_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +@@ -1181,7 +1181,7 @@ PyTypeObject PyVTKTupleReference_Type = { + sizeof(PyVTKReference), // tp_basicsize + 0, // tp_itemsize + PyVTKReference_Delete, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +diff --git a/Wrapping/PythonCore/PyVTKTemplate.cxx b/Wrapping/PythonCore/PyVTKTemplate.cxx +index be200985b3..ebc236ad5f 100644 +--- a/Wrapping/PythonCore/PyVTKTemplate.cxx ++++ b/Wrapping/PythonCore/PyVTKTemplate.cxx +@@ -268,7 +268,7 @@ PyTypeObject PyVTKTemplate_Type = { + 0, // tp_basicsize + 0, // tp_itemsize + nullptr, // tp_dealloc +- nullptr, // tp_print ++ 0, // tp_print + nullptr, // tp_getattr + nullptr, // tp_setattr + nullptr, // tp_compare +diff --git a/Wrapping/Tools/vtkWrapPythonClass.c b/Wrapping/Tools/vtkWrapPythonClass.c +index 989101b2ee..9a8b465acd 100644 +--- a/Wrapping/Tools/vtkWrapPythonClass.c ++++ b/Wrapping/Tools/vtkWrapPythonClass.c +@@ -527,7 +527,7 @@ void vtkWrapPython_GenerateObjectType( + " sizeof(PyVTKObject), // tp_basicsize\n" + " 0, // tp_itemsize\n" + " PyVTKObject_Delete, // tp_dealloc\n" +- " nullptr, // tp_print\n" ++ " 0, // tp_print\n" + " nullptr, // tp_getattr\n" + " nullptr, // tp_setattr\n" + " nullptr, // tp_compare\n" +diff --git a/Wrapping/Tools/vtkWrapPythonEnum.c b/Wrapping/Tools/vtkWrapPythonEnum.c +index b933702242..57c077490a 100644 +--- a/Wrapping/Tools/vtkWrapPythonEnum.c ++++ b/Wrapping/Tools/vtkWrapPythonEnum.c +@@ -145,7 +145,7 @@ void vtkWrapPython_GenerateEnumType( + " sizeof(PyIntObject), // tp_basicsize\n" + " 0, // tp_itemsize\n" + " nullptr, // tp_dealloc\n" +- " nullptr, // tp_print\n" ++ " 0, // tp_print\n" + " nullptr, // tp_getattr\n" + " nullptr, // tp_setattr\n" + " nullptr, // tp_compare\n" +diff --git a/Wrapping/Tools/vtkWrapPythonType.c b/Wrapping/Tools/vtkWrapPythonType.c +index 744cb1b9d3..f6361bcb26 100644 +--- a/Wrapping/Tools/vtkWrapPythonType.c ++++ b/Wrapping/Tools/vtkWrapPythonType.c +@@ -709,7 +709,7 @@ void vtkWrapPython_GenerateSpecialType( + " sizeof(PyVTKSpecialObject), // tp_basicsize\n" + " 0, // tp_itemsize\n" + " Py%s_Delete, // tp_dealloc\n" +- " nullptr, // tp_print\n" ++ " 0, // tp_print\n" + " nullptr, // tp_getattr\n" + " nullptr, // tp_setattr\n" + " nullptr, // tp_compare\n" diff --git a/superbuild/projects_modules/VTK.cmake b/superbuild/projects_modules/VTK.cmake index e0e547a37d..271beb842b 100644 --- a/superbuild/projects_modules/VTK.cmake +++ b/superbuild/projects_modules/VTK.cmake @@ -21,6 +21,10 @@ set(ep VTK) if(${USE_FFmpeg}) list(APPEND ${ep}_dependencies ffmpeg) endif() + +if(USE_Python) + list(APPEND ${ep}_dependencies pyncpp) +endif() ## ############################################################################# ## Prepare the project @@ -113,6 +117,24 @@ if(${USE_FFmpeg}) ) endif() +if(USE_Python) + set(python_version "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + if(UNIX) + set(python_executable "${pyncpp_DIR}/lib/python${python_version}/bin/python${python_version}") + set(python_include "${pyncpp_DIR}/lib/python${python_version}/include/python${python_version}") + set(python_library "${pyncpp_DIR}/lib/python${python_version}/lib/libpython${python_version}${CMAKE_SHARED_LIBRARY_SUFFIX}") + else() + # TODO + endif() + list(APPEND cmake_args + -DVTK_WRAP_PYTHON:BOOL=ON + -DVTK_PYTHON_VERSION:STRING=${python_version} + -DPYTHON_EXECUTABLE:PATH=${python_executable} + -DPYTHON_INCLUDE_DIR:PATH=${python_include} + -DPYTHON_LIBRARY:PATH=${python_library} + ) +endif() + ## ############################################################################# ## Check if patch has to be applied ## ############################################################################# From 9ccc67a08669f5307cf506b3c563f136e96eee92 Mon Sep 17 00:00:00 2001 From: fcollot Date: Mon, 11 Sep 2023 09:07:58 +0200 Subject: [PATCH 75/98] Add python plugin path (#763) --- src/app/medInria/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index 21d9a629b6..8099143094 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -162,6 +162,7 @@ int main(int argc,char* argv[]) #if(USE_PYTHON) pyncpp::Manager pythonManager; QDir pythonHome = qApp->applicationDirPath(); + QDir pythonPluginPath = pythonHome; QString pythonErrorMessage; if (!pythonHome.cd(PYTHON_HOME)) @@ -174,6 +175,21 @@ int main(int argc,char* argv[]) { pythonErrorMessage = "Initialization of the embedded Python failed."; } + else + { +#ifdef Q_OS_MACOS + if(!pythonPluginPath.cd("../Plugins/python")) + { + pythonPluginPath.cd("../../../plugins/python"); + } +#else +#ifdef Q_OS_LINUX + pythonPluginPath.cd("../plugins/python"); +#endif +#endif + pyncpp::Module sysModule = pyncpp::Module::import("sys"); + sysModule.attribute("path").append(pyncpp::Object(pythonPluginPath.absolutePath())); + } } #endif From c9a2535b91e6332c20230873484dc60a4f2a78e9 Mon Sep 17 00:00:00 2001 From: fcollot Date: Thu, 28 Sep 2023 09:27:24 +0200 Subject: [PATCH 76/98] [instances] merge --- .../database/medDatabaseImporter.cpp | 24 ++-- .../medDatabasePersistentController.cpp | 110 +++++++++++------- .../medDatabasePersistentController.h | 10 +- .../database/medDatabaseReader.cpp | 19 ++- .../database/medDatabaseRemover.cpp | 55 ++++++--- .../database/medLocalDbController.cpp | 109 ++++++++++++----- .../database/medLocalDbController.h | 16 ++- 7 files changed, 237 insertions(+), 106 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp index 57be53140b..8250a41701 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp @@ -52,11 +52,14 @@ QString medDatabaseImporter::getPatientID(QString patientName, QString birthDate QString patientID = ""; //Let's see if the patient is already in the db - QSqlQuery query ( medDataManager::instance().controller()->database() ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare ( "SELECT patientId FROM patient WHERE name = :name AND birthdate = :birthdate" ); query.bindValue ( ":name", patientName ); query.bindValue ( ":birthdate", birthDate ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + if ( !query.exec() ) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -80,20 +83,20 @@ QString medDatabaseImporter::getPatientID(QString patientName, QString birthDate medDataIndex medDatabaseImporter::populateDatabaseAndGenerateThumbnails ( medAbstractData* medData, QString pathToStoreThumbnail ) { - QSqlDatabase db = medDataManager::instance().controller()->database(); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); generateThumbnail ( medData, pathToStoreThumbnail ); - int patientDbId = getOrCreatePatient ( medData, db ); + int patientDbId = getOrCreatePatient ( medData, dbConnection ); - int studyDbId = getOrCreateStudy ( medData, db, patientDbId ); + int studyDbId = getOrCreateStudy ( medData, dbConnection, patientDbId ); // Update name of the series if a permanent data has this name already QString seriesName = medData->metadata(medMetaDataKeys::SeriesDescription.key()); QString newSeriesName = ensureUniqueSeriesName(seriesName, QString::number(studyDbId)); medData->setMetaData(medMetaDataKeys::SeriesDescription.key(), newSeriesName); - int seriesDbId = getOrCreateSeries ( medData, db, studyDbId ); + int seriesDbId = getOrCreateSeries ( medData, dbConnection, studyDbId ); medDataIndex index = medDataIndex ( medDataManager::instance().controller()->dataSourceId() , patientDbId, studyDbId, seriesDbId ); return index; @@ -118,6 +121,8 @@ int medDatabaseImporter::getOrCreatePatient ( const medAbstractData* medData, QS query.bindValue ( ":name", patientName ); query.bindValue ( ":birthdate", birthDate ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + if ( !query.exec() ) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -174,6 +179,8 @@ int medDatabaseImporter::getOrCreateStudy ( const medAbstractData* medData, QSql query.bindValue ( ":studyName", studyName ); query.bindValue ( ":studyUid", studyUid ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + if ( !query.exec() ) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -243,6 +250,8 @@ int medDatabaseImporter::getOrCreateSeries ( const medAbstractData* medData, QSq if( seriesName=="EmptySeries" ) return seriesDbId; + QMutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + if ( !query.exec() ) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -373,8 +382,9 @@ QString medDatabaseImporter::ensureUniqueSeriesName(const QString seriesName, co void medDatabaseImporter::createDBEntryForMetadataAttachedFile(medAbstractData *medData, int seriesDbId) { - QSqlDatabase db = medDataManager::instance().controller()->database(); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query (dbConnection); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); query.exec("SELECT COUNT(*) as cpt FROM pragma_table_info('series') WHERE name='json_meta_path'"); bool jsonColExist = false; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp index d41ca26e3e..c4085971af 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp @@ -26,7 +26,6 @@ class medDatabasePersistentControllerPrivate { public: void buildMetaDataLookup(); - bool isConnected; struct TableEntry { TableEntry(QString t, QString c, bool isPath_ = false) @@ -218,8 +217,9 @@ bool medDatabasePersistentController::moveDatabase(QString newLocation) // close connection if necessary if (this->isConnected()) { - this->closeConnection(); + getMainConnection().close(); } + // now update the datastorage path and make sure to reconnect medStorage::setDataLocation(newLocation); @@ -290,11 +290,6 @@ void medDatabasePersistentController::removeAll() qWarning() << "Attempt to remove all item from PERSISTENT dataBase"; } -const QSqlDatabase &medDatabasePersistentController::database(void) const -{ - return m_database; -} - /** create dataIndices out of partial ids */ medDataIndex medDatabasePersistentController::indexForPatient(int id) { @@ -307,12 +302,15 @@ medDataIndex medDatabasePersistentController::indexForPatient(int id) medDataIndex medDatabasePersistentController::indexForPatient( const QString &patientName) { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QVariant patientId = -1; query.prepare("SELECT id FROM patient WHERE name = :name"); query.bindValue(":name", patientName); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -330,13 +328,16 @@ medDataIndex medDatabasePersistentController::indexForPatient( medDataIndex medDatabasePersistentController::indexForStudy(int id) { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QVariant patientId = -1; query.prepare("SELECT patient FROM study WHERE id = :id"); query.bindValue(":id", id); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -352,11 +353,13 @@ medDataIndex medDatabasePersistentController::indexForStudy(int id) medDataIndex medDatabasePersistentController::indexForStudy(const QString &patientName, const QString &studyName) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + medDataIndex index = this->indexForPatient(patientName); if (!index.isValid()) return index; - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); QVariant patientId = index.patientId(); QVariant studyId = -1; @@ -365,6 +368,8 @@ medDataIndex medDatabasePersistentController::indexForStudy(const QString &patie query.bindValue(":id", patientId); query.bindValue(":name", studyName); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -383,11 +388,13 @@ medDataIndex medDatabasePersistentController::indexForStudy(const QString &patie medDataIndex medDatabasePersistentController::indexForStudyUID(const QString &patientName, const QString &studyInstanceUID) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + medDataIndex index = this->indexForPatient(patientName); if (!index.isValid()) return index; - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); QVariant patientId = index.patientId(); QVariant studyId = -1; @@ -396,6 +403,8 @@ medDataIndex medDatabasePersistentController::indexForStudyUID(const QString &pa query.bindValue(":id", patientId); query.bindValue(":uid", studyInstanceUID); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -413,7 +422,8 @@ medDataIndex medDatabasePersistentController::indexForStudyUID(const QString &pa medDataIndex medDatabasePersistentController::indexForSeries(int id) { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QVariant patientId = -1; QVariant studyId = -1; @@ -421,6 +431,8 @@ medDataIndex medDatabasePersistentController::indexForSeries(int id) query.prepare("SELECT study FROM series WHERE id = :id"); query.bindValue(":id", id); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -448,11 +460,13 @@ medDataIndex medDatabasePersistentController::indexForSeries( const QString &patientName, const QString &studyName, const QString &seriesName) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + medDataIndex index = this->indexForStudy(patientName, studyName); if (!index.isValid()) return index; - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); QVariant studyId = index.studyId(); @@ -460,6 +474,8 @@ medDataIndex medDatabasePersistentController::indexForSeries( query.bindValue(":id", studyId); query.bindValue(":name", seriesName); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -479,11 +495,13 @@ medDataIndex medDatabasePersistentController::indexForSeriesUID( const QString &patientName, const QString &studyInstanceUID, const QString &seriesInstanceUID) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + medDataIndex index = this->indexForStudyUID(patientName, studyInstanceUID); if (!index.isValid()) return index; - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); QVariant studyId = index.studyId(); @@ -491,6 +509,8 @@ medDataIndex medDatabasePersistentController::indexForSeriesUID( query.bindValue(":id", studyId); query.bindValue(":uid", seriesInstanceUID); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -505,25 +525,11 @@ medDataIndex medDatabasePersistentController::indexForSeriesUID( return medDataIndex(); } -/** - * Status of connection - * @return bool true on success - */ -bool medDatabasePersistentController::isConnected() const -{ - return d->isConnected; -} - -void medDatabasePersistentController::setConnected(bool flag) -{ - d->isConnected = flag; -} medDatabasePersistentController::medDatabasePersistentController() : d(new medDatabasePersistentControllerPrivate) { d->buildMetaDataLookup(); - d->isConnected = false; } medDatabasePersistentController::~medDatabasePersistentController() @@ -541,18 +547,21 @@ medDatabasePersistentController::~medDatabasePersistentController() QList medDatabasePersistentController::moveStudy( const medDataIndex &indexStudy, const medDataIndex &toPatient) { - QSqlQuery query(m_database); - bool result = false; QList newIndexList; medDataIndex newIndex; if (indexStudy.isValidForStudy() && toPatient.isValidForPatient()) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + query.prepare("UPDATE study SET patient=:patientId WHERE id=:studyId"); query.bindValue(":patientId", toPatient.patientId()); query.bindValue(":studyId", indexStudy.studyId()); + QMutexLocker mutexLocker(&getDatabaseMutex()); + result = execQuery(query, __FILE__, __LINE__); if (result) @@ -592,17 +601,20 @@ QList medDatabasePersistentController::moveStudy( medDataIndex medDatabasePersistentController::moveSeries( const medDataIndex &indexSeries, const medDataIndex &toStudy) { - QSqlQuery query(m_database); - bool result = false; medDataIndex newIndex; if (indexSeries.isValidForSeries() && toStudy.isValidForStudy()) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + query.prepare("UPDATE series SET study=:studyId WHERE id=:seriesId"); query.bindValue(":studyId", toStudy.studyId()); query.bindValue(":seriesId", indexSeries.seriesId()); + QMutexLocker mutexLocker(&getDatabaseMutex()); + result = execQuery(query, __FILE__, __LINE__); if (result) @@ -627,8 +639,6 @@ QString medDatabasePersistentController::metaData(const medDataIndex &index, typedef medDatabasePersistentControllerPrivate::MetaDataMap MetaDataMap; typedef medDatabasePersistentControllerPrivate::TableEntryList TableEntryList; - QSqlQuery query(m_database); - // Attempt to translate the desired metadata into a table / column entry. MetaDataMap::const_iterator it(d->metaDataLookup.find(key)); if (it == d->metaDataLookup.end()) @@ -661,9 +671,13 @@ QString medDatabasePersistentController::metaData(const medDataIndex &index, } if (id != -1) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + query.prepare("SELECT " + columnName + " FROM " + tableName + " WHERE id = :id"); query.bindValue(":id", id); + QMutexLocker mutexLocker(&getDatabaseMutex()); execQuery(query, __FILE__, __LINE__); if (query.next()) { @@ -687,8 +701,6 @@ bool medDatabasePersistentController::setMetaData(const medDataIndex &index, typedef medDatabasePersistentControllerPrivate::MetaDataMap MetaDataMap; typedef medDatabasePersistentControllerPrivate::TableEntryList TableEntryList; - QSqlQuery query(m_database); - // Attempt to translate the desired metadata into a table / column entry. MetaDataMap::const_iterator it(d->metaDataLookup.find(key)); if (it == d->metaDataLookup.end()) @@ -720,11 +732,15 @@ bool medDatabasePersistentController::setMetaData(const medDataIndex &index, } if (id != -1) { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + query.prepare(QString("UPDATE %1 SET %2 = :value WHERE id = :id") .arg(tableName) .arg(columnName)); query.bindValue(":value", value); query.bindValue(":id", id); + QMutexLocker mutexLocker(&getDatabaseMutex()); success = execQuery(query, __FILE__, __LINE__); if (success) { @@ -740,6 +756,7 @@ bool medDatabasePersistentController::setMetaData(const medDataIndex &index, QList medDatabasePersistentController::studies( const medDataIndex &index) const { + QSqlDatabase dbConnection = getThreadSpecificConnection(); QList ret; if (!index.isValidForPatient()) @@ -748,9 +765,10 @@ QList medDatabasePersistentController::studies( return ret; } - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); query.prepare("SELECT id FROM study WHERE patient = :patientId"); query.bindValue(":patientId", index.patientId()); + QMutexLocker mutexLocker(&getDatabaseMutex()); execQuery(query, __FILE__, __LINE__); #if QT_VERSION > 0x0406FF ret.reserve(query.size()); @@ -767,6 +785,7 @@ QList medDatabasePersistentController::studies( QList medDatabasePersistentController::series( const medDataIndex &index) const { + QSqlDatabase dbConnection = getThreadSpecificConnection(); QList ret; if (!index.isValidForStudy()) @@ -775,9 +794,10 @@ QList medDatabasePersistentController::series( return ret; } - QSqlQuery query(m_database); + QSqlQuery query(dbConnection); query.prepare("SELECT id FROM series WHERE study = :studyId"); query.bindValue(":studyId", index.studyId()); + QMutexLocker mutexLocker(&getDatabaseMutex()); execQuery(query, __FILE__, __LINE__); #if QT_VERSION > 0x0406FF ret.reserve(query.size()); @@ -793,7 +813,8 @@ QList medDatabasePersistentController::series( QStringList medDatabasePersistentController::series(const QString &seriesName, const QString &studyId) const { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); if (studyId.isEmpty()) { query.prepare ( "SELECT name FROM series WHERE name LIKE '" + seriesName + "%'"); @@ -804,6 +825,7 @@ QStringList medDatabasePersistentController::series(const QString &seriesName, c query.bindValue ( ":studyId", studyId ); } + QMutexLocker mutexLocker(&getDatabaseMutex()); execQuery(query, __FILE__, __LINE__); QStringList seriesNames; while (query.next()) @@ -839,6 +861,7 @@ QString medDatabasePersistentController::stringForPath(const QString &name) cons bool medDatabasePersistentController::contains(const medDataIndex &index) const { + if (index.isValid() && index.dataSourceId() == dataSourceId()) { // index is valid and comes from this dataSource @@ -846,7 +869,8 @@ bool medDatabasePersistentController::contains(const medDataIndex &index) const QVariant studyId = index.studyId(); QVariant seriesId = index.seriesId(); - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QString fromRequest = "SELECT * FROM patient"; QString whereRequest = " WHERE patient.id = :id"; @@ -869,6 +893,8 @@ bool medDatabasePersistentController::contains(const medDataIndex &index) const if (seriesId != -1) query.bindValue(":seID", seriesId); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; @@ -885,10 +911,12 @@ QString medDatabasePersistentController::attachedMetadataFileExists(const medDat if (contains(index)) { QVariant seriesId = index.seriesId(); - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QString request = "SELECT json_meta_path as json_path FROM series WHERE series.id = :id"; query.prepare(request); query.bindValue(":id", seriesId); + QMutexLocker mutexLocker(&getDatabaseMutex()); if (!execQuery(query, __FILE__, __LINE__)) { qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h index b6ddc78250..dcda21f56d 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h @@ -33,10 +33,10 @@ class MEDCORELEGACY_EXPORT medDatabasePersistentController : public medAbstractD public: ~medDatabasePersistentController(); - const QSqlDatabase &database() const; - bool createConnection() = 0; - virtual bool closeConnection() = 0; + virtual QSqlDatabase getMainConnection() const = 0; + virtual QSqlDatabase getThreadSpecificConnection() const = 0; + virtual QMutex& getDatabaseMutex() const = 0; medDataIndex indexForPatient(int id); medDataIndex indexForStudy(int id); @@ -58,8 +58,6 @@ class MEDCORELEGACY_EXPORT medDatabasePersistentController : public medAbstractD QString stringForPath(const QString &name) const; - bool isConnected() const override; - QList patients() const = 0; QList studies(const medDataIndex &index) const override; QList series(const medDataIndex &index) const override; @@ -82,7 +80,6 @@ class MEDCORELEGACY_EXPORT medDatabasePersistentController : public medAbstractD QString attachedMetadataFileExists(const medDataIndex &index) override; protected: - void setConnected(bool flag); void reset(); public slots: @@ -109,7 +106,6 @@ protected slots: protected: medDatabasePersistentController(); - QSqlDatabase m_database; private: medDatabasePersistentControllerPrivate *d; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp index 9eca4a1679..39f970efa8 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseReader.cpp @@ -63,7 +63,8 @@ medAbstractData* medDatabaseReader::run() QVariant studyDbId = d->index.studyId(); QVariant seriesDbId = d->index.seriesId(); - QSqlQuery query(medDataManager::instance().controller()->database()); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QString patientName, birthdate, gender, patientId; QString studyName, studyUid, studyId, studyTime, studyDate; @@ -76,13 +77,20 @@ medAbstractData* medDatabaseReader::run() query.prepare ( "SELECT name, birthdate, gender, patientId FROM patient WHERE id = :id" ); query.bindValue ( ":id", patientDbId ); + + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + if ( !query.exec() ) { + query.finish(); + mutexLocker.unlock(); FAILURE(query.lastError()); } if (! query.first() ) { + query.finish(); + mutexLocker.unlock(); FAILURE("No record in patient table for id:" + patientDbId.toString()); } @@ -100,6 +108,8 @@ medAbstractData* medDatabaseReader::run() if (! query.first() ) { + query.finish(); + mutexLocker.unlock(); FAILURE("No record in study table for id:" + studyDbId.toString()); } @@ -119,11 +129,15 @@ medAbstractData* medDatabaseReader::run() if ( !query.exec() ) { + query.finish(); + mutexLocker.unlock(); FAILURE(query.lastError()); } if ( ! query.first()) { + query.finish(); + mutexLocker.unlock(); FAILURE("No record in series table for id:" + seriesDbId.toString()); } @@ -162,6 +176,9 @@ medAbstractData* medDatabaseReader::run() seriesDate = query.value ( 29 ).toString(); kvp = query.value ( 30 ).toString(); + query.finish(); + mutexLocker.unlock(); + QStringList filePaths = seriesPath.split(';', QString::SkipEmptyParts); for(int i = 0 ; i < filePaths.size(); i++) { diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp index a8f39203c3..34e0850d33 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseRemover.cpp @@ -25,7 +25,6 @@ class medDatabaseRemoverPrivate { public: medDataIndex index; - QSqlDatabase db; static const QString T_PATIENT; static const QString T_STUDY; static const QString T_SERIES; @@ -40,7 +39,6 @@ const QString medDatabaseRemoverPrivate::T_SERIES = "series"; medDatabaseRemover::medDatabaseRemover ( const medDataIndex &index_ ) : medJobItemL(), d ( new medDatabaseRemoverPrivate ) { d->index = index_; - d->db = medDataManager::instance().controller()->database(); d->isCancelled = false; } @@ -52,8 +50,8 @@ medDatabaseRemover::~medDatabaseRemover() void medDatabaseRemover::internalRun() { - QSqlDatabase db( d->db ); - QSqlQuery ptQuery ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery ptQuery(dbConnection); // Is Patient const medDataIndex index = d->index; @@ -67,6 +65,7 @@ void medDatabaseRemover::internalRun() ptQuery.prepare ( "SELECT id FROM " + d->T_PATIENT ); } + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(ptQuery); while ( ptQuery.next() ) { @@ -74,7 +73,7 @@ void medDatabaseRemover::internalRun() break; int patientDbId = ptQuery.value ( 0 ).toInt(); - QSqlQuery stQuery ( db ); + QSqlQuery stQuery(dbConnection); // Is Study if ( index.isValidForStudy() ) @@ -95,7 +94,7 @@ void medDatabaseRemover::internalRun() break; int studyDbId = stQuery.value ( 0 ).toInt(); - QSqlQuery seQuery ( db ); + QSqlQuery seQuery(dbConnection); // Is Series if ( index.isValidForSeries() ) @@ -139,6 +138,10 @@ void medDatabaseRemover::internalRun() } } // ptQuery.next + + ptQuery.finish(); + mutexLocker.unlock(); + emit progress (this, 100 ); if ( d->isCancelled ) @@ -151,8 +154,9 @@ void medDatabaseRemover::internalRun() void medDatabaseRemover::removeSeries ( int patientDbId, int studyDbId, int seriesDbId ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); + QSqlQuery query(dbConnection); query.exec("SELECT COUNT(*) as cpt FROM pragma_table_info('series') WHERE name='json_meta_path'"); bool jsonColExist = false; if (query.next()) @@ -193,29 +197,34 @@ void medDatabaseRemover::removeSeries ( int patientDbId, int studyDbId, int seri removeThumbnailIfNeeded(query); } + query.finish(); + mutexLocker.unlock(); + if( removeTableRow ( d->T_SERIES, seriesDbId ) ) emit removed(medDataIndex(1, patientDbId, studyDbId, seriesDbId)); } bool medDatabaseRemover::isStudyEmpty ( int studyDbId ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare ( "SELECT id FROM " + d->T_SERIES + " WHERE study = :study " ); query.bindValue ( ":study", studyDbId ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(query); return !query.next(); } void medDatabaseRemover::removeStudy ( int patientDbId, int studyDbId ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare ( "SELECT thumbnail, name, uid FROM " + d->T_STUDY + " WHERE id = :id " ); query.bindValue ( ":id", studyDbId ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(query); if ( query.next() ) @@ -227,46 +236,56 @@ void medDatabaseRemover::removeStudy ( int patientDbId, int studyDbId ) } } + query.finish(); + mutexLocker.unlock(); + if( removeTableRow ( d->T_STUDY, studyDbId ) ) emit removed(medDataIndex(1, patientDbId, studyDbId, -1)); } bool medDatabaseRemover::isPatientEmpty ( int patientDbId ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare ( "SELECT id FROM " + d->T_STUDY + " WHERE patient = :patient " ); query.bindValue ( ":patient", patientDbId ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(query); return !query.next(); } void medDatabaseRemover::removePatient ( int patientDbId ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); QString patientId; query.prepare ( "SELECT thumbnail, patientId FROM " + d->T_PATIENT + " WHERE id = :patient " ); query.bindValue ( ":patient", patientDbId ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(query); if ( query.next() ) { removeThumbnailIfNeeded(query); patientId = query.value ( 1 ).toString(); } + + query.finish(); + mutexLocker.unlock(); + if( removeTableRow ( d->T_PATIENT, patientDbId ) ) emit removed(medDataIndex(1, patientDbId, -1, -1)); } bool medDatabaseRemover::removeTableRow ( const QString &table, int id ) { - QSqlDatabase db(d->db); - QSqlQuery query ( db ); + QSqlDatabase dbConnection = medDataManager::instance().controller()->getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare ( "DELETE FROM " + table + " WHERE id = :id" ); query.bindValue ( ":id", id ); + QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); medDataManager::instance().controller()->execQuery(query); return (query.numRowsAffected()==1); diff --git a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp index fa0aeb5f2a..7a1a770b36 100644 --- a/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medLocalDbController.cpp @@ -21,6 +21,7 @@ #include std::unique_ptr medLocalDbController::s_instance = nullptr; +const char* medLocalDbController::mainConnectionName = "sqlite"; medLocalDbController &medLocalDbController::instance() { @@ -31,35 +32,33 @@ medLocalDbController &medLocalDbController::instance() return *s_instance.get(); } -medLocalDbController::medLocalDbController() : medDatabasePersistentController() +medLocalDbController::medLocalDbController() : + medDatabasePersistentController(), databaseMutex(QMutex::Recursive) { -} + databasePath = medStorage::dataLocation(); -medLocalDbController::~medLocalDbController() -{ - m_database.close(); + if (medStorage::mkpath(databasePath)) + { + databasePath += "/db"; + } + else + { + qDebug() << "Cannot create database path: " << databasePath; + } } bool medLocalDbController::createConnection(void) { - medStorage::mkpath(medStorage::dataLocation() + "/"); - - m_database = QSqlDatabase::database("sqlite"); - if (!m_database.isValid()) - { - m_database = QSqlDatabase::addDatabase("QSQLITE", "sqlite"); - } - m_database.setDatabaseName(medStorage::dataLocation() + "/" + "db"); + QSqlDatabase database = createConnection(mainConnectionName); - if (!m_database.open()) + if (!database.open()) { - qDebug() << DTK_COLOR_FG_RED << "Cannot open database: unable to establish a database connection." << DTK_NO_COLOR; + qDebug() << DTK_COLOR_FG_RED << "Cannot open database: " << database.lastError().text() << DTK_NO_COLOR; return false; } else { - qDebug() << "Database opened at: " << qPrintable(m_database.databaseName()); - setConnected(true); + qDebug() << "Database opened at: " << qPrintable(database.databaseName()); } if (!createPatientTable() || !createStudyTable() || !createSeriesTable() || !updateFromNoVersionToVersion1()) @@ -68,7 +67,9 @@ bool medLocalDbController::createConnection(void) } // optimize speed of sqlite db - QSqlQuery query(m_database); + QSqlQuery query(database); + QMutexLocker mutexLocker(&getDatabaseMutex()); + if (!(query.prepare(QLatin1String("PRAGMA synchronous = 0")) && execQuery(query, __FILE__, __LINE__))) { qDebug() << "Could not set sqlite synchronous mode to asynchronous mode."; @@ -81,17 +82,54 @@ bool medLocalDbController::createConnection(void) return true; } -bool medLocalDbController::closeConnection(void) +bool medLocalDbController::isConnected() const { - m_database.close(); - QSqlDatabase::removeDatabase("QSQLITE"); - setConnected(false); - return true; + return getMainConnection().isOpen(); +} + +QSqlDatabase medLocalDbController::createConnection(QString name) const +{ + QSqlDatabase database; + + if (!databasePath.isEmpty()) + { + database = QSqlDatabase::addDatabase("QSQLITE", name); + const_cast(this)->databaseConnections.setLocalData(database); + + if (database.isValid()) + { + database.setDatabaseName(databasePath); + } + } + + return database; +} + +QSqlDatabase medLocalDbController::getMainConnection() const +{ + return QSqlDatabase::database(mainConnectionName); +} + +QSqlDatabase medLocalDbController::getThreadSpecificConnection() const +{ + if (!databaseConnections.hasLocalData()) + { + QSqlDatabase database = createConnection(QUuid::createUuid().toString()); + database.open(); + } + + return databaseConnections.localData(); +} + +QMutex& medLocalDbController::getDatabaseMutex() const +{ + return const_cast(this)->databaseMutex; } bool medLocalDbController::createPatientTable() { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); if (!query.prepare( "CREATE TABLE IF NOT EXISTS patient (" @@ -105,12 +143,16 @@ bool medLocalDbController::createPatientTable() { qDebug() << query.lastError(); } + + QMutexLocker mutexLocker(&getDatabaseMutex()); return execQuery(query, __FILE__, __LINE__); } bool medLocalDbController::createStudyTable() { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + QMutexLocker mutexLocker(&getDatabaseMutex()); query.prepare( "CREATE TABLE IF NOT EXISTS study (" @@ -140,7 +182,9 @@ bool medLocalDbController::createStudyTable() bool medLocalDbController::createSeriesTable() { - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + QMutexLocker mutexLocker(&getDatabaseMutex()); query.prepare( "CREATE TABLE IF NOT EXISTS series (" @@ -254,7 +298,9 @@ bool medLocalDbController::updateFromNoVersionToVersion1() // whatever reason the app/computer crashes, and we'll just try again on the // next launch. - QSqlQuery q(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery q(dbConnection); + QMutexLocker mutexLocker(&getDatabaseMutex()); if (!(q.exec("PRAGMA user_version") && q.first())) { @@ -350,8 +396,10 @@ bool medLocalDbController::updateFromNoVersionToVersion1() QList medLocalDbController::patients() const { QList ret; - QSqlQuery query(m_database); + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); query.prepare("SELECT id FROM patient"); + QMutexLocker mutexLocker(&getDatabaseMutex()); execQuery(query, __FILE__, __LINE__); #if QT_VERSION > 0x0406FF ret.reserve(query.size()); @@ -376,9 +424,11 @@ void medLocalDbController::requestDatabaseForModel(QHash #include +#include #include #include @@ -28,11 +29,14 @@ class MEDCORELEGACY_EXPORT medLocalDbController : public medDatabasePersistentCo Q_OBJECT public: - ~medLocalDbController(); static medLocalDbController &instance(); bool createConnection() override; - bool closeConnection() override; + bool isConnected() const override; + QSqlDatabase getMainConnection() const override; + QSqlDatabase getThreadSpecificConnection() const override; + + QMutex& getDatabaseMutex() const override; QList patients() const override; void requestDatabaseForModel(QHash > &patientData, @@ -46,6 +50,12 @@ class MEDCORELEGACY_EXPORT medLocalDbController : public medDatabasePersistentCo medLocalDbController(); private: + QThreadStorage databaseConnections; + QString databasePath; + QMutex databaseMutex; + + QSqlDatabase createConnection(QString name) const; + bool createPatientTable(); bool createStudyTable(); bool createSeriesTable(); @@ -53,4 +63,6 @@ class MEDCORELEGACY_EXPORT medLocalDbController : public medDatabasePersistentCo bool updateFromNoVersionToVersion1(); static std::unique_ptr s_instance; + + static const char* mainConnectionName; }; From aaf1192428a223f7f172882c303b0bdc3ff79323 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 29 Sep 2023 13:53:40 +0200 Subject: [PATCH 77/98] allow duplicate series names --- .../database/medAbstractDbController.h | 2 +- .../medCoreLegacy/database/medDataManager.cpp | 12 ++++++++++-- .../medCoreLegacy/database/medDataManager.h | 2 +- .../database/medDataPacsController.h | 2 +- .../database/medDatabaseImporter.cpp | 17 ++++++++++------- .../database/medDatabaseImporter.h | 4 +++- .../medDatabaseNonPersistentController.cpp | 2 +- .../medDatabaseNonPersistentController.h | 2 +- .../medDatabasePersistentController.cpp | 4 ++-- .../database/medDatabasePersistentController.h | 2 +- 10 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medAbstractDbController.h b/src/layers/legacy/medCoreLegacy/database/medAbstractDbController.h index 134cb6bdb7..86afcae3cd 100644 --- a/src/layers/legacy/medCoreLegacy/database/medAbstractDbController.h +++ b/src/layers/legacy/medCoreLegacy/database/medAbstractDbController.h @@ -60,7 +60,7 @@ class MEDCORELEGACY_EXPORT medAbstractDbController : public QObject public slots: virtual medAbstractData* retrieve(const medDataIndex& index, bool readFullData = true) const = 0; - virtual void importData(medAbstractData *data, const QUuid &importUuid) = 0; + virtual void importData(medAbstractData *data, const QUuid &importUuid, bool allowDuplicateSeriesName = false) = 0; virtual void importPath(const QString &file, const QUuid &importUuid, bool indexWithoutCopying) = 0; virtual bool importMetaDataFromPacs(const QHash > &pData, const QHash > &sData) = 0; diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp index a22a0e1fa4..c620f47eaa 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp @@ -173,7 +173,7 @@ void medDataManager::loadData(const medDataIndex &index) return; } -QUuid medDataManager::importData(medAbstractData *data, bool persistent) +QUuid medDataManager::importData(medAbstractData *data, bool persistent, bool allowDuplicateSeriesName) { if (!data) return QUuid(); @@ -183,7 +183,15 @@ QUuid medDataManager::importData(medAbstractData *data, bool persistent) medAbstractDbController *controller = persistent ? d->dbController : d->nonPersDbController; qDebug() << "generated uuid " << uuid.toString(); - controller->importData(data, uuid); + + if (persistent) + { + d->dbController->importData(data, uuid, allowDuplicateSeriesName); + } + else + { + controller->importData(data, uuid); + } return uuid; } diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.h b/src/layers/legacy/medCoreLegacy/database/medDataManager.h index 98de11e56f..a52ba3b8b9 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.h +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.h @@ -44,7 +44,7 @@ class MEDCORELEGACY_EXPORT medDataManager : public QObject QHash getPossibleWriters(medAbstractData* data); - QUuid importData(medAbstractData* data, bool persistent = false); + QUuid importData(medAbstractData* data, bool persistent = false, bool allowDuplicateSeriesName = false); QUuid importPath(const QString& dataPath, bool indexWithoutCopying, bool persistent = false); void fetchData(const QHash > &pData, const QHash > &sData); diff --git a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h index eb21828982..aa23aaaa1d 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDataPacsController.h @@ -85,7 +85,7 @@ class MEDCORELEGACY_EXPORT medDataPacsController : public medAbstractDbControlle public slots: medAbstractData *retrieve(const medDataIndex &index, bool readFullData = true) const override; - void importData(medAbstractData *data, const QUuid &importUuid) override{}; + void importData(medAbstractData *data, const QUuid &importUuid, bool allowDuplicateSeriesName = false) override{}; void importPath(const QString &file, const QUuid &importUuid, bool indexWithoutCopying) override{}; bool importMetaDataFromPacs(const QHash > &pData, const QHash > &sData) override; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp index 8250a41701..7d2b09039b 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp @@ -28,15 +28,15 @@ //----------------------------------------------------------------------------------------------------------- medDatabaseImporter::medDatabaseImporter ( const QString& file, const QUuid& uuid, bool indexWithoutImporting) : - medAbstractDatabaseImporter(file, uuid, indexWithoutImporting) + medAbstractDatabaseImporter(file, uuid, indexWithoutImporting), duplicateSeriesNamesEnabled(false) { } //----------------------------------------------------------------------------------------------------------- -medDatabaseImporter::medDatabaseImporter ( medAbstractData* medData, const QUuid& uuid ) : - medAbstractDatabaseImporter(medData, uuid) +medDatabaseImporter::medDatabaseImporter (medAbstractData* medData, const QUuid& uuid , bool allowDuplicateSeriesName) : + medAbstractDatabaseImporter(medData, uuid), duplicateSeriesNamesEnabled(allowDuplicateSeriesName) { } @@ -91,10 +91,13 @@ medDataIndex medDatabaseImporter::populateDatabaseAndGenerateThumbnails ( medAbs int studyDbId = getOrCreateStudy ( medData, dbConnection, patientDbId ); - // Update name of the series if a permanent data has this name already - QString seriesName = medData->metadata(medMetaDataKeys::SeriesDescription.key()); - QString newSeriesName = ensureUniqueSeriesName(seriesName, QString::number(studyDbId)); - medData->setMetaData(medMetaDataKeys::SeriesDescription.key(), newSeriesName); + if (!duplicateSeriesNamesEnabled) + { + // Update name of the series if a permanent data has this name already + QString seriesName = medData->metadata(medMetaDataKeys::SeriesDescription.key()); + QString newSeriesName = ensureUniqueSeriesName(seriesName, QString::number(studyDbId)); + medData->setMetaData(medMetaDataKeys::SeriesDescription.key(), newSeriesName); + } int seriesDbId = getOrCreateSeries ( medData, dbConnection, studyDbId ); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.h b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.h index febb00ad83..80ac4657c7 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.h @@ -37,10 +37,12 @@ class MEDCORELEGACY_EXPORT medDatabaseImporter : public medAbstractDatabaseImpor public: medDatabaseImporter ( const QString& file, const QUuid& uuid, bool indexWithoutImporting = false); - medDatabaseImporter ( medAbstractData* medData, const QUuid& callerUuid ); + medDatabaseImporter ( medAbstractData* medData, const QUuid& callerUuid, bool allowDuplicateSeriesName = false); ~medDatabaseImporter() override = default; private: + bool duplicateSeriesNamesEnabled; + QString ensureUniqueSeriesName ( const QString seriesName, const QString studyId ) override; medDataIndex populateDatabaseAndGenerateThumbnails ( medAbstractData* medData, QString pathToStoreThumbnail ) override; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp index 9285167d36..aae4251d15 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.cpp @@ -134,7 +134,7 @@ bool medDatabaseNonPersistentController::isConnected() const } void medDatabaseNonPersistentController::importData(medAbstractData *data, - const QUuid & callerUuid) + const QUuid & callerUuid, bool allowDuplicateSeriesName) { medDatabaseNonPersistentImporter * importer = new medDatabaseNonPersistentImporter(data,callerUuid); medMessageProgress * message = medMessageController::instance().showProgress("Importing data item"); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h index 6538a1df5e..f04fe97f2e 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentController.h @@ -72,7 +72,7 @@ class MEDCORELEGACY_EXPORT medDatabaseNonPersistentController: public medAbstrac public slots: virtual medAbstractData *retrieve(const medDataIndex &index, bool readFullData = true) const; - void importData(medAbstractData *data, const QUuid &callerUuid); + void importData(medAbstractData *data, const QUuid &callerUuid, bool allowDuplicateSeriesName = false); void importPath(const QString &file, const QUuid &callerUuid, bool indexWithoutCopying); bool importMetaDataFromPacs(const QHash > &pData, const QHash > &sData) override { return false; }; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp index c4085971af..4eb4db0260 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp @@ -185,9 +185,9 @@ void medDatabasePersistentController::importPath(const QString &file, const QUui * Import data into the db read from memory * @param medAbstractData * data dataObject */ -void medDatabasePersistentController::importData(medAbstractData *data, const QUuid &importUuid) +void medDatabasePersistentController::importData(medAbstractData *data, const QUuid &importUuid, bool allowDuplicateSeriesName) { - medDatabaseImporter *importer = new medDatabaseImporter(data, importUuid); + medDatabaseImporter *importer = new medDatabaseImporter(data, importUuid, allowDuplicateSeriesName); medMessageProgress *message = medMessageController::instance().showProgress("Saving database item"); connect(importer, SIGNAL(progressed(int)), message, SLOT(setProgress(int))); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h index dcda21f56d..1ce5eb92a7 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h @@ -86,7 +86,7 @@ public slots: medAbstractData *retrieve(const medDataIndex &index, bool readFullData = true) const; void importPath(const QString &file, const QUuid &importUuid, bool indexWithoutCopying = false) override; - void importData(medAbstractData *data, const QUuid &importUuid) override; + void importData(medAbstractData *data, const QUuid &importUuid, bool allowDuplicateSeriesName = false) override; bool importMetaDataFromPacs(const QHash > &pData, const QHash > &sData) override { return false; }; From a47e7b340e02f4665401464537be6c0b3a754edd Mon Sep 17 00:00:00 2001 From: mathildemerle Date: Mon, 6 Nov 2023 08:44:05 +0100 Subject: [PATCH 78/98] [Export] allow export of multiple data at same time (#780) --- .../medCoreLegacy/database/medDataManager.cpp | 6 ++++ .../medCoreLegacy/database/medDataManager.h | 1 + .../database/medDatabaseExporter.cpp | 28 +++++++++++++++++-- .../database/medDatabaseExporter.h | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp index c620f47eaa..40f36798d1 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.cpp @@ -477,6 +477,12 @@ void medDataManager::exportDataToPath(dtkSmartPointer data, con launchExporter(exporter, filename); } +void medDataManager::exportDataToPath(QList dataList, const QString & filename, const QString & writer) +{ + auto exporter = new medDatabaseExporter(dataList, filename, writer); + launchExporter(exporter, filename); +} + void medDataManager::launchExporter(medDatabaseExporter *exporter, const QString &filename) { diff --git a/src/layers/legacy/medCoreLegacy/database/medDataManager.h b/src/layers/legacy/medCoreLegacy/database/medDataManager.h index a52ba3b8b9..4899924291 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDataManager.h +++ b/src/layers/legacy/medCoreLegacy/database/medDataManager.h @@ -51,6 +51,7 @@ class MEDCORELEGACY_EXPORT medDataManager : public QObject void exportData(dtkSmartPointer data); void exportDataToPath(dtkSmartPointer data, const QString& path, const QString& format = ""); + void exportDataToPath(QList dataList, const QString& path, const QString& format = ""); QUuid makePersistent(medDataIndex index); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp index 2a284ed69f..4713bd988d 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.cpp @@ -13,14 +13,17 @@ #include #include +#include #include class medDatabaseExporterPrivate { public: dtkSmartPointer data; + QList dataList; QString filename; QString writer; + bool saveMultipleData; }; medDatabaseExporter::medDatabaseExporter(dtkSmartPointer data, const QString & filename, const QString & writer) : medJobItemL(), d(new medDatabaseExporterPrivate) @@ -28,6 +31,17 @@ medDatabaseExporter::medDatabaseExporter(dtkSmartPointer data, d->data = data; d->filename = filename; d->writer = writer; + d->saveMultipleData = false; +} + +medDatabaseExporter::medDatabaseExporter(QList data, const QString & filename, const QString & writer) + : medJobItemL(), d(new medDatabaseExporterPrivate) +{ + d->data = nullptr; + d->dataList = data; + d->filename = filename; + d->writer = writer; + d->saveMultipleData = true; } medDatabaseExporter::~medDatabaseExporter() @@ -45,7 +59,8 @@ medDatabaseExporter::~medDatabaseExporter() */ void medDatabaseExporter::internalRun() { - if (!d->data) + if ((!d->saveMultipleData && !d->data) || + (d->saveMultipleData && d->dataList.isEmpty())) { emit showError("Cannot export data", 3000); return; @@ -57,7 +72,16 @@ void medDatabaseExporter::internalRun() } dtkAbstractDataWriter * dataWriter = medAbstractDataFactory::instance()->writer(d->writer); - dataWriter->setData(d->data); + if(!d->saveMultipleData) + { + dataWriter->setData(d->data); + } + else + { + auto medDataWriter = dynamic_cast(dataWriter); + Q_ASSERT(medDataWriter != nullptr); + medDataWriter->setData(d->dataList); + } if ( ! dataWriter->canWrite(d->filename) || ! dataWriter->write(d->filename)) { diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.h b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.h index 2d2c135ad6..da0b022444 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseExporter.h @@ -28,6 +28,7 @@ class MEDCORELEGACY_EXPORT medDatabaseExporter : public medJobItemL public: medDatabaseExporter(dtkSmartPointer data, const QString & filename, const QString & writer); + medDatabaseExporter(QList data, const QString & filename, const QString & writer); ~medDatabaseExporter(); protected: From 550d9e8963bdf7616e6d2950f9e9cd7942df6356 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 8 Feb 2024 11:45:06 +0100 Subject: [PATCH 79/98] [Video] put a better default frame rate --- src/plugins/legacy/medExportVideo/medExportVideo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/legacy/medExportVideo/medExportVideo.cpp b/src/plugins/legacy/medExportVideo/medExportVideo.cpp index 8a717472be..3a4b80bdd9 100644 --- a/src/plugins/legacy/medExportVideo/medExportVideo.cpp +++ b/src/plugins/legacy/medExportVideo/medExportVideo.cpp @@ -85,7 +85,7 @@ ExportVideo::ExportVideo() : medAbstractProcessLegacy(), d(new ExportVideoPrivat // User parameters d->format = OGGVORBIS; d->filename = ""; - d->frameRate = 1; + d->frameRate = 24; d->subsampling = false; d->quality = 2; } From 83b4b559057e56eeb26dd5970d2ee937293e2272 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Tue, 20 Feb 2024 10:53:30 +0100 Subject: [PATCH 80/98] [DCM] same test as on med3.3 branch, quicker --- src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp index 78b93a400f..d19b2a48d0 100644 --- a/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp +++ b/src/layers/legacy/medImageIO/itkDCMTKImageIO.cpp @@ -687,7 +687,7 @@ double DCMTKImageIO::GetPositionOnStackingAxisForImage (int index) double DCMTKImageIO::GetPositionFromPrincipalAxisIndex(int index, int principalAxisIndex) { std::string s_position = this->GetMetaDataValueString("(0020,0032)", index); - if ( s_position == "" ) + if (s_position.empty()) { itkWarningMacro ( << "Tag (0020,0032) (ImageOrigin) was not found, assuming 0.0/0.0/0.0" << std::endl); return 0.0; From 4d2cc14b490d7ddd7063145f3b60a839593e4f73 Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Thu, 22 Feb 2024 10:17:46 +0100 Subject: [PATCH 81/98] [DCMTK] repo not accessible anymore, switch to github --- superbuild/projects_modules/DCMTK.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superbuild/projects_modules/DCMTK.cmake b/superbuild/projects_modules/DCMTK.cmake index 68d08c461a..90ca7dc215 100644 --- a/superbuild/projects_modules/DCMTK.cmake +++ b/superbuild/projects_modules/DCMTK.cmake @@ -39,7 +39,7 @@ if (NOT USE_SYSTEM_${ep}) ## Set up versioning control ## ############################################################################# -set(git_url git://git.dcmtk.org/dcmtk.git) +set(git_url ${GITHUB_PREFIX}DCMTK/dcmtk.git) set(git_tag DCMTK-3.6.2) From 91eca8e4c3e82d6ad192003c7eb622edf94dc4da Mon Sep 17 00:00:00 2001 From: Mathilde Merle Date: Mon, 26 Feb 2024 11:58:15 +0100 Subject: [PATCH 82/98] {Reslice} background color for selected view --- src/plugins/legacy/reformat/medResliceViewer.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/legacy/reformat/medResliceViewer.cpp b/src/plugins/legacy/reformat/medResliceViewer.cpp index c5e04aedf4..56003b649a 100644 --- a/src/plugins/legacy/reformat/medResliceViewer.cpp +++ b/src/plugins/legacy/reformat/medResliceViewer.cpp @@ -162,6 +162,7 @@ medResliceViewer::medResliceViewer(medAbstractView *view, QWidget *parent): medA riw[i]->SetRenderWindow(renderWindow); riw[i]->GetRenderer()->SetBackground(0,0,0); // black background } + riw[selectedView]->GetRenderer()->SetBackground(0.3,0,0); // Build views for (int i = 0; i < 4; i++) @@ -507,18 +508,15 @@ bool medResliceViewer::eventFilter(QObject *object, QEvent *event) { for(int i=0; i<3; i++) { + riw[i]->GetRenderer()->SetBackground(0,0,0); if (views[i]==object) { selectedView = i; } } - return false; - } - - if (event->type() == QEvent::FocusOut) - { - return false; + riw[selectedView]->GetRenderer()->SetBackground(0.3,0,0); } + return false; } From 7da6c37e5743b40f214650352525171ef424ae7f Mon Sep 17 00:00:00 2001 From: fcollot Date: Thu, 4 Apr 2024 17:29:23 +0200 Subject: [PATCH 83/98] Switch to _ROOT (#1212) --- CMakeLists.txt | 2 + packaging/apple/ApplePackScript.cmake.in | 2 +- packaging/apple/mac_packager.sh.in | 10 ++--- packaging/windows/WindowsPackaging.cmake | 26 ++++++------- src/cmake/module/set_lib_install_rules.cmake | 4 +- .../module/set_plugin_install_rules.cmake | 2 +- superbuild/CMakeLists.txt | 9 ++--- superbuild/ConfigureExternalProjects.cmake | 1 + superbuild/launchers/Launchers.cmake | 12 +++--- superbuild/projects_modules/DCMTK.cmake | 3 +- superbuild/projects_modules/ITK.cmake | 8 +--- superbuild/projects_modules/LogDemons.cmake | 10 ++--- superbuild/projects_modules/QtDCM.cmake | 6 +-- superbuild/projects_modules/RPI.cmake | 6 +-- superbuild/projects_modules/TTK.cmake | 6 +-- superbuild/projects_modules/VTK.cmake | 8 ++-- superbuild/projects_modules/dtk.cmake | 2 +- superbuild/projects_modules/dtkImaging.cmake | 4 +- superbuild/projects_modules/ffmpeg.cmake | 2 +- superbuild/projects_modules/medInria.cmake | 39 ++++++++----------- superbuild/projects_modules/pyncpp.cmake | 2 +- 21 files changed, 78 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d332a1cdb8..4774e1da74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ if(POLICY CMP0020) cmake_policy(SET CMP0020 NEW) endif() +cmake_policy(SET CMP0074 NEW) + if(NOT DEFINED ${MEDINRIA_SUPERBUILD_VERSION}) set(MEDINRIA_SUPERBUILD_VERSION 3.4.0) endif() diff --git a/packaging/apple/ApplePackScript.cmake.in b/packaging/apple/ApplePackScript.cmake.in index c9625bd2b6..4929f31c3b 100644 --- a/packaging/apple/ApplePackScript.cmake.in +++ b/packaging/apple/ApplePackScript.cmake.in @@ -1,2 +1,2 @@ execute_process(COMMAND rm -f @CMAKE_BINARY_DIR@/medInria-@@PROJECT_NAME@_VERSION@.dmg) -execute_process(COMMAND @PROJECT_BINARY_DIR@/apple/mac_packager.sh @medInria_DIR@/bin/plugins @medInria_DIR@/bin/plugins_legacy @PRIVATE_PLUGINS_DIRS@ @PRIVATE_PLUGINS_LEGACY_DIRS@) +execute_process(COMMAND @PROJECT_BINARY_DIR@/apple/mac_packager.sh @medInria_ROOT@/bin/plugins @medInria_ROOT@/bin/plugins_legacy @PRIVATE_PLUGINS_DIRS@ @PRIVATE_PLUGINS_LEGACY_DIRS@) diff --git a/packaging/apple/mac_packager.sh.in b/packaging/apple/mac_packager.sh.in index 477ed4d8c4..75256973e1 100755 --- a/packaging/apple/mac_packager.sh.in +++ b/packaging/apple/mac_packager.sh.in @@ -16,9 +16,9 @@ cd @PROJECT_BINARY_DIR@ \rm -fr TmpInstall mkdir -p TmpInstall/medInria.app/Contents/Resources -cp -r @medInria_DIR@/bin/medInria.app/Contents/MacOS TmpInstall/medInria.app/Contents -cp @medInria_DIR@/bin/medInria.app/Contents/Info.plist TmpInstall/medInria.app/Contents -cp @medInria_DIR@/bin/medInria.app/Contents/Resources/medInria.icns TmpInstall/medInria.app/Contents/Resources +cp -r @medInria_ROOT@/bin/medInria.app/Contents/MacOS TmpInstall/medInria.app/Contents +cp @medInria_ROOT@/bin/medInria.app/Contents/Info.plist TmpInstall/medInria.app/Contents +cp @medInria_ROOT@/bin/medInria.app/Contents/Resources/medInria.icns TmpInstall/medInria.app/Contents/Resources cd TmpInstall @@ -31,9 +31,9 @@ for i in $*; do injectDirs="$injectDirs -inject-dir=$i" done -@dtk_DIR@/bin/dtkDeploy medInria.app $injectDirs &>/dev/null +@dtk_ROOT@/bin/dtkDeploy medInria.app $injectDirs &>/dev/null -@CMAKE_COMMAND@ -DCMAKE_INSTALL_PREFIX:STRING=medInria.app/Contents/Resources -DCMAKE_INSTALL_COMPONENT:STRING=Python -P @pyncpp_DIR@/cmake_install.cmake +@CMAKE_COMMAND@ -DCMAKE_INSTALL_PREFIX:STRING=medInria.app/Contents/Resources -DCMAKE_INSTALL_COMPONENT:STRING=Python -P @pyncpp_ROOT@/cmake_install.cmake #Run fancy packaging apple script diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index 16816d666b..0b8463977f 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -88,7 +88,7 @@ endif() set(APP "\${CMAKE_INSTALL_PREFIX}/bin/medInria.exe") set(QT_BINARY_DIR "${Qt5_DIR}/../../../bin") set(QT_PLUGINS_DIR "${Qt5_DIR}/../../../plugins") -set(MEDINRIA_FILES "${medInria_DIR}/Release/bin") +set(MEDINRIA_FILES "${medInria_ROOT}/Release/bin") list(APPEND libSearchDirs @@ -96,26 +96,26 @@ list(APPEND ${QT_PLUGINS_DIR}/platforms ${QT_BINARY_DIR}/sqldrivers ${QT_BINARY_DIR} - ${ITK_DIR}/bin/Release - ${DCMTK_DIR}/bin/Release - ${VTK_DIR}/bin/Release - ${QtDCM_DIR}/bin/Release - ${TTK_DIR}/bin/Release - ${dtk_DIR}/bin/Release - ${RPI_DIR}/bin/Release + ${ITK_ROOT}/bin/Release + ${DCMTK_ROOT}/bin/Release + ${VTK_ROOT}/bin/Release + ${QtDCM_ROOT}/bin/Release + ${TTK_ROOT}/bin/Release + ${dtk_ROOT}/bin/Release + ${RPI_ROOT}/bin/Release ) set(CPACK_INSTALL_CMAKE_PROJECTS - ${pyncpp_DIR} pyncpp Python "/" + ${pyncpp_ROOT} pyncpp Runtime "/" ${CPACK_INSTALL_CMAKE_PROJECTS} ) install(CODE " -file(GLOB_RECURSE itk_files LIST_DIRECTORIES true \"${ITK_DIR}/bin/*.dll\") -file(GLOB_RECURSE vtk_files LIST_DIRECTORIES true \"${VTK_DIR}/bin/*.dll\") -file(GLOB_RECURSE dtk_files LIST_DIRECTORIES true \"${dtk_DIR}/bin/*.dll\") -file(GLOB_RECURSE dcm_files LIST_DIRECTORIES true \"${QtDCM_DIR}/bin/*.dll\") +file(GLOB_RECURSE itk_files LIST_DIRECTORIES true \"${ITK_ROOT}/bin/*.dll\") +file(GLOB_RECURSE vtk_files LIST_DIRECTORIES true \"${VTK_ROOT}/bin/*.dll\") +file(GLOB_RECURSE dtk_files LIST_DIRECTORIES true \"${dtk_ROOT}/bin/*.dll\") +file(GLOB_RECURSE dcm_files LIST_DIRECTORIES true \"${QtDCM_ROOT}/bin/*.dll\") file(GLOB_RECURSE qt5_files LIST_DIRECTORIES true \"${QT_BINARY_DIR}/*.dll\") list(APPEND files \${itk_files}) list(APPEND files \${vtk_files}) diff --git a/src/cmake/module/set_lib_install_rules.cmake b/src/cmake/module/set_lib_install_rules.cmake index 789a9a6247..18de8757a6 100644 --- a/src/cmake/module/set_lib_install_rules.cmake +++ b/src/cmake/module/set_lib_install_rules.cmake @@ -36,8 +36,8 @@ function(set_lib_install_rules target) cmake_parse_arguments(PARSE_ARGV 1 "ARG" "" "" "HEADERS") get_property(GENERATOR_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - if (medInria_DIR) - set(dest ${medInria_DIR}) + if (medInria_ROOT) + set(dest ${medInria_ROOT}) else() set(dest ${CMAKE_BINARY_DIR}) endif() diff --git a/src/cmake/module/set_plugin_install_rules.cmake b/src/cmake/module/set_plugin_install_rules.cmake index e5a02aefc5..08f5b83245 100644 --- a/src/cmake/module/set_plugin_install_rules.cmake +++ b/src/cmake/module/set_plugin_install_rules.cmake @@ -105,5 +105,5 @@ endmacro() # ################################################################################ macro(set_plugin_install_rules_legacy_external target) - set_plugin_install_rules_legacy_generic(${target} ${medInria_DIR}) + set_plugin_install_rules_legacy_generic(${target} ${medInria_ROOT}) endmacro() diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index 9ee6b6bc1f..83e01f29ff 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -202,10 +202,9 @@ endif() set(external_projects ${external_projects} PARENT_SCOPE) foreach(external_project ${external_projects}) - if(NOT USE_SYSTEM_${external_project} AND BUILD_SHARED_LIBS_${external_project}) - ExternalProject_Get_Property(${external_project} binary_dir) - ExternalProject_Get_Property(${external_project} source_dir) - set(${external_project}_DIR ${binary_dir} PARENT_SCOPE) - set(${external_project}_SOURCE_DIR ${source_dir} PARENT_SCOPE) + if(DEFINED ${external_project}_ROOT) + set(${external_project}_ROOT ${${external_project}_ROOT} PARENT_SCOPE) endif() endforeach() + +set(medInria_SOURCE_DIR ${medInria_SOURCE_DIR} PARENT_SCOPE) diff --git a/superbuild/ConfigureExternalProjects.cmake b/superbuild/ConfigureExternalProjects.cmake index c2ec4b6888..491d51a773 100644 --- a/superbuild/ConfigureExternalProjects.cmake +++ b/superbuild/ConfigureExternalProjects.cmake @@ -134,6 +134,7 @@ set(ep_common_cache_args -DCMAKE_CXX_COMPILER:=${CMAKE_CXX_COMPILER} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} + -DCMAKE_POLICY_DEFAULT_CMP0074=NEW ) if(CMAKE_EXTRA_GENERATOR) diff --git a/superbuild/launchers/Launchers.cmake b/superbuild/launchers/Launchers.cmake index 231083d33f..cd8fcad088 100644 --- a/superbuild/launchers/Launchers.cmake +++ b/superbuild/launchers/Launchers.cmake @@ -23,19 +23,17 @@ foreach (dir ${PRIVATE_PLUGINS_LEGACY_DIRS}) set(DEV_PLUGINS_LEGACY_DIRS "${DEV_PLUGINS_LEGACY_DIRS}:${dir}/bin/plugins_legacy") endforeach() -ExternalProject_Get_Property(medInria binary_dir) - set(LOCATE "") -set(MEDINRIA_DIR ${CMAKE_BINARY_DIR}) +set(MEDINRIA_DIR ${medInria_ROOT}) if (APPLE) - set(MEDINRIA_BIN ${binary_dir}/bin/medInria.app/Contents/MacOS/medInria) + set(MEDINRIA_BIN ${medInria_ROOT}/bin/medInria.app/Contents/MacOS/medInria) else() - set(MEDINRIA_BIN ${binary_dir}/bin/medInria) + set(MEDINRIA_BIN ${medInria_ROOT}/bin/medInria) endif() -set(MEDINRIA_PLUGINS_DIRS "${binary_dir}/bin/plugins:${DEV_PLUGINS_DIRS}") -set(MEDINRIA_PLUGINS_LEGACY_DIRS "${binary_dir}/bin/plugins_legacy:${DEV_PLUGINS_LEGACY_DIRS}") +set(MEDINRIA_PLUGINS_DIRS "${medInria_ROOT}/bin/plugins:${DEV_PLUGINS_DIRS}") +set(MEDINRIA_PLUGINS_LEGACY_DIRS "${medInria_ROOT}/bin/plugins_legacy:${DEV_PLUGINS_LEGACY_DIRS}") configure_file(${CMAKE_CURRENT_LIST_DIR}/medInria.sh.in ${CMAKE_BINARY_DIR}/medInria.sh @ONLY) diff --git a/superbuild/projects_modules/DCMTK.cmake b/superbuild/projects_modules/DCMTK.cmake index 90ca7dc215..c1683fd971 100644 --- a/superbuild/projects_modules/DCMTK.cmake +++ b/superbuild/projects_modules/DCMTK.cmake @@ -1,3 +1,4 @@ + ################################################################################ # # medInria @@ -122,7 +123,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/ITK.cmake b/superbuild/projects_modules/ITK.cmake index 24bafee290..6d33a0eeed 100644 --- a/superbuild/projects_modules/ITK.cmake +++ b/superbuild/projects_modules/ITK.cmake @@ -68,11 +68,7 @@ set(cmake_args -DModule_ITKReview:BOOL=ON -DModule_ITKVtkGlue:BOOL=ON -DITK_LEGACY_REMOVE:BOOL=ON - -DVTK_DIR:PATH=${VTK_DIR} - ) - -set(cmake_cache_args - -DVTK_DIR:PATH=${VTK_DIR} + -DVTK_ROOT:PATH=${VTK_ROOT} ) ## ############################################################################# @@ -111,7 +107,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(ITK binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/LogDemons.cmake b/superbuild/projects_modules/LogDemons.cmake index f8937ab29c..e3b369ced5 100644 --- a/superbuild/projects_modules/LogDemons.cmake +++ b/superbuild/projects_modules/LogDemons.cmake @@ -68,9 +68,9 @@ set(cmake_args set(cmake_cache_args -DQt5_DIR:FILEPATH=${Qt5_DIR} - -Ddtk_DIR:FILEPATH=${dtk_DIR} - -DITK_DIR:FILEPATH=${ITK_DIR} - -DRPI_DIR:FILEPATH=${RPI_DIR} + -Ddtk_ROOT:FILEPATH=${dtk_ROOT} + -DITK_ROOT:FILEPATH=${ITK_ROOT} + -DRPI_ROOT:FILEPATH=${RPI_ROOT} ) ## ############################################################################# @@ -102,9 +102,9 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep -endfunction() \ No newline at end of file +endfunction() diff --git a/superbuild/projects_modules/QtDCM.cmake b/superbuild/projects_modules/QtDCM.cmake index fe83616e35..073470080c 100644 --- a/superbuild/projects_modules/QtDCM.cmake +++ b/superbuild/projects_modules/QtDCM.cmake @@ -78,8 +78,8 @@ set(cmake_args set(cmake_cache_args -DQt5_DIR:FILEPATH=${Qt5_DIR} - -DITK_DIR:FILEPATH=${ITK_DIR} - -DDCMTK_DIR:FILEPATH=${DCMTK_DIR} + -DITK_ROOT:FILEPATH=${ITK_ROOT} + -DDCMTK_ROOT:FILEPATH=${DCMTK_ROOT} ) @@ -112,7 +112,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/RPI.cmake b/superbuild/projects_modules/RPI.cmake index 4da33c1ad6..b5cd1616ab 100644 --- a/superbuild/projects_modules/RPI.cmake +++ b/superbuild/projects_modules/RPI.cmake @@ -69,7 +69,7 @@ set(cmake_args ) set(cmake_cache_args - -DITK_DIR:FILEPATH=${ITK_DIR} + -DITK_ROOT:FILEPATH=${ITK_ROOT} ) @@ -102,9 +102,9 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep -endfunction() \ No newline at end of file +endfunction() diff --git a/superbuild/projects_modules/TTK.cmake b/superbuild/projects_modules/TTK.cmake index 0c58930e30..75a612c188 100644 --- a/superbuild/projects_modules/TTK.cmake +++ b/superbuild/projects_modules/TTK.cmake @@ -69,8 +69,8 @@ set(cmake_args ) set(cmake_cache_args - -DVTK_DIR:FILEPATH=${VTK_DIR} - -DITK_DIR:FILEPATH=${ITK_DIR} + -DVTK_ROOT:FILEPATH=${VTK_ROOT} + -DITK_ROOT:FILEPATH=${ITK_ROOT} ) @@ -103,7 +103,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/VTK.cmake b/superbuild/projects_modules/VTK.cmake index 271beb842b..3f75156894 100644 --- a/superbuild/projects_modules/VTK.cmake +++ b/superbuild/projects_modules/VTK.cmake @@ -120,9 +120,9 @@ endif() if(USE_Python) set(python_version "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") if(UNIX) - set(python_executable "${pyncpp_DIR}/lib/python${python_version}/bin/python${python_version}") - set(python_include "${pyncpp_DIR}/lib/python${python_version}/include/python${python_version}") - set(python_library "${pyncpp_DIR}/lib/python${python_version}/lib/libpython${python_version}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(python_executable "${pyncpp_ROOT}/lib/python${python_version}/bin/python${python_version}") + set(python_include "${pyncpp_ROOT}/lib/python${python_version}/include/python${python_version}") + set(python_library "${pyncpp_ROOT}/lib/python${python_version}/lib/libpython${python_version}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() # TODO endif() @@ -171,7 +171,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/dtk.cmake b/superbuild/projects_modules/dtk.cmake index 05d0095eb7..1d7f261056 100644 --- a/superbuild/projects_modules/dtk.cmake +++ b/superbuild/projects_modules/dtk.cmake @@ -113,7 +113,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/dtkImaging.cmake b/superbuild/projects_modules/dtkImaging.cmake index 4c68c8c826..0819ce8972 100644 --- a/superbuild/projects_modules/dtkImaging.cmake +++ b/superbuild/projects_modules/dtkImaging.cmake @@ -65,7 +65,7 @@ set(cmake_args -DCMAKE_SHARED_LINKER_FLAGS:STRING=${${ep}_shared_linker_flags} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS_${ep}} - -Ddtk_DIR:PATH=${dtk_DIR} + -Ddtk_ROOT:PATH=${dtk_ROOT} ) set(cmake_cache_args @@ -102,7 +102,7 @@ ExternalProject_Add(${ep} ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/ffmpeg.cmake b/superbuild/projects_modules/ffmpeg.cmake index 6e73985335..56e84217fb 100644 --- a/superbuild/projects_modules/ffmpeg.cmake +++ b/superbuild/projects_modules/ffmpeg.cmake @@ -86,7 +86,7 @@ endif() ## ############################################################################# ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) +set(${ep}_ROOT ${binary_dir} PARENT_SCOPE) endif() #NOT USE_SYSTEM_ep diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index 261938aae2..5c36726cfb 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -87,15 +87,15 @@ set(cmake_args ) set(cmake_cache_args - -DDCMTK_DIR:PATH=${DCMTK_DIR} - -Ddtk_DIR:PATH=${dtk_DIR} - -DITK_DIR:PATH=${ITK_DIR} - -DQtDCM_DIR:PATH=${QtDCM_DIR} - -DRPI_DIR:PATH=${RPI_DIR} - -DTTK_DIR:PATH=${TTK_DIR} - -DVTK_DIR:PATH=${VTK_DIR} + -DDCMTK_ROOT:PATH=${DCMTK_ROOT} + -Ddtk_ROOT:PATH=${dtk_ROOT} + -DITK_ROOT:PATH=${ITK_ROOT} + -DQtDCM_ROOT:PATH=${QtDCM_ROOT} + -DRPI_ROOT:PATH=${RPI_ROOT} + -DTTK_ROOT:PATH=${TTK_ROOT} + -DVTK_ROOT:PATH=${VTK_ROOT} -DQt5_DIR:PATH=${Qt5_DIR} - -DLogDemons_DIR:PATH=${LogDemons_DIR} + -DLogDemons_ROOT:PATH=${LogDemons_ROOT} -DBoost_INCLUDE_DIR:PATH=${Boost_INCLUDE_DIR} ) @@ -107,13 +107,13 @@ endif() if (USE_DTKIMAGING) set(cmake_args ${cmake_args} - -DdtkImaging_DIR:PATH=${dtkImaging_DIR} + -DdtkImaging_ROOT:PATH=${dtkImaging_ROOT} ) endif() if (USE_Python) list(APPEND cmake_cache_args - -Dpyncpp_DIR:PATH=${pyncpp_DIR} + -Dpyncpp_ROOT:PATH=${pyncpp_ROOT} ) endif() @@ -139,21 +139,14 @@ ExternalProject_Add(${ep} ## Set variable to provide infos about the project ## ############################################################################# -ExternalProject_Get_Property(${ep} binary_dir) -set(${ep}_DIR ${binary_dir} PARENT_SCOPE) - -ExternalProject_Get_Property(${ep} source_dir) -set(${ep}_SOURCE_DIR ${source_dir} PARENT_SCOPE) - - if (WIN32) - file(TO_NATIVE_PATH ${ITK_DIR} ITK_BIN_BASE) - file(TO_NATIVE_PATH ${VTK_DIR} VTK_BIN_BASE) - file(TO_NATIVE_PATH ${dtk_DIR} DTK_BIN_BASE) - file(TO_NATIVE_PATH ${QtDCM_DIR} DCM_BIN_BASE) + file(TO_NATIVE_PATH ${ITK_ROOT} ITK_BIN_BASE) + file(TO_NATIVE_PATH ${VTK_ROOT} VTK_BIN_BASE) + file(TO_NATIVE_PATH ${dtk_ROOT} DTK_BIN_BASE) + file(TO_NATIVE_PATH ${QtDCM_ROOT} DCM_BIN_BASE) file(TO_NATIVE_PATH ${_qt5Core_install_prefix} QT5_BIN_BASE) file(TO_NATIVE_PATH ${medInria_BINARY_DIR} MED_BIN_BASE) - file(TO_NATIVE_PATH ${pyncpp_DIR} PYNCPP_BIN_BASE) + file(TO_NATIVE_PATH ${pyncpp_ROOT} PYNCPP_BIN_BASE) set(CONFIG_MODE $<$:Debug>$<$:Release>$<$:MinSizeRel>$<$:RelWithDebInfo>) @@ -175,4 +168,6 @@ endif() endif() #NOT USE_SYSTEM_ep +set(${ep}_ROOT ${medInria_BINARY_DIR} PARENT_SCOPE) + endfunction() diff --git a/superbuild/projects_modules/pyncpp.cmake b/superbuild/projects_modules/pyncpp.cmake index e6f8b0076b..f8240ce2d2 100644 --- a/superbuild/projects_modules/pyncpp.cmake +++ b/superbuild/projects_modules/pyncpp.cmake @@ -74,7 +74,7 @@ function(pyncpp_project) ## Export variables ## ##################################################################### - set(${ep}_DIR ${build_path} PARENT_SCOPE) + set(${ep}_ROOT "${build_path}" PARENT_SCOPE) endif() From 7fce0517867e7f963a755324a1252c5894ee37f3 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 10:54:41 +0200 Subject: [PATCH 84/98] [Linux packaging] install external projects manually (#1216) --- packaging/linux/LinuxPackaging.cmake | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packaging/linux/LinuxPackaging.cmake b/packaging/linux/LinuxPackaging.cmake index c83ed55f39..051dbbe2af 100644 --- a/packaging/linux/LinuxPackaging.cmake +++ b/packaging/linux/LinuxPackaging.cmake @@ -84,11 +84,16 @@ set(backup_CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS}) #clear it set(CPACK_INSTALL_CMAKE_PROJECTS "") -foreach(external_project ${external_projects}) - if(NOT USE_SYSTEM_${external_project} AND BUILD_SHARED_LIBS_${external_project}) - ExternalProject_Get_Property(${external_project} binary_dir) - set(CPACK_INSTALL_CMAKE_PROJECTS ${CPACK_INSTALL_CMAKE_PROJECTS} ${binary_dir} ${external_project} ALL "/") - endif() +foreach(external_project ${external_projects}) + if(NOT USE_SYSTEM_${external_project} + AND BUILD_SHARED_LIBS_${external_project} + AND DEFINED ${external_project}_ROOT) + install(CODE " + execute_process( + COMMAND ${CMAKE_COMMAND} --install ${${external_project}_ROOT} --prefix \"\${CMAKE_INSTALL_PREFIX}\" + ) + ") + endif() endforeach() foreach(dir ${PRIVATE_PLUGINS_DIRS}) From 8cf05fa07e76f023e111ed473dda46b3be15aec7 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 11:04:37 +0200 Subject: [PATCH 85/98] Fix python packaging (#1213) --- packaging/CMakeLists.txt | 10 ++ packaging/apple/mac_packager.sh.in | 23 ++++- packaging/windows/WindowsPackaging.cmake | 10 +- src/CMakeLists.txt | 2 - src/app/medInria/CMakeLists.txt | 38 ++++---- src/app/medInria/main.cpp | 85 ++++++++++++---- .../module/set_plugin_install_rules.cmake | 96 +++++++++++-------- superbuild/CMakeLists.txt | 2 +- superbuild/projects_modules/VTK.cmake | 7 +- superbuild/projects_modules/medInria.cmake | 5 +- superbuild/projects_modules/pyncpp.cmake | 9 +- 11 files changed, 196 insertions(+), 91 deletions(-) diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 68157eb3d6..5977d44a08 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -50,6 +50,16 @@ option(CPACK_SOURCE_ZIP "Enable to build ZIP source packages" OFF) set(CPACK_INSTALL_CMAKE_PROJECTS ${PROJECT_BINARY_DIR} ${PROJECT_NAME} ALL "/") +if(USE_Python) + set(PYTHON_PLUGINS_DIR "${medInria_ROOT}/bin/python_plugins") + make_directory("${PYTHON_PLUGINS_DIR}") + + install(DIRECTORY "${PYTHON_PLUGINS_DIR}" + DESTINATION bin + COMPONENT Runtime + ) +endif() + # Set cpack variables specific to the plateform if (WIN32) diff --git a/packaging/apple/mac_packager.sh.in b/packaging/apple/mac_packager.sh.in index 75256973e1..c9db3f10bb 100755 --- a/packaging/apple/mac_packager.sh.in +++ b/packaging/apple/mac_packager.sh.in @@ -31,9 +31,30 @@ for i in $*; do injectDirs="$injectDirs -inject-dir=$i" done +mkdir python_plugins +injectDirs="$injectDirs -inject-dir=@PROJECT_BINARY_DIR@/TmpInstall/python_plugins" + +python_libs='' +for python_lib in @medInria_ROOT@/bin/python_plugins/*; do + lib=${python_lib##*/} + libname=${lib%.so} + cp $python_lib python_plugins/$libname.dylib + python_libs="$python_libs $libname" +done + @dtk_ROOT@/bin/dtkDeploy medInria.app $injectDirs &>/dev/null -@CMAKE_COMMAND@ -DCMAKE_INSTALL_PREFIX:STRING=medInria.app/Contents/Resources -DCMAKE_INSTALL_COMPONENT:STRING=Python -P @pyncpp_ROOT@/cmake_install.cmake +mkdir medInria.app/Contents/Plugins/python +if [ -n "${python_libs}" ] +then + for python_lib in $python_libs; do + mv medInria.app/Contents/Plugins/$python_lib.dylib medInria.app/Contents/Plugins/python/$python_lib.so + done +fi + +@CMAKE_COMMAND@ --install @pyncpp_ROOT@ --prefix . --component Runtime +mkdir medInria.app/Contents/Resources/lib +mv lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@ medInria.app/Contents/Resources/lib #Run fancy packaging apple script diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index 0b8463977f..c2f4129414 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -130,8 +130,14 @@ foreach(file \${files}) endif() endforeach() -file(INSTALL ${MEDINRIA_FILES}/ DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ FILES_MATCHING PATTERN \"*${CMAKE_EXECUTABLE_SUFFIX}\") -file(INSTALL ${MEDINRIA_FILES}/ DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") +file(INSTALL ${MEDINRIA_FILES}/ + DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ + FILES_MATCHING + PATTERN \"*${CMAKE_EXECUTABLE_SUFFIX}\" + PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\" + PATTERN \"*.pyd\" + ) + file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qgif.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qicns.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qico.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a53c3f5639..1d93338e7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,8 +166,6 @@ if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") add_definitions(-DQT_NO_DEBUG) endif() -add_compile_definitions(USE_PYTHON) - ## ############################################################################# ## Windows specificity ## ############################################################################# diff --git a/src/app/medInria/CMakeLists.txt b/src/app/medInria/CMakeLists.txt index 42f80ade62..94a1b21f9c 100644 --- a/src/app/medInria/CMakeLists.txt +++ b/src/app/medInria/CMakeLists.txt @@ -154,29 +154,33 @@ target_link_libraries(${TARGET_NAME} if(USE_Python) target_link_libraries(${TARGET_NAME} ${pyncpp_CPP_API_LIBRARY}) - if(WIN32) - set(python_home ".") + if(APPLE) + set(python_home "../Resources") else() - if(APPLE) - set(python_home "../Resources/${pyncpp_PYTHON_INSTALL_DESTINATION}") - else() - set(python_home "../${pyncpp_PYTHON_INSTALL_DESTINATION}") - endif() + set(python_home "..") + endif() - get_filename_component(python_home_base "${python_home}" DIRECTORY) - get_filename_component(python_dir "${python_home}" NAME) + set(python_home "${python_home}/${pyncpp_PYTHON_INSTALL_DESTINATION}") - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "$/${python_home_base}" - ) + if(APPLE) + set(python_plugin_path "../Plugins/python") + else() + set(python_plugin_path "python_plugins") + endif() + + target_compile_definitions(${TARGET_NAME} PUBLIC + USE_PYTHON + PYTHON_HOME="${python_home}" + PYTHON_PLUGIN_PATH="${python_plugin_path}" + BUILD_PYTHON_HOME="${pyncpp_PYTHON_DIR}" + BUILD_PYTHON_PLUGIN_PATH="${CMAKE_BINARY_DIR}/bin/python_plugins" + ) - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink "${pyncpp_PYTHON_DIR}" "${python_dir}" - WORKING_DIRECTORY "$/${python_home_base}" + if(WIN32) + target_compile_definitions(${TARGET_NAME} PUBLIC + PLUGINS_LEGACY_PATH="plugins_legacy" ) endif() - - target_compile_definitions(${TARGET_NAME} PUBLIC PYTHON_HOME="${python_home}") endif() ## ############################################################################# diff --git a/src/app/medInria/main.cpp b/src/app/medInria/main.cpp index 8099143094..269d6404d9 100644 --- a/src/app/medInria/main.cpp +++ b/src/app/medInria/main.cpp @@ -19,7 +19,7 @@ #include #endif -#if(USE_PYTHON) +#ifdef USE_PYTHON #include #endif @@ -159,13 +159,31 @@ int main(int argc,char* argv[]) medDataManager::instance().setDatabaseLocation(); -#if(USE_PYTHON) +#ifdef USE_PYTHON pyncpp::Manager pythonManager; - QDir pythonHome = qApp->applicationDirPath(); + QString pythonHomePath = PYTHON_HOME; + QDir applicationPath = qApp->applicationDirPath(); + QDir pythonHome = applicationPath; QDir pythonPluginPath = pythonHome; + bool pythonHomeFound = false; + bool pythonPluginsFound = false; QString pythonErrorMessage; - if (!pythonHome.cd(PYTHON_HOME)) + if (pythonHome.cd(PYTHON_HOME)) + { + pythonHomeFound = true; + pythonPluginsFound = pythonPluginPath.cd(PYTHON_PLUGIN_PATH); + } + else + { + if (pythonHome.cd(BUILD_PYTHON_HOME)) + { + pythonHomeFound = true; + pythonPluginsFound = pythonPluginPath.cd(BUILD_PYTHON_PLUGIN_PATH); + } + } + + if (!pythonHomeFound) { pythonErrorMessage = "The embedded Python could not be found "; } @@ -177,21 +195,52 @@ int main(int argc,char* argv[]) } else { -#ifdef Q_OS_MACOS - if(!pythonPluginPath.cd("../Plugins/python")) + if (pythonPluginsFound) { - pythonPluginPath.cd("../../../plugins/python"); + try + { + pyncpp::Module sysModule = pyncpp::Module::import("sys"); + pyncpp::Object sysPath = sysModule.attribute("path"); + sysPath.append(pyncpp::Object(pythonPluginPath.absolutePath())); + qInfo() << "Added Python plugin path: " << pythonPluginPath.path(); + +#ifdef Q_OS_WIN + QDir sitePackages = pythonHome; + + if (sitePackages.cd("lib/site-packages")) + { + sysPath.append(pyncpp::Object(sitePackages.absolutePath())); + } + else + { + pythonErrorMessage = "Cannot find site directory."; + } + + pyncpp::Module osModule = pyncpp::Module::import("os"); + osModule.callMethod("add_dll_directory", applicationPath.absolutePath()); + qInfo() << "Added Python DLL path: " << applicationPath.path(); + + QDir pluginsLegacyPath = applicationPath; + + if (pluginsLegacyPath.cd(PLUGINS_LEGACY_PATH)) + { + osModule.callMethod("add_dll_directory", pluginsLegacyPath.absolutePath()); + qInfo() << "Added Python DLL path: " << pluginsLegacyPath.path(); + } + else + { + pythonErrorMessage = "Could not find legacy plugins path."; + } +#endif // Q_OS_WIN + } + catch (pyncpp::Exception& e) + { + pythonErrorMessage = e.what(); + } } -#else -#ifdef Q_OS_LINUX - pythonPluginPath.cd("../plugins/python"); -#endif -#endif - pyncpp::Module sysModule = pyncpp::Module::import("sys"); - sysModule.attribute("path").append(pyncpp::Object(pythonPluginPath.absolutePath())); } } -#endif +#endif // USE_PYTHON medPluginManager::instance().setVerboseLoading(true); medPluginManager::instance().initialize(); @@ -240,11 +289,11 @@ int main(int argc,char* argv[]) QGLFormat::setDefaultFormat(format); } -#if(USE_PYTHON) +#ifdef USE_PYTHON if(!pythonErrorMessage.isEmpty()) { - QMessageBox::warning(mainwindow, "Python", pythonErrorMessage); - qWarning() << pythonErrorMessage; + qWarning() << "(Python error) " << pythonErrorMessage; + QMessageBox::warning(mainwindow, "Python error", pythonErrorMessage); } #endif diff --git a/src/cmake/module/set_plugin_install_rules.cmake b/src/cmake/module/set_plugin_install_rules.cmake index 08f5b83245..834cc276ae 100644 --- a/src/cmake/module/set_plugin_install_rules.cmake +++ b/src/cmake/module/set_plugin_install_rules.cmake @@ -18,37 +18,46 @@ macro(set_plugin_install_rules target) ################################################################################ # -# Usage: set_plugin_install_rules(target) +# Usage: set_plugin_install_rules(target [PYTHON]) # set rules for the plugin designed by the target # ################################################################################ + +cmake_parse_arguments(ARG "PYTHON" "" "" ${ARGN}) + +if(ARG_PYTHON) + set(plugin_subdir python_plugins) +else() + set(plugin_subdir plugins) +endif() + get_property(GENERATOR_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(${GENERATOR_MULTI_CONFIG}) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/plugins ) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/plugins) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/plugins) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/plugins) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/plugins) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${plugin_subdir} ) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/plugins) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/plugins) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/plugins) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/plugins) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/plugins) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/plugins) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/plugins) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/plugins) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) else() - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/plugins) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/plugins) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/plugins) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${plugin_subdir}) endif() install(TARGETS ${target} - RUNTIME DESTINATION bin/plugins - LIBRARY DESTINATION bin/plugins + RUNTIME DESTINATION bin/${plugin_subdir} + LIBRARY DESTINATION bin/${plugin_subdir} ) endmacro() @@ -57,33 +66,42 @@ endmacro() ################################################################################ ############################## LEGACY ###################################### macro(set_plugin_install_rules_legacy_generic target dest) + +cmake_parse_arguments(ARG "PYTHON" "" "" ${ARGN}) + +if(ARG_PYTHON) + set(plugin_subdir python_plugins) +else() + set(plugin_subdir plugins_legacy) +endif() + get_property(GENERATOR_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(${GENERATOR_MULTI_CONFIG}) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/plugins_legacy) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/plugins_legacy) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/plugins_legacy) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${dest}/${platformType}Debug/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${dest}/${platformType}Release/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL ${dest}/${platformType}MinSizeRel/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${dest}/${platformType}RelWithDebInfo/bin/${plugin_subdir}) else() - set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${dest}/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${dest}/bin/plugins_legacy) - set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${dest}/bin/plugins_legacy) + set_target_properties( ${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${dest}/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${dest}/bin/${plugin_subdir}) + set_target_properties( ${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${dest}/bin/${plugin_subdir}) endif() install(TARGETS ${target} - RUNTIME DESTINATION bin/plugins_legacy - LIBRARY DESTINATION bin/plugins_legacy - FRAMEWORK DESTINATION bin/plugins_legacy + RUNTIME DESTINATION bin/${plugin_subdir} + LIBRARY DESTINATION bin/${plugin_subdir} + FRAMEWORK DESTINATION bin/${plugin_subdir} RESOURCE DESTINATION resources/${target} ) endmacro() @@ -95,7 +113,7 @@ endmacro() # ################################################################################ macro(set_plugin_install_rules_legacy target) - set_plugin_install_rules_legacy_generic(${target} ${CMAKE_BINARY_DIR}) + set_plugin_install_rules_legacy_generic(${target} ${CMAKE_BINARY_DIR} ${ARGN}) endmacro() ################################################################################ @@ -105,5 +123,5 @@ endmacro() # ################################################################################ macro(set_plugin_install_rules_legacy_external target) - set_plugin_install_rules_legacy_generic(${target} ${medInria_ROOT}) + set_plugin_install_rules_legacy_generic(${target} ${medInria_ROOT} ${ARGN}) endmacro() diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index 83e01f29ff..1ba11f668d 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -32,7 +32,7 @@ find_package(Boost REQUIRED) if(USE_Python AND UNIX) if(APPLE) - set(default_root_dir "/usr/local/opt/openssl@1.1") + set(default_root_dir "/usr/local/opt/openssl") else() set(default_root_dir) endif() diff --git a/superbuild/projects_modules/VTK.cmake b/superbuild/projects_modules/VTK.cmake index 3f75156894..43f53d46dc 100644 --- a/superbuild/projects_modules/VTK.cmake +++ b/superbuild/projects_modules/VTK.cmake @@ -118,13 +118,16 @@ if(${USE_FFmpeg}) endif() if(USE_Python) - set(python_version "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") if(UNIX) + set(python_version "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") set(python_executable "${pyncpp_ROOT}/lib/python${python_version}/bin/python${python_version}") set(python_include "${pyncpp_ROOT}/lib/python${python_version}/include/python${python_version}") set(python_library "${pyncpp_ROOT}/lib/python${python_version}/lib/libpython${python_version}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() - # TODO + set(python_version "${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}") + set(python_executable "${pyncpp_ROOT}/python${python_version}/pythonw$<$:_d>.exe") + set(python_include "${pyncpp_ROOT}/python${python_version}/include") + set(python_library "${pyncpp_ROOT}/python${python_version}/libs/python${python_version}$<$:_d>.lib") endif() list(APPEND cmake_args -DVTK_WRAP_PYTHON:BOOL=ON diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index 5c36726cfb..9fdf9b984b 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -159,9 +159,8 @@ if (WIN32) COMMAND for %%I in ( ${DTK_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) COMMAND for %%I in ( ${DCM_BIN_BASE}\\bin\\${CONFIG_MODE}\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) COMMAND for %%I in ( ${QT5_BIN_BASE}\\bin\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - COMMAND for %%I in ( ${PYTHON_BIN_BASE}\\bin\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - COMMAND for %%I in ( ${PYTHON_BIN_BASE}\\bin\\DLLs ${PYTHON_BIN_BASE}\\bin\\Lib ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /d /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /d /H ${MED_BIN_BASE}\\%%~nxI %%~fI) - ) + COMMAND for %%I in ( ${PYNCPP_BIN_BASE}\\bin\\*.dll ) do (if EXIST ${MED_BIN_BASE}\\%%~nxI (del /S ${MED_BIN_BASE}\\%%~nxI & mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) else mklink /H ${MED_BIN_BASE}\\%%~nxI %%~fI) + ) endif() diff --git a/superbuild/projects_modules/pyncpp.cmake b/superbuild/projects_modules/pyncpp.cmake index f8240ce2d2..d96900aa04 100644 --- a/superbuild/projects_modules/pyncpp.cmake +++ b/superbuild/projects_modules/pyncpp.cmake @@ -31,15 +31,17 @@ function(pyncpp_project) set(project_args GIT_REPOSITORY ${GITHUB_PREFIX}LIRYC-IHU/pyncpp.git - GIT_TAG origin/working + GIT_TAG working GIT_SHALLOW True GIT_PROGRESS True ) set(cmake_args + ${ep_common_cache_args} -D "PYNCPP_PYTHON_VERSION_MAJOR:STRING=${PYTHON_VERSION_MAJOR}" -D "PYNCPP_PYTHON_VERSION_MINOR:STRING=${PYTHON_VERSION_MINOR}" -D "PYNCPP_PYTHON_VERSION_PATCH:STRING=${PYTHON_VERSION_PATCH}" + -D "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_externals_projects}" ) if(UNIX) @@ -47,11 +49,6 @@ function(pyncpp_project) -D Qt5_DIR:PATH=${Qt5_DIR} -D OPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR} ) - if(APPLE) - list(APPEND cmake_args - -D CMAKE_MACOSX_RPATH:BOOL=OFF - ) - endif() endif() ## ##################################################################### From 85d71d163ffde53f209deab14cec22de184abbda Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 11:04:48 +0200 Subject: [PATCH 86/98] Place superbuild options earlier (#1215) --- superbuild/CMakeLists.txt | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index 1ba11f668d..ec6036996e 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -11,6 +11,20 @@ # ################################################################################ +## ############################################################################# +## Options +## ############################################################################# + +option(USE_DTKIMAGING "Use DTK imaging layer" OFF ) + +option(USE_OSPRay "Use OSPRay for CPU VR" OFF ) + +option(USE_Python "Build with Python support" ON) + +if (NOT WIN32) + option(USE_FFmpeg "Build with FFmpeg video export support" OFF) +endif() + ## ############################################################################# ## Add packages ## ############################################################################# @@ -53,14 +67,7 @@ endif() # * otherwise use download and compile locally the package as an external # module. -option(USE_DTKIMAGING "Use DTK imaging layer" OFF ) - -option(USE_OSPRay "Use OSPRay for CPU VR" OFF ) - -option(USE_Python "Build with Python support" ON) - if (NOT WIN32) - option(USE_FFmpeg "Build with FFmpeg video export support" OFF) if (${USE_FFmpeg}) list(APPEND external_projects ffmpeg) endif() From d5a25cb1bf00642a9e5aabbf52d47db3908d079f Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:42:52 +0200 Subject: [PATCH 87/98] [PolygonRoi] clear deactivating the tlbx (#812) (#1223) Co-authored-by: mathildemerle --- .../toolboxes/polygonRoiToolBox.cpp | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp index f6832620db..79a7a8e90c 100644 --- a/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp +++ b/src/plugins/legacy/polygonRoi/toolboxes/polygonRoiToolBox.cpp @@ -386,13 +386,8 @@ void polygonRoiToolBox::clickClosePolygon(bool state) { if (!state) { - pMedToolBox->hide(); - for (baseViewEvent *event : viewEventHash.values()) - { - event->removeViewInteractor(); - disconnect(event->getCurrentView(), SIGNAL(selectedRequest(bool)), this, SLOT(onDataIndexActivated())); - disconnect(event->getCurrentView(), SIGNAL(layerRemoved(medAbstractData *)), this, SLOT(onLayerRemoved(medAbstractData *))); - } + clear(); + updateView(); } else { @@ -413,13 +408,13 @@ void polygonRoiToolBox::clickClosePolygon(bool state) { viewEventHash.values().first()->getCurrentView()->selectedRequest(true); } + saveBinaryMaskButton->setEnabled(state); + saveContourButton->setEnabled(state); + saveLabel->setEnabled(state); + interpolate->setEnabled(state); + repulsorTool->setEnabled(state); + repulsorLabel->setEnabled(state); } - saveBinaryMaskButton->setEnabled(state); - saveContourButton->setEnabled(state); - saveLabel->setEnabled(state); - interpolate->setEnabled(state); - repulsorTool->setEnabled(state); - repulsorLabel->setEnabled(state); } void polygonRoiToolBox::activateRepulsor(bool state) From cc8485eaf21aee48b41349cc333589b4ce40dd79 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:43:04 +0200 Subject: [PATCH 88/98] [VoiCutter] solve keys, min and max intensity, and add waiting toolbox (#829) (#1222) * {VoiCutter} keySym instead of keyCode and wait status * [VoiCutter] solve min and max intensity and keys on mac & linux Co-authored-by: mathildemerle --- .../legacy/voiCutter/voiCutterToolBox.cpp | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp index 40affe00de..36710a3643 100644 --- a/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp +++ b/src/plugins/legacy/voiCutter/voiCutterToolBox.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -55,8 +56,9 @@ class voiCutterToolBoxPrivate medAbstractBoolParameterL *orientation3DParam; // Rendering parameters - QHash windowingParams; QString lut; + double minIntensity; + double maxIntensity; }; class vtkCutterObserver : public vtkCommand @@ -77,22 +79,22 @@ class vtkCutterObserver : public vtkCommand vtkRenderWindowInteractor *iren = static_cast (tb->d->currentView->backend())->renWin->GetInteractor(); - char keycode = iren->GetKeyCode(); - - // enter => keep only VOI defined - // tab => restore VOI defined - // backspace => delete VOI defined - switch(keycode) + auto key = iren->GetKeySym(); + + if ((key == vtkStdString("Delete")) || (key == vtkStdString("BackSpace"))) { - case '\b': // backspace - tb->launchTheCutting(Remove); - break; - case '\r': // return - tb->launchTheCutting(Keep); - break; - case '\t': // tab - tb->launchTheCutting(Restore); - break; + // Delete VOI defined + tb->launchTheCutting(Remove); + } + else if (key == vtkStdString("Return")) + { + // Keep only VOI defined + tb->launchTheCutting(Keep); + } + else if (key == vtkStdString("Tab")) + { + // Restore VOI defined + tb->launchTheCutting(Restore); } } @@ -155,6 +157,9 @@ voiCutterToolBox::voiCutterToolBox(QWidget *parent) : d->orientation3DParam = 0; activateButtons(false); + + d->minIntensity = std::numeric_limits::min(); + d->maxIntensity = std::numeric_limits::max(); } voiCutterToolBox::~voiCutterToolBox() @@ -404,11 +409,15 @@ void voiCutterToolBox::launchTheCutting(MODE m) { if (d->currentView && d->scissorOn) { + this->setToolBoxOnWaitStatusForNonRunnableProcess(); + vtkImageView3D *view3D = static_cast(d->currentView->backend())->view3D; std::vector polygonPoints = static_cast(view3D->GetInteractor()->GetInteractorStyle())->GetPolygonPoints(); definePolygonsImage(polygonPoints, m); d->interactorStyleDrawPolygon->ResetPolygon(); view3D->GetInteractor()->SetInteractorStyle(d->interactorStyleDrawPolygon); + + this->setToolBoxOnReadyToUse(); } } @@ -428,18 +437,17 @@ void voiCutterToolBox::saveRenderingParameters() { d->lut = qobject_cast(parameters[j])->value(); } + else if (parameters[j]->name() == "Min Intensity") + { + d->minIntensity = qobject_cast(parameters[j])->value(); + } + else if (parameters[j]->name() == "Max Intensity") + { + d->maxIntensity = qobject_cast(parameters[j])->value(); + } } } } - - // Synchronize Window/Level - medCompositeParameterL *windowLevelParameter1 = d->currentView->windowLevelParameter(0); - QList windowLevel = windowLevelParameter1->values(); - if (windowLevel.size() == 2) - { - d->windowingParams.insert("Level", windowLevel[0]); - d->windowingParams.insert("Window", windowLevel[1]); - } } void voiCutterToolBox::applyRenderingParameters() @@ -457,12 +465,17 @@ void voiCutterToolBox::applyRenderingParameters() { qobject_cast(parameters[j])->setValue(d->lut); } + else if (parameters[j]->name() == "Min Intensity") + { + qobject_cast(parameters[j])->setValue(d->minIntensity); + } + else if (parameters[j]->name() == "Max Intensity") + { + qobject_cast(parameters[j])->setValue(d->maxIntensity); + } } } } - - medCompositeParameterL *windowLevelParameter = d->currentView->windowLevelParameter(0); - windowLevelParameter->setValues(d->windowingParams); } // intersect3D_SegmentPlane(): intersect a segment and a plane From a52b3f89cbed80ae5fb1ce50e8f452c97a1c6dac Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:43:38 +0200 Subject: [PATCH 89/98] added a patch for ITK to compile on visual studio 2019 (#1221) --- superbuild/patches/ITK_Win.patch | 37 +++++++++++++++++++++++++++ superbuild/projects_modules/ITK.cmake | 6 ++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 superbuild/patches/ITK_Win.patch diff --git a/superbuild/patches/ITK_Win.patch b/superbuild/patches/ITK_Win.patch new file mode 100644 index 0000000000..fce4052e27 --- /dev/null +++ b/superbuild/patches/ITK_Win.patch @@ -0,0 +1,37 @@ +diff --git a/Modules/ThirdParty/KWSys/src/CMakeLists.txt b/Modules/ThirdParty/KWSys/src/CMakeLists.txt +index 3c806d816d..6096ec57df 100644 +--- a/Modules/ThirdParty/KWSys/src/CMakeLists.txt ++++ b/Modules/ThirdParty/KWSys/src/CMakeLists.txt +@@ -24,7 +24,9 @@ set(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT Development) + set(KWSYS_PROPERTIES_C WINDOWS_EXPORT_ALL_SYMBOLS FALSE) + set( _macosx_rpath 1) + if(DEFINED CMAKE_MACOSX_RPATH AND NOT "${CMAKE_MACOSX_RPATH}" STREQUAL "") +- set(_macosx_rpath ${CMAKE_MACOSX_RPATH}) ++ if (NOT CMAKE_MACOSX_RPATH) ++ set(_macosx_rpath 0) ++ endif() + endif() + set(KWSYS_PROPERTIES_CXX MACOSX_RPATH ${_macosx_rpath} WINDOWS_EXPORT_ALL_SYMBOLS FALSE) + +diff --git a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h +index e75a220d4d..60d185e233 100644 +--- a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h ++++ b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h +@@ -92,7 +92,7 @@ Most compilers implement their own version of this keyword ... + + /* MSVC 64bits doesn't support _asm */ + #if !defined(_WIN64) +-static INLINE long lrintf(float f){ ++static INLINE long opj_lrintf(float f){ + int i; + + _asm{ +@@ -103,7 +103,7 @@ static INLINE long lrintf(float f){ + return i; + } + #else +-static INLINE long lrintf(float x){ ++static INLINE long opj_lrintf(float x){ + long r; + if (x>=0.f) + { diff --git a/superbuild/projects_modules/ITK.cmake b/superbuild/projects_modules/ITK.cmake index 6d33a0eeed..b3a6e8a4cb 100644 --- a/superbuild/projects_modules/ITK.cmake +++ b/superbuild/projects_modules/ITK.cmake @@ -75,7 +75,11 @@ set(cmake_args ## Check if patch has to be applied ## ############################################################################# -ep_GeneratePatchCommand(${ep} ${ep}_PATCH_COMMAND ITK_Mac.patch) +if (WIN32 AND ${CMAKE_GENERATOR} STREQUAL "Visual Studio 16 2019") + ep_GeneratePatchCommand(${ep} ${ep}_PATCH_COMMAND ITK_Win.patch) +elseif(APPLE) + ep_GeneratePatchCommand(${ep} ${ep}_PATCH_COMMAND ITK_Mac.patch) +endif() ## ############################################################################# ## Add external-project From c28eb649a07acd79605d03e732e96f9eb9cc7efa Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:44:25 +0200 Subject: [PATCH 90/98] Remove BUILD_ALWAYS from external projects (#1220) --- superbuild/projects_modules/DCMTK.cmake | 1 - superbuild/projects_modules/ITK.cmake | 1 - superbuild/projects_modules/LogDemons.cmake | 1 - superbuild/projects_modules/QtDCM.cmake | 1 - superbuild/projects_modules/RPI.cmake | 1 - superbuild/projects_modules/TTK.cmake | 1 - superbuild/projects_modules/VTK.cmake | 1 - superbuild/projects_modules/dtk.cmake | 1 - superbuild/projects_modules/dtkImaging.cmake | 1 - 9 files changed, 9 deletions(-) diff --git a/superbuild/projects_modules/DCMTK.cmake b/superbuild/projects_modules/DCMTK.cmake index c1683fd971..c5cb9278e3 100644 --- a/superbuild/projects_modules/DCMTK.cmake +++ b/superbuild/projects_modules/DCMTK.cmake @@ -115,7 +115,6 @@ ExternalProject_Add(${ep} CMAKE_ARGS ${cmake_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/ITK.cmake b/superbuild/projects_modules/ITK.cmake index b3a6e8a4cb..dfacc24435 100644 --- a/superbuild/projects_modules/ITK.cmake +++ b/superbuild/projects_modules/ITK.cmake @@ -103,7 +103,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/LogDemons.cmake b/superbuild/projects_modules/LogDemons.cmake index e3b369ced5..8670dfd4d2 100644 --- a/superbuild/projects_modules/LogDemons.cmake +++ b/superbuild/projects_modules/LogDemons.cmake @@ -94,7 +94,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/QtDCM.cmake b/superbuild/projects_modules/QtDCM.cmake index 073470080c..c485b86ad9 100644 --- a/superbuild/projects_modules/QtDCM.cmake +++ b/superbuild/projects_modules/QtDCM.cmake @@ -104,7 +104,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/RPI.cmake b/superbuild/projects_modules/RPI.cmake index b5cd1616ab..2d95479073 100644 --- a/superbuild/projects_modules/RPI.cmake +++ b/superbuild/projects_modules/RPI.cmake @@ -94,7 +94,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 0 ) ## ############################################################################# diff --git a/superbuild/projects_modules/TTK.cmake b/superbuild/projects_modules/TTK.cmake index 75a612c188..f35768b003 100644 --- a/superbuild/projects_modules/TTK.cmake +++ b/superbuild/projects_modules/TTK.cmake @@ -95,7 +95,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/VTK.cmake b/superbuild/projects_modules/VTK.cmake index 43f53d46dc..c3ee974756 100644 --- a/superbuild/projects_modules/VTK.cmake +++ b/superbuild/projects_modules/VTK.cmake @@ -166,7 +166,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/dtk.cmake b/superbuild/projects_modules/dtk.cmake index 1d7f261056..5894b1ef41 100644 --- a/superbuild/projects_modules/dtk.cmake +++ b/superbuild/projects_modules/dtk.cmake @@ -105,7 +105,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# diff --git a/superbuild/projects_modules/dtkImaging.cmake b/superbuild/projects_modules/dtkImaging.cmake index 0819ce8972..c9f27d1bdf 100644 --- a/superbuild/projects_modules/dtkImaging.cmake +++ b/superbuild/projects_modules/dtkImaging.cmake @@ -94,7 +94,6 @@ ExternalProject_Add(${ep} CMAKE_CACHE_ARGS ${cmake_cache_args} DEPENDS ${${ep}_dependencies} INSTALL_COMMAND "" - BUILD_ALWAYS 1 ) ## ############################################################################# From 9c777e164012c3a02589e0d6c2532c5ab6208616 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:44:39 +0200 Subject: [PATCH 91/98] Use DICOM Ids to identify patients and studies (#1219) --- .../database/medDatabaseImporter.cpp | 90 ++++++++++++++++--- .../medDatabaseNonPersistentImporter.cpp | 12 ++- .../medDatabasePersistentController.cpp | 29 ++++++ .../medDatabasePersistentController.h | 1 + 4 files changed, 116 insertions(+), 16 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp index 7d2b09039b..7a4d169e89 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseImporter.cpp @@ -120,9 +120,38 @@ int medDatabaseImporter::getOrCreatePatient ( const medAbstractData* medData, QS QString birthDate = medMetaDataKeys::BirthDate.getFirstValue(medData); QString patientId = medMetaDataKeys::PatientID.getFirstValue(medData); - query.prepare ( "SELECT id FROM patient WHERE name = :name AND birthdate = :birthdate" ); - query.bindValue ( ":name", patientName ); - query.bindValue ( ":birthdate", birthDate ); + QString queryText = "SELECT id FROM patient WHERE patientId = :patientId"; + + if (patientName.isEmpty()) + { + queryText += " AND (name = '' OR name IS NULL)"; + } + else + { + queryText += " AND name = :patientName"; + } + + if (birthDate.isEmpty()) + { + queryText += " AND (birthdate = '' OR birthdate IS NULL)"; + } + else + { + queryText += " AND birthdate = :birthdate"; + } + + query.prepare(queryText); + query.bindValue(":patientId", patientId); + + if (!patientName.isEmpty()) + { + query.bindValue(":patientName", patientName); + } + + if (!birthDate.isEmpty()) + { + query.bindValue(":birthdate", birthDate); + } QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); @@ -137,13 +166,12 @@ int medDatabaseImporter::getOrCreatePatient ( const medAbstractData* medData, QS } else { - QString birthdate = medMetaDataKeys::BirthDate.getFirstValue(medData); - QString gender = medMetaDataKeys::Gender.getFirstValue(medData); + QString gender = medMetaDataKeys::Gender.getFirstValue(medData); query.prepare ( "INSERT INTO patient (name, thumbnail, birthdate, gender, patientId) VALUES (:name, :thumbnail, :birthdate, :gender, :patientId)" ); query.bindValue ( ":name", patientName ); query.bindValue ( ":thumbnail", QString("") ); - query.bindValue ( ":birthdate", birthdate ); + query.bindValue ( ":birthdate", birthDate ); query.bindValue ( ":gender", gender ); query.bindValue ( ":patientId", patientId); query.exec(); @@ -168,19 +196,55 @@ int medDatabaseImporter::getOrCreateStudy ( const medAbstractData* medData, QSql QString studyName = medMetaDataKeys::StudyDescription.getFirstValue(medData).simplified(); QString studyUid = medMetaDataKeys::StudyInstanceUID.getFirstValue(medData); QString studyId = medMetaDataKeys::StudyID.getFirstValue(medData); - QString seriesName = medMetaDataKeys::SeriesDescription.getFirstValue(medData).simplified(); QString studyTime = medMetaDataKeys::StudyTime.getFirstValue(medData); QString studyDate = medMetaDataKeys::StudyDate.getFirstValue(medData); - if( studyName=="EmptyStudy" && seriesName=="EmptySeries" ) + QString queryText = "SELECT id FROM study WHERE patient = :patient"; + + if (studyUid.isEmpty()) + { + queryText += " AND (uid = '' OR uid IS NULL)"; + } + else + { + queryText += " AND uid = :studyUid"; + } + + if (studyId.isEmpty()) + { + queryText += " AND (studyId = '' OR studyId IS NULL)"; + } + else { - return studyDbId; + queryText += " AND studyId = :studyId"; } - query.prepare ( "SELECT id FROM study WHERE patient = :patient AND name = :studyName AND uid = :studyUid" ); - query.bindValue ( ":patient", patientDbId ); - query.bindValue ( ":studyName", studyName ); - query.bindValue ( ":studyUid", studyUid ); + if (studyName.isEmpty()) + { + queryText += " AND (name = '' OR name IS NULL)"; + } + else + { + queryText += " AND name = :studyName"; + } + + query.prepare(queryText); + query.bindValue(":patient", patientDbId); + + if(!studyUid.isEmpty()) + { + query.bindValue(":studyUid", studyUid); + } + + if (!studyId.isEmpty()) + { + query.bindValue(":studyId", studyId); + } + + if (!studyName.isEmpty()) + { + query.bindValue(":studyName", studyName); + } QMutexLocker mutexLocker(&medDataManager::instance().controller()->getDatabaseMutex()); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp index f199adfedf..2569a5f22c 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabaseNonPersistentImporter.cpp @@ -89,7 +89,13 @@ medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbn QString birthdate = medMetaDataKeys::BirthDate.getFirstValue(data); // check if patient is already in the persistent database - medDataIndex databaseIndex = medDataManager::instance().controller()->indexForPatient(patientName); + medDataIndex databaseIndex = medDataManager::instance().controller()->indexForPatientID(patientId); + + if (!databaseIndex.isValidForPatient()) + { + databaseIndex = medDataManager::instance().controller()->indexForPatient(patientName); + } + medDatabaseNonPersistentItem *patientItem = nullptr; if ( databaseIndex.isValid() ) @@ -101,8 +107,8 @@ medDataIndex medDatabaseNonPersistentImporter::populateDatabaseAndGenerateThumbn { // check if patient is already in the non persistent database for ( int i=0; iname() ==patientName ) - if ( medMetaDataKeys::PatientName.getFirstValue(items[i]->data()) == patientName ) + if ((!patientId.isEmpty() && medMetaDataKeys::PatientID.getFirstValue(items[i]->data()) == patientId) + || medMetaDataKeys::PatientName.getFirstValue(items[i]->data()) == patientName) { patientDbId = items[i]->index().patientId(); patientItem = items[i]; diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp index 4eb4db0260..45772ed64f 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.cpp @@ -326,6 +326,35 @@ medDataIndex medDatabasePersistentController::indexForPatient( return medDataIndex(); } +medDataIndex medDatabasePersistentController::indexForPatientID(const QString& patientId) +{ + medDataIndex dataIndex; + + if (!patientId.isEmpty()) + { + QSqlDatabase dbConnection = getThreadSpecificConnection(); + QSqlQuery query(dbConnection); + + query.prepare("SELECT id FROM patient WHERE patientId = :patientId"); + query.bindValue(":patientId", patientId); + + QMutexLocker mutexLocker(&getDatabaseMutex()); + + if (!execQuery(query, __FILE__, __LINE__)) + { + qDebug() << DTK_COLOR_FG_RED << query.lastError() << DTK_NO_COLOR; + } + + if (query.first()) + { + QVariant patientDbId = query.value(0); + dataIndex = medDataIndex::makePatientIndex(this->dataSourceId(), patientDbId.toInt()); + } + } + + return dataIndex; +} + medDataIndex medDatabasePersistentController::indexForStudy(int id) { QSqlDatabase dbConnection = getThreadSpecificConnection(); diff --git a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h index 1ce5eb92a7..e0052da383 100644 --- a/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h +++ b/src/layers/legacy/medCoreLegacy/database/medDatabasePersistentController.h @@ -43,6 +43,7 @@ class MEDCORELEGACY_EXPORT medDatabasePersistentController : public medAbstractD medDataIndex indexForSeries(int id); medDataIndex indexForPatient(const QString &patientName); + medDataIndex indexForPatientID(const QString &patientId); medDataIndex indexForStudy(const QString &patientName, const QString &studyName); medDataIndex indexForStudyUID(const QString &patientName, From 5e4208b2cdb411f026d6aa0716dd1090a3ba31af Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:44:52 +0200 Subject: [PATCH 92/98] Add development settings (#1218) --- src/app/medInria/medApplication.cpp | 2 + .../medDevelopmentSettingsWidget.cpp | 66 +++++++++++++++++++ .../medDevelopmentSettingsWidget.h | 37 +++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.cpp create mode 100644 src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.h diff --git a/src/app/medInria/medApplication.cpp b/src/app/medInria/medApplication.cpp index c8ff8ae130..c65f5f35d9 100644 --- a/src/app/medInria/medApplication.cpp +++ b/src/app/medInria/medApplication.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,7 @@ void medApplication::initialize() medSettingsWidgetFactory* settingsWidgetFactory = medSettingsWidgetFactory::instance(); settingsWidgetFactory->registerSettingsWidget(); settingsWidgetFactory->registerSettingsWidget(); + settingsWidgetFactory->registerSettingsWidget(); //Register annotations medAbstractDataFactory * datafactory = medAbstractDataFactory::instance(); diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.cpp b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.cpp new file mode 100644 index 0000000000..45042b8499 --- /dev/null +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.cpp @@ -0,0 +1,66 @@ +/*============================================================================== + + medInria + + Copyright (c) INRIA 2024. All rights reserved. + See LICENSE.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. + +==============================================================================*/ + +#include "medDevelopmentSettingsWidget.h" + +#include + +#include +#include +#include +#include + +class medDevelopmentSettingsWidgetPrivate +{ +public: + QCheckBox* developerModeCheckBox; +}; + +medDevelopmentSettingsWidget::medDevelopmentSettingsWidget(QWidget *parent) : + medSettingsWidget(parent), d(new medDevelopmentSettingsWidgetPrivate()) +{ + setTabName("Development"); + + QFormLayout* formLayout = new QFormLayout(); + formLayout->setSizeConstraint(QLayout::SetFixedSize); + formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->addLayout(formLayout); + + d->developerModeCheckBox = new QCheckBox("Enable developer mode"); + formLayout->addRow(d->developerModeCheckBox); + + QLabel* label = new QLabel("Enabling developer mode will make various " + "development-specific options visible throughout " + "the application."); + formLayout->addRow(label); + + setLayout(mainLayout); +} + +bool medDevelopmentSettingsWidget::write() +{ + medSettingsManager::instance().setValue("development", "developer_mode", d->developerModeCheckBox->isChecked()); + return true; +} + +void medDevelopmentSettingsWidget::read() +{ + d->developerModeCheckBox->setChecked(medSettingsManager::instance().value("development", "developer_mode").toBool()); +} + +bool medDevelopmentSettingsWidget::validate() +{ + return true; +} diff --git a/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.h b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.h new file mode 100644 index 0000000000..ae638df5c1 --- /dev/null +++ b/src/layers/legacy/medCoreLegacy/gui/settingsWidgets/medDevelopmentSettingsWidget.h @@ -0,0 +1,37 @@ +#pragma once + +/*============================================================================== + + medInria + + Copyright (c) INRIA 2024. All rights reserved. + See LICENSE.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. + +==============================================================================*/ + +#include +#include + +class medDevelopmentSettingsWidgetPrivate; + +class MEDCORELEGACY_EXPORT medDevelopmentSettingsWidget : public medSettingsWidget +{ + Q_OBJECT + MED_SETTINGS_INTERFACE("Development", "Development Settings") + +public: + medDevelopmentSettingsWidget(QWidget* parent = nullptr); + + virtual bool write(); + +public slots: + virtual void read(); + virtual bool validate(); + +private: + medDevelopmentSettingsWidgetPrivate *d; +}; From f33f86bec597f58a03be2897bc26b9cda9d602e4 Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:45:10 +0200 Subject: [PATCH 93/98] Order of settings widgets (#1217) --- .../factories/medSettingsWidgetFactory.cpp | 55 +++++++++++++++---- .../gui/factories/medSettingsWidgetFactory.h | 9 +-- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.cpp b/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.cpp index 50ad02be4a..04b6576797 100644 --- a/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.cpp +++ b/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.cpp @@ -18,7 +18,7 @@ class medSettingsWidgetFactoryPrivate { public: - medSettingsWidgetFactory::medSettingsWidgetCreatorHash creators; + medSettingsWidgetFactory::medSettingsWidgetCreatorList creators; }; /** @@ -40,15 +40,27 @@ bool medSettingsWidgetFactory::registerSettingsWidget(const QString& type, QString description, medSettingsWidgetCreator func) { - if(!d->creators.contains(type)) + bool typeFound = false; + + foreach(medSettingDetails* creatorDetails, d->creators) + { + if(creatorDetails->type == type) + { + typeFound = true; + break; + } + } + + if(!typeFound) { - medSettingDetails* holder = new medSettingDetails(name, + medSettingDetails* holder = new medSettingDetails(type, + name, description, func); - d->creators.insert( type, - holder); + d->creators.append(holder); return true; } + return false; } @@ -59,7 +71,14 @@ bool medSettingsWidgetFactory::registerSettingsWidget(const QString& type, */ QList medSettingsWidgetFactory::settingsWidgets(void) { - return d->creators.keys(); + QList typeNames; + + foreach(medSettingDetails* creatorDetails, d->creators) + { + typeNames.append(creatorDetails->type); + } + + return typeNames; } /** @@ -70,12 +89,16 @@ QList medSettingsWidgetFactory::settingsWidgets(void) */ medSettingsWidget *medSettingsWidgetFactory::createSettingsWidget(QString type,QWidget * parent) { - if(!d->creators.contains(type)) - return nullptr; - - medSettingsWidget *conf = d->creators[type]->creator(parent); + foreach(medSettingDetails* creatorDetails, d->creators) + { + if(creatorDetails->type == type) + { + medSettingsWidget* widget = creatorDetails->creator(parent); + return widget; + } + } - return conf; + return nullptr; } /** @@ -85,7 +108,15 @@ medSettingsWidget *medSettingsWidgetFactory::createSettingsWidget(QString type,Q medSettingDetails * medSettingsWidgetFactory::settingDetailsFromId( const QString &id) const { - return d->creators.value(id); + foreach(medSettingDetails* creatorDetails, d->creators) + { + if(creatorDetails->type == id) + { + return creatorDetails; + } + } + + return nullptr; } /** diff --git a/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.h b/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.h index 956ffcbfc4..eec7fc71b8 100644 --- a/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.h +++ b/src/layers/legacy/medCoreLegacy/gui/factories/medSettingsWidgetFactory.h @@ -33,8 +33,8 @@ class MEDCORELEGACY_EXPORT medSettingsWidgetFactory : public dtkAbstractFactory //! This function pointer designates functions allocating memory typedef medSettingsWidget*(*medSettingsWidgetCreator)(QWidget *); - //! Type designating the internal has table containing the creator functions. - typedef QHash medSettingsWidgetCreatorHash; + //! Type designating the internal list containing the creator functions. + typedef QList medSettingsWidgetCreatorList; static medSettingsWidgetFactory * instance(); /** @@ -96,12 +96,13 @@ public slots: * */ struct MEDCORELEGACY_EXPORT medSettingDetails{ + QString type; QString name; /** Readable name*/ QString description; /** (tooltip) short description */ medSettingsWidgetFactory::medSettingsWidgetCreator creator; /** function pointer allocating memory for the widget*/ - medSettingDetails(QString name,QString description, + medSettingDetails(QString type, QString name,QString description, medSettingsWidgetFactory::medSettingsWidgetCreator creator): - name(name),description(description), + type(type), name(name),description(description), creator(creator){} }; From 314a6193dbc05ffc687f924f9cf342bf3b5dbd6b Mon Sep 17 00:00:00 2001 From: fcollot Date: Fri, 5 Apr 2024 14:45:26 +0200 Subject: [PATCH 94/98] Adapt ep initialization macro for non-cmake projects (#1214) --- .../EP_Initialisation.cmake | 124 +++++++++--------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/superbuild/external_projects_tools/EP_Initialisation.cmake b/superbuild/external_projects_tools/EP_Initialisation.cmake index bba3f35a06..a454b6f23d 100644 --- a/superbuild/external_projects_tools/EP_Initialisation.cmake +++ b/superbuild/external_projects_tools/EP_Initialisation.cmake @@ -11,11 +11,30 @@ # ################################################################################ -macro(ep_Initialisation ep - USE_SYSTEM use_system_def - BUILD_SHARED_LIBS build_shared_libs_def - REQUIRED_FOR_PLUGINS required_for_plugins - ) +macro(ep_Initialisation ep) + +cmake_parse_arguments(ep_Initialisation + "NO_CMAKE_PACKAGE" + "USE_SYSTEM;BUILD_SHARED_LIBS;REQUIRED_FOR_PLUGINS;PACKAGE_NAME" + "" + ${ARGN} + ) + +if (NOT ep_Initialisation_USE_SYSTEM) + set(ep_Initialisation_USE_SYSTEM OFF) +endif() + +if (NOT ep_Initialisation_BUILD_SHARED_LIBS) + set(ep_Initialisation_BUILD_SHARED_LIBS ON) +endif() + +if (NOT ep_Initialisation_REQUIRED_FOR_PLUGINS) + set(ep_Initialisation_REQUIRED_FOR_PLUGINS OFF) +endif() + +if (NOT ep_Initialisation_PACKAGE_NAME) + set(ep_Initialisation_PACKAGE_NAME ${ep}) +endif() ## ############################################################################# ## Add variable : do we want use the system version ? @@ -23,48 +42,27 @@ macro(ep_Initialisation ep option(USE_SYSTEM_${ep} "Use system installed version of ${ep}" - ${use_system_def} + ${ep_Initialisation_USE_SYSTEM} ) if (USE_SYSTEM_${ep}) - find_package(${ep} REQUIRED) -if (WIN32) - if (DEFINED ${ep}_DIR) - file(TO_CMAKE_PATH ${${ep}_DIR} ${ep}_DIR) - endif() - - if (DEFINED EP_PREFIX) - file(TO_CMAKE_PATH ${EP_PREFIX} EP_PREFIX) - endif() - - if (DEFINED ${ep}_BINARY_DIR) - file(TO_CMAKE_PATH ${${ep}_BINARY_DIR} ${ep}_BINARY_DIR) - endif() - - if (DEFINED EP_PATH_SOURCE) - file(TO_CMAKE_PATH ${EP_PATH_SOURCE} EP_PATH_SOURCE) - endif() - - if (DEFINED EP_PATH_BUILD) - file(TO_CMAKE_PATH ${EP_PATH_BUILD} EP_PATH_BUILD) - endif() -endif() + if(NOT ep_Initialisation_NO_CMAKE_PACKAGE) + find_package(${ep_Initialisation_PACKAGE_NAME} REQUIRED) ## ############################################################################# ## Complete superProjectConfig.cmake ## ############################################################################# - if(${required_for_plugins}) - # provide path of project needeed for Asclepios and visages plugins - file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} - "find_package(${ep} REQUIRED - PATHS \"${${ep}_DIR}\" - NO_CMAKE_BUILDS_PATH - )\n" - ) - endif() - + if(ep_Initialisation_REQUIRED_FOR_PLUGINS) + # provide path of project needeed for Asclepios and visages plugins + file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} + "find_package(${ep_Initialisation_PACKAGE_NAME} REQUIRED + PATHS \"${${ep_Initialisation_PACKAGE_NAME}_DIR}\" + )\n" + ) + endif() + endif() else() ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -73,30 +71,30 @@ else() ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - if (${required_for_plugins}) - if (DEFINED ${ep}_BINARY_DIR) - file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} - "find_package(${ep} REQUIRED - PATHS \"${${ep}_BINARY_DIR}\" - PATH_SUFFIXES install build - NO_CMAKE_BUILDS_PATH - )\n" - ) - else() - if(DEFINED EP_PATH_BUILD) - set(build_dir ${EP_PATH_BUILD}) - else() - set(build_dir "${EP_PATH_SOURCE}/${ep}-build" ) - endif() - - file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} - "find_package(${ep} REQUIRED - PATHS \"${build_dir}\" - PATH_SUFFIXES install build - NO_CMAKE_BUILDS_PATH - )\n" - ) - endif() + if(NOT ep_Initialisation_NO_CMAKE_PACKAGE) + if (ep_Initialisation_REQUIRED_FOR_PLUGINS) + if (DEFINED ${ep}_BINARY_DIR) + file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} + "find_package(${ep_Initialisation_PACKAGE_NAME} REQUIRED + PATHS \"${${ep_Initialisation_PACKAGE_NAME}_BINARY_DIR}\" + PATH_SUFFIXES install build + )\n" + ) + else() + if(DEFINED EP_PATH_BUILD) + set(build_dir ${EP_PATH_BUILD}) + else() + set(build_dir "${EP_PATH_SOURCE}/${ep}-build" ) + endif() + + file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} + "find_package(${ep} REQUIRED + PATHS \"${build_dir}\" + PATH_SUFFIXES install build + )\n" + ) + endif() + endif() endif() @@ -106,7 +104,7 @@ else() option(BUILD_SHARED_LIBS_${ep} "Build shared libs for ${ep}" - ${build_shared_libs_def} + ${ep_Initialisation_BUILD_SHARED_LIBS} ) mark_as_advanced(BUILD_SHARED_LIBS_${ep}) From 84cc5a0b505378d9e6f9d163d46615b06f6b1052 Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 10 Apr 2024 16:38:06 +0200 Subject: [PATCH 95/98] Minor changes (spaces etc.) (#1226) --- packaging/CMakeLists.txt | 14 ++++++------- superbuild/CMakeLists.txt | 24 +++++++++++----------- superbuild/ConfigureExternalProjects.cmake | 2 +- superbuild/projects_modules/medInria.cmake | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 5977d44a08..ecef9f721c 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -66,14 +66,12 @@ if (WIN32) set(WIN_PACK_DIR ${PROJECT_SOURCE_DIR}/windows) include(${WIN_PACK_DIR}/WindowsLaunchers.cmake) include(${WIN_PACK_DIR}/WindowsPackaging.cmake) -endif() - -if (APPLE) - include(${PROJECT_SOURCE_DIR}/apple/ApplePackaging.cmake) -endif() - -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - include(${PROJECT_SOURCE_DIR}/linux/LinuxPackaging.cmake) +elseif (UNIX) + if(APPLE) + include(${PROJECT_SOURCE_DIR}/apple/ApplePackaging.cmake) + else() + include(${PROJECT_SOURCE_DIR}/linux/LinuxPackaging.cmake) + endif() endif() # Include cpack modules diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index ec6036996e..dae1d2b538 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -80,12 +80,12 @@ if (USE_Python) endif() list(APPEND external_projects - VTK - ITK - RPI - TTK - DCMTK - QtDCM + VTK + ITK + RPI + TTK + DCMTK + QtDCM dtk LogDemons ) @@ -129,12 +129,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/external_projects_tools ${CMAKE_MODULE_PATH} - ) + ) option(USE_GITHUB_SSH "Use by default Git SSH addresses, requires public key set on github" OFF ) - + option(USE_GITLAB_INRIA_SSH "Use by default Git SSH addresses, requires public key set on Inria gitlab" OFF ) @@ -143,7 +143,7 @@ option(USE_GITLAB_INRIA_SSH # Asclepios and visages plugins set(${PROJECT_NAME}_CONFIG_FILE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake") file(WRITE ${${PROJECT_NAME}_CONFIG_FILE} - "set(CMAKE_MODULE_PATH + "set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \${CMAKE_MODULE_PATH} )\n\n @@ -155,7 +155,7 @@ file(WRITE ${${PROJECT_NAME}_CONFIG_FILE} file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} "set(USE_GITLAB_INRIA_SSH ${USE_GITLAB_INRIA_SSH})\n" ) - + # Add Qt dir file(APPEND ${${PROJECT_NAME}_CONFIG_FILE} "set(Qt5_DIR ${Qt5_DIR})\n\n" @@ -174,10 +174,10 @@ set(PRIVATE_PLUGINS_LEGACY_DIRS "" CACHE PATH "Folders containing legacy private ## ############################################################################# set(global_targets - configure + configure install ) - + # This adds targets that will be run in each external-projects set_property(DIRECTORY PROPERTY EP_STEP_TARGETS ${global_targets}) diff --git a/superbuild/ConfigureExternalProjects.cmake b/superbuild/ConfigureExternalProjects.cmake index 491d51a773..664056899c 100644 --- a/superbuild/ConfigureExternalProjects.cmake +++ b/superbuild/ConfigureExternalProjects.cmake @@ -87,7 +87,7 @@ else() else() if(EP_CHECKBOX_ON_TOP_LEVEL_change) unset(EP_PATH_BASE CACHE) - unset(EP_PATH_BASE_PREVIOUS CACHE) + unset(EP_PATH_BASE_PREVIOUS CACHE) set(EP_DIR_NAME "ExtProjs" CACHE FILEPATH ${ep_dir_name_comment} FORCE) endif() ep_change_garde(EP_DIR_NAME) diff --git a/superbuild/projects_modules/medInria.cmake b/superbuild/projects_modules/medInria.cmake index 9fdf9b984b..e16d642f95 100644 --- a/superbuild/projects_modules/medInria.cmake +++ b/superbuild/projects_modules/medInria.cmake @@ -102,7 +102,7 @@ set(cmake_cache_args if (${USE_FFmpeg}) list(APPEND cmake_args -DUSE_FFmpeg=${USE_FFmpeg}) -endif() +endif() if (USE_DTKIMAGING) set(cmake_args @@ -116,7 +116,7 @@ if (USE_Python) -Dpyncpp_ROOT:PATH=${pyncpp_ROOT} ) endif() - + ## ############################################################################# ## Add external-project ## ############################################################################# From 8b553763832b976018399095d45025f7c32a142c Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 10 Apr 2024 16:38:22 +0200 Subject: [PATCH 96/98] Icon in Windows taskbar (#1225) --- packaging/windows/WindowsPackaging.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index c2f4129414..b63e3e9a42 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -36,6 +36,10 @@ set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${MS set(ICON_PATH "${CMAKE_SOURCE_DIR}/src/app/medInria/resources/medInria.ico") +# Used on pinned on taskbar +set(CPACK_PACKAGE_ICON ${ICON_PATH}) +string(REGEX REPLACE "/" "\\\\\\\\" CPACK_PACKAGE_ICON "${CPACK_PACKAGE_ICON}") + # The icon to install the application. set(CPACK_NSIS_MUI_ICON ${ICON_PATH}) From f075f9ebdf9c4caa0895b845948ee1a05935952c Mon Sep 17 00:00:00 2001 From: fcollot Date: Wed, 10 Apr 2024 17:53:33 +0200 Subject: [PATCH 97/98] Avoid removal of files during windows install (#1224) --- packaging/windows/WindowsPackaging.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index b63e3e9a42..29a6f31552 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -127,13 +127,6 @@ list(APPEND files \${dtk_files}) list(APPEND files \${dcm_files}) list(APPEND files \${qt5_files}) -foreach(file \${files}) - get_filename_component(file2delete \${file} NAME) - if(EXISTS \"${MEDINRIA_FILES}/\${file2delete}\") - file(REMOVE \"${MEDINRIA_FILES}/\${file2delete}\") - endif() -endforeach() - file(INSTALL ${MEDINRIA_FILES}/ DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ FILES_MATCHING @@ -142,6 +135,13 @@ file(INSTALL ${MEDINRIA_FILES}/ PATTERN \"*.pyd\" ) +foreach(file \${files}) + get_filename_component(file2delete \${file} NAME) + if(EXISTS \"\${CMAKE_INSTALL_PREFIX}/bin/\${file2delete}\") + file(REMOVE \"${MEDINRIA_FILES}/\${file2delete}\") + endif() +endforeach() + file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qgif.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qicns.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") file(INSTALL ${QT_PLUGINS_DIR}/imageformats/qico.dll DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/imageformats/ FILES_MATCHING PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\") From 084d34a2595d78e478f28ebcfdf136c9a15d8d41 Mon Sep 17 00:00:00 2001 From: Florent Collot Date: Wed, 10 Apr 2024 18:50:54 +0200 Subject: [PATCH 98/98] merge fixes --- packaging/CMakeLists.txt | 1 - packaging/windows/WindowsPackaging.cmake | 8 - .../resources/pixmaps/medInria-splash.png | Bin 27931 -> 39695 bytes .../resources/pixmaps/medInriaLogo.svg | 302 ++++-------------- .../medImageIO/itkDataImageWriterBase.cpp | 1 - superbuild/CMakeLists.txt | 26 +- superbuild/projects_modules/DCMTK.cmake | 1 - superbuild/projects_modules/qwt.cmake | 2 +- 8 files changed, 81 insertions(+), 260 deletions(-) diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 41dfd32e7e..6014eb5dd8 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -67,7 +67,6 @@ if (WIN32) include(${WIN_PACK_DIR}/WindowsLaunchers.cmake) include(${WIN_PACK_DIR}/WindowsPackaging.cmake) elseif (UNIX) -if (UNIX) if(APPLE) include(${PROJECT_SOURCE_DIR}/apple/ApplePackaging.cmake) else() diff --git a/packaging/windows/WindowsPackaging.cmake b/packaging/windows/WindowsPackaging.cmake index 7354f3fbee..fba1809b0a 100644 --- a/packaging/windows/WindowsPackaging.cmake +++ b/packaging/windows/WindowsPackaging.cmake @@ -130,14 +130,6 @@ list(APPEND files \${dcm_files}) list(APPEND files \${qt5_files}) list(APPEND files \${zlib_files}) -file(INSTALL ${MEDINRIA_FILES}/ - DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ - FILES_MATCHING - PATTERN \"*${CMAKE_EXECUTABLE_SUFFIX}\" - PATTERN \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\" - PATTERN \"*.pyd\" - ) - file(INSTALL ${MEDINRIA_FILES}/ DESTINATION \${CMAKE_INSTALL_PREFIX}/bin/ FILES_MATCHING diff --git a/src/app/medInria/resources/pixmaps/medInria-splash.png b/src/app/medInria/resources/pixmaps/medInria-splash.png index 4197c3e69339e69ce5a7bfde440adcedf86bb9f2..19b660507ef658fb8cab0ec9a2f8b5655c66135a 100644 GIT binary patch literal 39695 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 diff --git a/src/app/medInria/resources/pixmaps/medInriaLogo.svg b/src/app/medInria/resources/pixmaps/medInriaLogo.svg index 31afca08b7..eaecda94bb 100644 --- a/src/app/medInria/resources/pixmaps/medInriaLogo.svg +++ b/src/app/medInria/resources/pixmaps/medInriaLogo.svg @@ -2,27 +2,27 @@ image/svg+xml + sodipodi:cx="364.64285" + id="path3980-2-9" + style="fill:none;stroke:#ffffff;stroke-width:3.84999871;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3096-4)" + sodipodi:type="arc" /> \ No newline at end of file diff --git a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp index 5187cf9695..11a1ad9163 100644 --- a/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp +++ b/src/layers/legacy/medImageIO/itkDataImageWriterBase.cpp @@ -50,7 +50,6 @@ bool itkDataImageWriterBase::canWrite(const QString& path) template bool itkDataImageWriterBase::write_image(const QString& path,const char* type){ -{ medAbstractData* medData = dynamic_cast(this->data()); if (medData && medData->identifier() != type) { diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index 9e5404718c..e478a6192e 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -100,9 +100,9 @@ list(APPEND external_projects TTK DCMTK QtDCM - dtk - LogDemons - ) + dtk + LogDemons + ) if(USE_MUSIC_PLUGINS) list(APPEND external_projects @@ -165,9 +165,9 @@ set(medInria_BINARY_DIR ${CMAKE_BINARY_DIR}/medInria-build) ## ############################################################################# set(CMAKE_MODULE_PATH - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/external_projects_tools - ${CMAKE_MODULE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/external_projects_tools + ${CMAKE_MODULE_PATH} ) option(USE_GITHUB_SSH @@ -183,11 +183,11 @@ option(USE_GITLAB_INRIA_SSH set(${PROJECT_NAME}_CONFIG_FILE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake") file(WRITE ${${PROJECT_NAME}_CONFIG_FILE} "set(CMAKE_MODULE_PATH - ${CMAKE_MODULE_PATH} - \${CMAKE_MODULE_PATH} - )\n\n -set(USE_GITHUB_SSH ${USE_GITHUB_SSH})\n" -) + ${CMAKE_MODULE_PATH} + \${CMAKE_MODULE_PATH} + )\n\n + set(USE_GITHUB_SSH ${USE_GITHUB_SSH})\n" + ) # Add CMAKE_MODULE_PATH to superProjectConfig.cmake, usefull for # Asclepios and visages plugins @@ -214,8 +214,8 @@ set(PRIVATE_PLUGINS_LEGACY_DIRS "" CACHE PATH "Folders containing legacy private set(global_targets configure - install - ) + install + ) # This adds targets that will be run in each external-projects set_property(DIRECTORY PROPERTY EP_STEP_TARGETS ${global_targets}) diff --git a/superbuild/projects_modules/DCMTK.cmake b/superbuild/projects_modules/DCMTK.cmake index c5cb9278e3..cb9d64a938 100644 --- a/superbuild/projects_modules/DCMTK.cmake +++ b/superbuild/projects_modules/DCMTK.cmake @@ -1,4 +1,3 @@ - ################################################################################ # # medInria diff --git a/superbuild/projects_modules/qwt.cmake b/superbuild/projects_modules/qwt.cmake index 642b8cf978..8353a2bff4 100644 --- a/superbuild/projects_modules/qwt.cmake +++ b/superbuild/projects_modules/qwt.cmake @@ -16,7 +16,7 @@ EP_Initialisation(${ep} USE_SYSTEM OFF BUILD_SHARED_LIBS OFF REQUIRED_FOR_PLUGINS ON - NO_CONFIG_FILE + NO_CMAKE_PACKAGE ) if (NOT USE_SYSTEM_${ep})