From cf6c99d4e654854a5cc8950366bd54f33b572a3a Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Sat, 6 Jan 2024 01:50:01 +0100 Subject: [PATCH 01/73] ENH: Add visual DICOM browser COMP: Fix Windows build Co-authored-by: Andras Lasso COMP: Fix Windows build Co-authored-by: Andras Lasso COMP: Fix Windows build Co-authored-by: Andras Lasso BUG: Use port 104 as default for www.dicomserver.co.uk STYLE: Strip trailing spaces of new files --- .../ctkDICOMVisualBrowser/CMakeLists.txt | 40 + .../Testing/CMakeLists.txt | 1 + .../Testing/Cpp/CMakeLists.txt | 21 + .../Cpp/ctkDICOMVisualBrowserTest1.cpp | 58 + .../ctkDICOMVisualBrowserMain.cpp | 99 + .../target_libraries.cmake | 9 + CMake/ctkMacroSetupQt.cmake | 6 + CMakeLists.txt | 4 + Libs/Core/CMakeLists.txt | 9 + Libs/Core/ctkAbstractJob.cpp | 152 + Libs/Core/ctkAbstractJob.h | 126 + Libs/Core/ctkAbstractScheduler.cpp | 35 + Libs/Core/ctkAbstractScheduler.h | 59 + Libs/Core/ctkAbstractWorker.cpp | 95 + Libs/Core/ctkAbstractWorker.h | 74 + Libs/DICOM/Core/CMakeLists.txt | 66 +- Libs/DICOM/Core/Resources/ctkDICOMCore.qrc | 11 +- Libs/DICOM/Core/Resources/dicom-schema.sql | 1 + Libs/DICOM/Core/Resources/storescp.cfg | 503 +++ Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt | 29 + .../Core/Testing/Cpp/ctkDICOMEchoTest1.cpp | 95 + .../Cpp/ctkDICOMJobResponseSetTest1.cpp | 78 + .../Core/Testing/Cpp/ctkDICOMJobTest1.cpp | 142 + .../Core/Testing/Cpp/ctkDICOMQueryTest2.cpp | 7 + .../Testing/Cpp/ctkDICOMRetrieveTest1.cpp | 2 +- .../Testing/Cpp/ctkDICOMRetrieveTest2.cpp | 18 +- .../Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 158 + .../Core/Testing/Cpp/ctkDICOMServerTest1.cpp | 78 + .../Core/ctkDICOMCorePythonQtDecorators.h | 136 + Libs/DICOM/Core/ctkDICOMDatabase.cpp | 852 +++-- Libs/DICOM/Core/ctkDICOMDatabase.h | 25 +- Libs/DICOM/Core/ctkDICOMDatabase_p.h | 4 +- Libs/DICOM/Core/ctkDICOMEcho.cpp | 271 ++ Libs/DICOM/Core/ctkDICOMEcho.h | 90 + Libs/DICOM/Core/ctkDICOMIndexer.cpp | 19 +- Libs/DICOM/Core/ctkDICOMIndexer_p.h | 1 - Libs/DICOM/Core/ctkDICOMInserter.cpp | 167 + Libs/DICOM/Core/ctkDICOMInserter.h | 84 + Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 162 + Libs/DICOM/Core/ctkDICOMInserterJob.h | 81 + Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 168 + Libs/DICOM/Core/ctkDICOMInserterWorker.h | 80 + Libs/DICOM/Core/ctkDICOMInserterWorker_p.h | 47 + Libs/DICOM/Core/ctkDICOMItem.cpp | 6 + Libs/DICOM/Core/ctkDICOMItem.h | 12 +- Libs/DICOM/Core/ctkDICOMJob.cpp | 162 + Libs/DICOM/Core/ctkDICOMJob.h | 115 + Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 412 +++ Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 154 + Libs/DICOM/Core/ctkDICOMQuery.cpp | 1105 ++++-- Libs/DICOM/Core/ctkDICOMQuery.h | 104 +- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 210 ++ Libs/DICOM/Core/ctkDICOMQueryJob.h | 106 + Libs/DICOM/Core/ctkDICOMQueryJob_p.h | 48 + Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 232 ++ Libs/DICOM/Core/ctkDICOMQueryWorker.h | 79 + Libs/DICOM/Core/ctkDICOMQueryWorker_p.h | 49 + Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 659 +++- Libs/DICOM/Core/ctkDICOMRetrieve.h | 108 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 187 ++ Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 78 + Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h | 46 + Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 327 ++ Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 77 + Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h | 49 + Libs/DICOM/Core/ctkDICOMScheduler.cpp | 1129 +++++++ Libs/DICOM/Core/ctkDICOMScheduler.h | 227 ++ Libs/DICOM/Core/ctkDICOMScheduler_p.h | 71 + Libs/DICOM/Core/ctkDICOMServer.cpp | 333 ++ Libs/DICOM/Core/ctkDICOMServer.h | 121 + Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 451 +++ Libs/DICOM/Core/ctkDICOMStorageListener.h | 117 + .../DICOM/Core/ctkDICOMStorageListenerJob.cpp | 145 + Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 86 + .../DICOM/Core/ctkDICOMStorageListenerJob_p.h | 48 + .../Core/ctkDICOMStorageListenerWorker.cpp | 227 ++ .../Core/ctkDICOMStorageListenerWorker.h | 81 + .../Core/ctkDICOMStorageListenerWorker_p.h | 50 + Libs/DICOM/Core/ctkDICOMWorker.cpp | 91 + Libs/DICOM/Core/ctkDICOMWorker.h | 63 + Libs/DICOM/Widgets/CMakeLists.txt | 23 + Libs/DICOM/Widgets/Plugins/CMakeLists.txt | 4 + .../ctkDICOMVisualBrowserWidgetPlugin.cpp | 71 + .../ctkDICOMVisualBrowserWidgetPlugin.h | 48 + .../Widgets/Plugins/ctkDICOMWidgetsPlugins.h | 2 + .../Widgets/Resources/UI/Icons/accept.svg | 1 + Libs/DICOM/Widgets/Resources/UI/Icons/add.svg | 1 + .../Widgets/Resources/UI/Icons/cancel.svg | 1 + .../Widgets/Resources/UI/Icons/cloud.svg | 49 + .../Widgets/Resources/UI/Icons/delete.svg | 1 + Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg | 1 + .../Resources/UI/Icons/downloading.svg | 39 + .../Widgets/Resources/UI/Icons/import.svg | 1 + .../DICOM/Widgets/Resources/UI/Icons/load.svg | 1 + .../Widgets/Resources/UI/Icons/loaded.svg | 50 + .../Widgets/Resources/UI/Icons/more_vert.svg | 1 + .../Widgets/Resources/UI/Icons/patient.svg | 1 + .../Widgets/Resources/UI/Icons/query.svg | 1 + .../DICOM/Widgets/Resources/UI/Icons/save.svg | 1 + .../Resources/UI/Icons/text_document.svg | 1 + .../Widgets/Resources/UI/Icons/visible.svg | 50 + .../DICOM/Widgets/Resources/UI/Icons/wait.svg | 1 + .../Widgets/Resources/UI/Icons/warning.svg | 1 + .../Resources/UI/ctkDICOMPatientItemWidget.ui | 365 ++ .../Resources/UI/ctkDICOMQueryWidget.ui | 2 +- .../Resources/UI/ctkDICOMSeriesItemWidget.ui | 101 + .../Resources/UI/ctkDICOMServerNodeWidget2.ui | 407 +++ .../Resources/UI/ctkDICOMStudyItemWidget.ui | 235 ++ .../UI/ctkDICOMVisualBrowserWidget.ui | 965 ++++++ .../Widgets/Resources/UI/ctkDICOMWidget.qrc | 22 + Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt | 10 + .../Testing/Cpp/ctkDICOMBrowserTest.cpp | 1 - .../Cpp/ctkDICOMPatientItemWidgetTest1.cpp | 75 + .../Cpp/ctkDICOMSeriesItemWidgetTest1.cpp | 84 + .../Cpp/ctkDICOMServerNodeWidget2Test1.cpp | 97 + .../Cpp/ctkDICOMStudyItemWidgetTest1.cpp | 81 + .../Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 151 + Libs/DICOM/Widgets/ctkDICOMBrowser.cpp | 9 +- .../Widgets/ctkDICOMObjectListWidget.cpp | 10 +- .../Widgets/ctkDICOMPatientItemWidget.cpp | 828 +++++ .../DICOM/Widgets/ctkDICOMPatientItemWidget.h | 159 + .../Widgets/ctkDICOMQueryRetrieveWidget.cpp | 33 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 826 +++++ Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 143 + .../Widgets/ctkDICOMServerNodeWidget2.cpp | 1308 ++++++++ .../DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 124 + .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 689 ++++ Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 154 + .../Widgets/ctkDICOMThumbnailGenerator.cpp | 6 +- .../Widgets/ctkDICOMThumbnailGenerator.h | 2 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 2979 +++++++++++++++++ .../Widgets/ctkDICOMVisualBrowserWidget.h | 373 +++ .../Widgets/Resources/UI/ctkThumbnailLabel.ui | 111 +- Libs/Widgets/ctkThumbnailLabel.cpp | 73 +- Libs/Widgets/ctkThumbnailLabel.h | 14 + 135 files changed, 21753 insertions(+), 762 deletions(-) create mode 100644 Applications/ctkDICOMVisualBrowser/CMakeLists.txt create mode 100644 Applications/ctkDICOMVisualBrowser/Testing/CMakeLists.txt create mode 100644 Applications/ctkDICOMVisualBrowser/Testing/Cpp/CMakeLists.txt create mode 100644 Applications/ctkDICOMVisualBrowser/Testing/Cpp/ctkDICOMVisualBrowserTest1.cpp create mode 100644 Applications/ctkDICOMVisualBrowser/ctkDICOMVisualBrowserMain.cpp create mode 100644 Applications/ctkDICOMVisualBrowser/target_libraries.cmake create mode 100644 Libs/Core/ctkAbstractJob.cpp create mode 100644 Libs/Core/ctkAbstractJob.h create mode 100644 Libs/Core/ctkAbstractScheduler.cpp create mode 100644 Libs/Core/ctkAbstractScheduler.h create mode 100644 Libs/Core/ctkAbstractWorker.cpp create mode 100644 Libs/Core/ctkAbstractWorker.h create mode 100644 Libs/DICOM/Core/Resources/storescp.cfg create mode 100644 Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp create mode 100644 Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp create mode 100644 Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp create mode 100644 Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp create mode 100644 Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMEcho.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMEcho.h create mode 100644 Libs/DICOM/Core/ctkDICOMInserter.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMInserter.h create mode 100644 Libs/DICOM/Core/ctkDICOMInserterJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMInserterJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMInserterWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMInserterWorker.h create mode 100644 Libs/DICOM/Core/ctkDICOMInserterWorker_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMJobResponseSet.h create mode 100644 Libs/DICOM/Core/ctkDICOMQueryJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMQueryJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMQueryJob_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMQueryWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMQueryWorker.h create mode 100644 Libs/DICOM/Core/ctkDICOMQueryWorker_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveWorker.h create mode 100644 Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMScheduler.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMScheduler.h create mode 100644 Libs/DICOM/Core/ctkDICOMScheduler_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMServer.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMServer.h create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListener.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListener.h create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h create mode 100644 Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMWorker.h create mode 100644 Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp create mode 100644 Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/accept.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/add.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/cancel.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/cloud.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/downloading.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/import.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/load.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/loaded.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/patient.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/query.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/save.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/visible.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/Icons/warning.svg create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui create mode 100644 Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc create mode 100644 Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp create mode 100644 Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp create mode 100644 Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp create mode 100644 Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp create mode 100644 Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h create mode 100644 Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h create mode 100644 Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h create mode 100644 Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h create mode 100644 Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp create mode 100644 Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h diff --git a/Applications/ctkDICOMVisualBrowser/CMakeLists.txt b/Applications/ctkDICOMVisualBrowser/CMakeLists.txt new file mode 100644 index 0000000000..448ffa5e37 --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/CMakeLists.txt @@ -0,0 +1,40 @@ +project(ctkDICOMVisualBrowser) + +# +# See CTK/CMake/ctkMacroBuildApp.cmake for details +# + +# Source files +set(KIT_SRCS + ctkDICOMVisualBrowserMain.cpp + ) + +# Headers that should run through moc +set(KIT_MOC_SRCS + ) + +# UI files +set(KIT_UI_FORMS +) + +# Resources +set(KIT_resources +) + +# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake +# The following macro will read the target libraries from the file 'target_libraries.cmake' +ctkFunctionGetTargetLibraries(KIT_target_libraries) + +ctkMacroBuildApp( + NAME ${PROJECT_NAME} + SRCS ${KIT_SRCS} + MOC_SRCS ${KIT_MOC_SRCS} + UI_FORMS ${KIT_UI_FORMS} + TARGET_LIBRARIES ${KIT_target_libraries} + RESOURCES ${KIT_resources} + ) + +# Testing +if(BUILD_TESTING) + add_subdirectory(Testing) +endif() diff --git a/Applications/ctkDICOMVisualBrowser/Testing/CMakeLists.txt b/Applications/ctkDICOMVisualBrowser/Testing/CMakeLists.txt new file mode 100644 index 0000000000..cdeb442a1d --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/Testing/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Cpp) diff --git a/Applications/ctkDICOMVisualBrowser/Testing/Cpp/CMakeLists.txt b/Applications/ctkDICOMVisualBrowser/Testing/Cpp/CMakeLists.txt new file mode 100644 index 0000000000..4524b0b494 --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/Testing/Cpp/CMakeLists.txt @@ -0,0 +1,21 @@ +set(KIT ${PROJECT_NAME}) + +create_test_sourcelist(Tests ${KIT}CppTests.cpp + ctkDICOMVisualBrowserTest1.cpp + ) + +SET (TestsToRun ${Tests}) +REMOVE (TestsToRun ${KIT}CppTests.cpp) + +# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake +# The following macro will read the target libraries from the file '/target_libraries.cmake' +ctkFunctionGetTargetLibraries(KIT_target_libraries ${${KIT}_SOURCE_DIR}) + +ctk_add_executable_utf8(${KIT}CppTests ${Tests}) +target_link_libraries(${KIT}CppTests ${KIT_target_libraries}) + +# +# Add Tests +# + +SIMPLE_TEST(ctkDICOMVisualBrowserTest1 $) diff --git a/Applications/ctkDICOMVisualBrowser/Testing/Cpp/ctkDICOMVisualBrowserTest1.cpp b/Applications/ctkDICOMVisualBrowser/Testing/Cpp/ctkDICOMVisualBrowserTest1.cpp new file mode 100644 index 0000000000..b7f267ef51 --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/Testing/Cpp/ctkDICOMVisualBrowserTest1.cpp @@ -0,0 +1,58 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include + +// STD includes +#include +#include + +int ctkDICOMVisualBrowserTest1(int argc, char * argv []) +{ + QCoreApplication app(argc, argv); + if (app.arguments().count() != 2) + { + std::cerr << "Line " << __LINE__ << " - Failed to run " << argv[0] << "\n" + << "Usage:\n" + << " " << argv[0] << " /path/to/ctkDICOM"; + return EXIT_FAILURE; + } + QString command = app.arguments().at(1); + QProcess process; + process.start(command, /* arguments= */ QStringList()); + bool res = process.waitForStarted(); + if (!res) + { + std::cerr << '\"' << qPrintable(command) << '\"' + << " didn't start correctly" << std::endl; + return res ? EXIT_SUCCESS : EXIT_FAILURE; + } + process.kill(); + res = process.waitForFinished(); + if (!res) + { + std::cerr << '\"' << qPrintable(command) << '\"' + << " failed to terminate" << std::endl; + return res ? EXIT_SUCCESS : EXIT_FAILURE; + } + return res ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/Applications/ctkDICOMVisualBrowser/ctkDICOMVisualBrowserMain.cpp b/Applications/ctkDICOMVisualBrowser/ctkDICOMVisualBrowserMain.cpp new file mode 100644 index 0000000000..3e00123f54 --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/ctkDICOMVisualBrowserMain.cpp @@ -0,0 +1,99 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Isomics Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include + +// CTK widget includes +#include +#include +#include + +// STD includes +#include + +int main(int argc, char** argv) +{ + QApplication app(argc, argv); + + app.setOrganizationName("commontk"); + app.setOrganizationDomain("commontk.org"); + app.setApplicationName("ctkDICOM"); + + // set up Qt resource files + QResource::registerResource("./Resources/ctkDICOM.qrc"); + + QWidget mainWidget; + mainWidget.setObjectName(QString::fromUtf8("MainWidget")); + mainWidget.setWindowTitle(QString::fromUtf8("DICOM Visual Browser")); + + QVBoxLayout mainLayout; + mainLayout.setObjectName(QString::fromUtf8("mainLayout")); + mainLayout.setContentsMargins(1, 1, 1, 1); + + QHBoxLayout topLayout; + topLayout.setObjectName(QString::fromUtf8("topLayout")); + topLayout.setContentsMargins(1, 1, 1, 1); + + QLabel databaseNameLabel; + databaseNameLabel.setObjectName(QString::fromUtf8("DatabaseNameLabel")); + databaseNameLabel.setMaximumSize(QSize(100, 30)); + topLayout.addWidget(&databaseNameLabel); + + ctkDirectoryButton directoryButton; + directoryButton.setObjectName(QString::fromUtf8("DirectoryButton")); + directoryButton.setMinimumSize(QSize(200, 30)); + if (argc > 1) + { + directoryButton.setDirectory(argv[1]); + } + topLayout.addWidget(&directoryButton); + + mainLayout.addLayout(&topLayout); + + ctkDICOMVisualBrowserWidget DICOMVisualBrowser; + DICOMVisualBrowser.setObjectName(QString::fromUtf8("DICOMVisualBrowser")); + DICOMVisualBrowser.setDatabaseDirectorySettingsKey("DatabaseDirectory"); + DICOMVisualBrowser.setMinimumSize(QSize(1000, 1000)); + // set up the database + if (argc > 1) + { + DICOMVisualBrowser.setDatabaseDirectory(argv[1]); + } + + DICOMVisualBrowser.serverSettingsGroupBox()->setChecked(true); + QObject::connect(&directoryButton, SIGNAL(directoryChanged(const QString&)), + &DICOMVisualBrowser, SLOT(setDatabaseDirectory(const QString&))); + + mainLayout.addWidget(&DICOMVisualBrowser); + + mainWidget.setLayout(&mainLayout); + + mainWidget.show(); + + return app.exec(); +} diff --git a/Applications/ctkDICOMVisualBrowser/target_libraries.cmake b/Applications/ctkDICOMVisualBrowser/target_libraries.cmake new file mode 100644 index 0000000000..014ce6da4a --- /dev/null +++ b/Applications/ctkDICOMVisualBrowser/target_libraries.cmake @@ -0,0 +1,9 @@ +# +# See CMake/ctkFunctionGetTargetLibraries.cmake +# +# This file should list the libraries required to build the current CTK application. +# + +set(target_libraries + CTKDICOMWidgets + ) \ No newline at end of file diff --git a/CMake/ctkMacroSetupQt.cmake b/CMake/ctkMacroSetupQt.cmake index cfd93cf345..9777ad4297 100644 --- a/CMake/ctkMacroSetupQt.cmake +++ b/CMake/ctkMacroSetupQt.cmake @@ -29,6 +29,12 @@ macro(ctkMacroSetupQt) # See https://github.com/commontk/CTK/wiki/Maintenance#updates-of-required-qt-components + if(CTK_LIB_Widgets + OR CTK_LIB_DICOM/Widgets + ) + list(APPEND CTK_QT5_COMPONENTS Svg) + endif() + if(CTK_LIB_Widgets OR CTK_LIB_Scripting/Python/Core_PYTHONQT_WRAP_QTXML ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f3982f4f7..48c4ef83b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -550,6 +550,10 @@ ctk_app_option(ctkDICOM2 "Build the new DICOM example application (experimental)" OFF CTK_ENABLE_DICOM AND CTK_BUILD_EXAMPLES) +ctk_app_option(ctkDICOMVisualBrowser + "Build the new DICOM example application (experimental)" OFF + CTK_ENABLE_DICOM AND CTK_BUILD_EXAMPLES) + ctk_app_option(ctkDICOMIndexer "Build the DICOM example application" OFF CTK_ENABLE_DICOM AND CTK_BUILD_EXAMPLES) diff --git a/Libs/Core/CMakeLists.txt b/Libs/Core/CMakeLists.txt index 1873e89be5..f88a2bfb28 100644 --- a/Libs/Core/CMakeLists.txt +++ b/Libs/Core/CMakeLists.txt @@ -22,6 +22,8 @@ set(KIT_SRCS ctkAbstractFactory.tpp ctkAbstractFileBasedFactory.h ctkAbstractFileBasedFactory.tpp + ctkAbstractJob.cpp + ctkAbstractJob.h ctkAbstractObjectFactory.h ctkAbstractObjectFactory.tpp ctkAbstractPluginFactory.h @@ -30,6 +32,10 @@ set(KIT_SRCS ctkAbstractQObjectFactory.tpp ctkAbstractLibraryFactory.h ctkAbstractLibraryFactory.tpp + ctkAbstractScheduler.cpp + ctkAbstractScheduler.h + ctkAbstractWorker.cpp + ctkAbstractWorker.h ctkBackTrace.cpp ctkBooleanMapper.cpp ctkBooleanMapper.h @@ -100,6 +106,9 @@ endif() # Headers that should run through moc set(KIT_MOC_SRCS + ctkAbstractJob.h + ctkAbstractScheduler.h + ctkAbstractWorker.h ctkBooleanMapper.h ctkCallback.h ctkCommandLineParser.h diff --git a/Libs/Core/ctkAbstractJob.cpp b/Libs/Core/ctkAbstractJob.cpp new file mode 100644 index 0000000000..2a1fefaace --- /dev/null +++ b/Libs/Core/ctkAbstractJob.cpp @@ -0,0 +1,152 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#include "ctkAbstractJob.h" + +// Qt includes +#include + +// -------------------------------------------------------------------------- +ctkAbstractJob::ctkAbstractJob() +{ + this->Status = JobStatus::Initialized; + this->Persistent = false; + this->JobUID = QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); + this->RetryCounter = 0; + this->RetryDelay = 100; + this->MaximumNumberOfRetry = 3; + this->MaximumConcurrentJobsPerType = 20; + this->Priority = QThread::Priority::LowPriority; +} + +//---------------------------------------------------------------------------- +ctkAbstractJob::~ctkAbstractJob() +{ +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setJobUID(const QString &jobUID) +{ + this->JobUID = jobUID; +} + +//---------------------------------------------------------------------------- +QString ctkAbstractJob::className() const +{ + if (!this->metaObject()) + { + return ""; + } + return this->metaObject()->className(); +} + +//---------------------------------------------------------------------------- +QString ctkAbstractJob::jobUID() const +{ + return this->JobUID; +} + +//---------------------------------------------------------------------------- +ctkAbstractJob::JobStatus ctkAbstractJob::status() const +{ + return this->Status; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setStatus(const JobStatus &status) +{ + this->Status = status; +} + +//---------------------------------------------------------------------------- +bool ctkAbstractJob::isPersistent() const +{ + return this->Persistent; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setIsPersistent(const bool &persistent) +{ + this->Persistent = persistent; +} + +//---------------------------------------------------------------------------- +int ctkAbstractJob::retryCounter() const +{ + return this->RetryCounter; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setRetryCounter(const int& retryCounter) +{ + this->RetryCounter = retryCounter; +} + +//---------------------------------------------------------------------------- +int ctkAbstractJob::maximumConcurrentJobsPerType() const +{ + return this->MaximumConcurrentJobsPerType; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setMaximumConcurrentJobsPerType(const int &maximumConcurrentJobsPerType) +{ + this->MaximumConcurrentJobsPerType = maximumConcurrentJobsPerType; +} + +//---------------------------------------------------------------------------- +int ctkAbstractJob::maximumNumberOfRetry() const +{ + return this->MaximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) +{ + this->MaximumNumberOfRetry = maximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +int ctkAbstractJob::retryDelay() const +{ + return this->RetryDelay; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setRetryDelay(const int &retryDelay) +{ + this->RetryDelay = retryDelay; +} + +//---------------------------------------------------------------------------- +QThread::Priority ctkAbstractJob::priority() const +{ + return this->Priority; +} + +//---------------------------------------------------------------------------- +void ctkAbstractJob::setPriority(const QThread::Priority &priority) +{ + this->Priority = priority; +} + diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h new file mode 100644 index 0000000000..055ae3b111 --- /dev/null +++ b/Libs/Core/ctkAbstractJob.h @@ -0,0 +1,126 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkAbstractJob_h +#define __ctkAbstractJob_h + +// Qt includes +#include +#include + +// CTK includes +#include "ctkCoreExport.h" + +class ctkAbstractWorker; + +//------------------------------------------------------------------------------ +/// \ingroup Core +class CTK_CORE_EXPORT ctkAbstractJob : public QObject +{ + Q_OBJECT + Q_ENUMS(JobStatus); + Q_PROPERTY(QString jobUID READ jobUID WRITE setJobUID); + Q_PROPERTY(QString className READ className); + Q_PROPERTY(JobStatus status READ status WRITE setStatus); + Q_PROPERTY(bool persistent READ isPersistent WRITE setIsPersistent); + Q_PROPERTY(bool retryCounter READ retryCounter WRITE setRetryCounter); + Q_PROPERTY(int maximumNumberOfRetry READ maximumNumberOfRetry WRITE setMaximumNumberOfRetry); + Q_PROPERTY(int retryDelay READ retryDelay WRITE setRetryDelay); + Q_PROPERTY(bool maximumConcurrentJobsPerType READ maximumConcurrentJobsPerType WRITE setMaximumConcurrentJobsPerType); + Q_PROPERTY(QThread::Priority priority READ priority WRITE setPriority); + +public: + explicit ctkAbstractJob(); + virtual ~ctkAbstractJob(); + + /// Job UID + QString jobUID() const; + virtual void setJobUID(const QString& jobUID); + + /// Class name + QString className() const; + + /// Status + enum JobStatus { + Initialized = 0, + Queued, + Running, + Stopped, + Finished, + }; + JobStatus status() const; + virtual void setStatus(const JobStatus& status); + + /// Persistent + bool isPersistent() const; + void setIsPersistent(const bool& persistent); + + /// Number of retries: current counter of how many times + /// the task has been relunched on fails + int retryCounter() const; + void setRetryCounter(const int& retryCounter); + + /// Set the maximum concurrent jobs per job type. + /// Default value is 20. + int maximumConcurrentJobsPerType() const; + void setMaximumConcurrentJobsPerType(const int& maximumConcurrentJobsPerType); + + /// Maximum number of retries that the Job pool will try on each failed Job + /// default: 3 + int maximumNumberOfRetry() const; + void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + + /// Retry delay in millisec + /// default: 100 msec + int retryDelay() const; + void setRetryDelay(const int& retryDelay); + + /// Priority + QThread::Priority priority() const; + void setPriority(const QThread::Priority& priority); + + /// Generate worker for job + Q_INVOKABLE virtual ctkAbstractWorker* createWorker() = 0; + +Q_SIGNALS: + void started(); + void finished(); + void canceled(); + void failed(); + +protected: + QString JobUID; + JobStatus Status; + bool Persistent; + int RetryDelay; + int RetryCounter; + int MaximumNumberOfRetry; + int MaximumConcurrentJobsPerType; + QThread::Priority Priority; + +private: + Q_DISABLE_COPY(ctkAbstractJob) +}; + + +#endif // ctkAbstractJob_h diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp new file mode 100644 index 0000000000..cb5b8b27fb --- /dev/null +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -0,0 +1,35 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#include "ctkAbstractScheduler.h" + +// -------------------------------------------------------------------------- +ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) + : QObject(parent) +{ +} + +// -------------------------------------------------------------------------- +ctkAbstractScheduler::~ctkAbstractScheduler() +{ +} diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h new file mode 100644 index 0000000000..ef2a264d5c --- /dev/null +++ b/Libs/Core/ctkAbstractScheduler.h @@ -0,0 +1,59 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkAbstractScheduler_h +#define __ctkAbstractScheduler_h + +// Qt includes +#include + +// CTK includes +#include "ctkCoreExport.h" + +//------------------------------------------------------------------------------ +/// \ingroup Core +class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject +{ + Q_OBJECT +public: + explicit ctkAbstractScheduler(QObject* parent = 0); + virtual ~ctkAbstractScheduler(); + +Q_SIGNALS: + void jobStarted(QString jobUID, QString jobType); + void jobFinished(QString jobUID, QString jobType); + void jobCanceled(QString jobUID, QString jobType); + void jobFailed(QString jobUID, QString jobType); + +public Q_SLOTS: + virtual void onJobStarted() = 0; + virtual void onJobFinished() = 0; + virtual void onJobCanceled() = 0; + virtual void onJobFailed() = 0; + +private: + Q_DISABLE_COPY(ctkAbstractScheduler) +}; + + +#endif // ctkAbstractScheduler_h diff --git a/Libs/Core/ctkAbstractWorker.cpp b/Libs/Core/ctkAbstractWorker.cpp new file mode 100644 index 0000000000..2dc0396dae --- /dev/null +++ b/Libs/Core/ctkAbstractWorker.cpp @@ -0,0 +1,95 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#include "ctkAbstractJob.h" +#include "ctkAbstractScheduler.h" +#include "ctkAbstractWorker.h" + +// -------------------------------------------------------------------------- +ctkAbstractWorker::ctkAbstractWorker() +{ + this->Job = nullptr; + this->Scheduler = nullptr; + this->setAutoDelete(false); +} + +//---------------------------------------------------------------------------- +ctkAbstractWorker::~ctkAbstractWorker() +{ +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkAbstractJob* ctkAbstractWorker::job() const +{ + return this->Job.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkAbstractWorker::jobShared() const +{ + return this->Job; +} + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::setJob(ctkAbstractJob &job) +{ + this->setJob(QSharedPointer(&job, skipDelete)); +} + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::setJob(QSharedPointer job) +{ + this->Job = job; +} + +//---------------------------------------------------------------------------- +ctkAbstractScheduler *ctkAbstractWorker::scheduler() const +{ + return this->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkAbstractWorker::schedulerShared() const +{ + return this->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::setScheduler(ctkAbstractScheduler &scheduler) +{ + this->Scheduler = QSharedPointer(&scheduler, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::setScheduler(QSharedPointer scheduler) +{ + this->Scheduler = scheduler; +} diff --git a/Libs/Core/ctkAbstractWorker.h b/Libs/Core/ctkAbstractWorker.h new file mode 100644 index 0000000000..7d6be0d498 --- /dev/null +++ b/Libs/Core/ctkAbstractWorker.h @@ -0,0 +1,74 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkAbstractWorker_h +#define __ctkAbstractWorker_h + +// Qt includes +#include +#include + +// CTK includes +#include "ctkCoreExport.h" + +class ctkAbstractJob; +class ctkAbstractScheduler; + +//------------------------------------------------------------------------------ +/// \ingroup Core +class CTK_CORE_EXPORT ctkAbstractWorker : public QObject, public QRunnable +{ + Q_OBJECT + +public: + explicit ctkAbstractWorker(); + virtual ~ctkAbstractWorker(); + + /// Execute worker + virtual void run() = 0; + + /// Cancel worker + virtual void cancel() = 0; + + /// Job + Q_INVOKABLE ctkAbstractJob* job() const; + QSharedPointer jobShared() const; + Q_INVOKABLE void setJob(ctkAbstractJob& job); + void setJob(QSharedPointer job); + + /// Scheduler + Q_INVOKABLE ctkAbstractScheduler* scheduler() const; + QSharedPointer schedulerShared() const; + Q_INVOKABLE void setScheduler(ctkAbstractScheduler& scheduler); + void setScheduler(QSharedPointer scheduler); + +protected: + QSharedPointer Job; + QSharedPointer Scheduler; + +private: + Q_DISABLE_COPY(ctkAbstractWorker) +}; + + +#endif // ctkAbstractWorker_h diff --git a/Libs/DICOM/Core/CMakeLists.txt b/Libs/DICOM/Core/CMakeLists.txt index 80a5422544..49199036b9 100644 --- a/Libs/DICOM/Core/CMakeLists.txt +++ b/Libs/DICOM/Core/CMakeLists.txt @@ -16,21 +16,59 @@ set(KIT_SRCS ctkDICOMItem.h ctkDICOMDisplayedFieldGenerator.cpp ctkDICOMDisplayedFieldGenerator.h + ctkDICOMEcho.cpp + ctkDICOMEcho.h ctkDICOMFilterProxyModel.cpp ctkDICOMFilterProxyModel.h ctkDICOMIndexer.cpp ctkDICOMIndexer.h ctkDICOMIndexer_p.h + ctkDICOMInserter.cpp + ctkDICOMInserter.h + ctkDICOMInserterJob.cpp + ctkDICOMInserterJob.h + ctkDICOMInserterWorker.cpp + ctkDICOMInserterWorker.h + ctkDICOMInserterWorker_p.h ctkDICOMItem.cpp ctkDICOMItem.h + ctkDICOMJob.cpp + ctkDICOMJob.h + ctkDICOMJobResponseSet.cpp + ctkDICOMJobResponseSet.h ctkDICOMModel.cpp ctkDICOMModel.h ctkDICOMPersonName.cpp ctkDICOMPersonName.h ctkDICOMQuery.cpp ctkDICOMQuery.h + ctkDICOMQueryJob.cpp + ctkDICOMQueryJob.h + ctkDICOMQueryJob_p.h + ctkDICOMQueryWorker.cpp + ctkDICOMQueryWorker.h + ctkDICOMQueryWorker_p.h ctkDICOMRetrieve.cpp ctkDICOMRetrieve.h + ctkDICOMRetrieveJob.cpp + ctkDICOMRetrieveJob.h + ctkDICOMRetrieveJob_p.h + ctkDICOMRetrieveWorker.cpp + ctkDICOMRetrieveWorker.h + ctkDICOMRetrieveWorker_p.h + ctkDICOMScheduler.cpp + ctkDICOMScheduler.h + ctkDICOMScheduler_p.h + ctkDICOMServer.cpp + ctkDICOMServer.h + ctkDICOMStorageListener.cpp + ctkDICOMStorageListener.h + ctkDICOMStorageListenerJob.cpp + ctkDICOMStorageListenerJob.h + ctkDICOMStorageListenerJob_p.h + ctkDICOMStorageListenerWorker.cpp + ctkDICOMStorageListenerWorker.h + ctkDICOMStorageListenerWorker_p.h ctkDICOMTester.cpp ctkDICOMTester.h ctkDICOMUtil.cpp @@ -50,6 +88,8 @@ set(KIT_SRCS ctkDICOMDisplayedFieldGeneratorStudyNumberOfSeriesRule.cpp ctkDICOMDisplayedFieldGeneratorPatientNumberOfStudiesRule.h ctkDICOMDisplayedFieldGeneratorPatientNumberOfStudiesRule.cpp + ctkDICOMWorker.cpp + ctkDICOMWorker.h ) # Abstract class should not be wrapped ! @@ -66,13 +106,37 @@ set(KIT_MOC_SRCS ctkDICOMDisplayedFieldGenerator.h ctkDICOMDisplayedFieldGenerator_p.h ctkDICOMDisplayedFieldGeneratorRuleFactory.h + ctkDICOMEcho.h ctkDICOMIndexer.h ctkDICOMIndexer_p.h + ctkDICOMInserter.h + ctkDICOMInserterJob.h + ctkDICOMInserterWorker.h + ctkDICOMInserterWorker_p.h + ctkDICOMJob.h + ctkDICOMJobResponseSet.h ctkDICOMFilterProxyModel.h ctkDICOMModel.h ctkDICOMQuery.h + ctkDICOMQueryJob.h + ctkDICOMQueryJob_p.h + ctkDICOMQueryWorker.h + ctkDICOMQueryWorker_p.h ctkDICOMRetrieve.h + ctkDICOMRetrieveJob.h + ctkDICOMRetrieveJob_p.h + ctkDICOMRetrieveWorker.h + ctkDICOMRetrieveWorker_p.h + ctkDICOMScheduler.h + ctkDICOMScheduler_p.h + ctkDICOMServer.h + ctkDICOMStorageListener.h + ctkDICOMStorageListenerJob.h + ctkDICOMStorageListenerJob_p.h + ctkDICOMStorageListenerWorker.h + ctkDICOMStorageListenerWorker_p.h ctkDICOMTester.h + ctkDICOMWorker.h ) # UI files @@ -93,7 +157,6 @@ list(APPEND KIT_target_libraries Qt${CTK_QT_VERSION}::Sql) # create a dcm query/retrieve service config file that points to the build dir set (DCMQRSCP_STORE_DIR ${CMAKE_CURRENT_BINARY_DIR}/Testing) configure_file( Resources/dcmqrscp.cfg.in dcmqrscp.cfg ) -set (DCMQRSCP_CONFIG ${CMAKE_CURRENT_BINARY_DIR}/dcmqrscp.cfg) ctkMacroBuildLib( NAME ${PROJECT_NAME} @@ -108,7 +171,6 @@ ctkMacroBuildLib( if(DEFINED DCMTK_HAVE_CONFIG_H_OPTIONAL AND NOT DCMTK_HAVE_CONFIG_H_OPTIONAL) # Workaround Debian packaging issue - See FindDCMTK.cmake for more details - set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS ${DCMTK_DEFINITIONS}) set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS ${DCMTK_DEFINITIONS}) endif() diff --git a/Libs/DICOM/Core/Resources/ctkDICOMCore.qrc b/Libs/DICOM/Core/Resources/ctkDICOMCore.qrc index 977d25846b..8ec68f4abd 100644 --- a/Libs/DICOM/Core/Resources/ctkDICOMCore.qrc +++ b/Libs/DICOM/Core/Resources/ctkDICOMCore.qrc @@ -1,6 +1,7 @@ - - - dicom-schema.sql - dicom-qr-schema.sql - + + + dicom-schema.sql + dicom-qr-schema.sql + storescp.cfg + diff --git a/Libs/DICOM/Core/Resources/dicom-schema.sql b/Libs/DICOM/Core/Resources/dicom-schema.sql index 06b352f9f4..61af7adf3b 100644 --- a/Libs/DICOM/Core/Resources/dicom-schema.sql +++ b/Libs/DICOM/Core/Resources/dicom-schema.sql @@ -74,6 +74,7 @@ CREATE TABLE 'Series' ( 'BodyPartExamined' VARCHAR(255) NULL , 'FrameOfReferenceUID' VARCHAR(64) NULL , 'AcquisitionNumber' INT NULL , + 'ContrastAgent' VARCHAR(255) NULL , 'ScanningSequence' VARCHAR(45) NULL , 'EchoNumber' INT NULL , diff --git a/Libs/DICOM/Core/Resources/storescp.cfg b/Libs/DICOM/Core/Resources/storescp.cfg new file mode 100644 index 0000000000..07660fc6ea --- /dev/null +++ b/Libs/DICOM/Core/Resources/storescp.cfg @@ -0,0 +1,503 @@ +# +# Copyright (C) 2003-2021, OFFIS e.V. +# All rights reserved. See COPYRIGHT file for details. +# +# This software and supporting documentation were developed by +# +# OFFIS e.V. +# R&D Division Health +# Escherweg 2 +# D-26121 Oldenburg, Germany +# +# Module: dcmnet +# +# Author: Marco Eichelberg, Joerg Riesmeier +# +# Purpose: Sample configuration file for storescp +# + +# ============================================================================ +[[TransferSyntaxes]] +# ============================================================================ + +[Uncompressed] +TransferSyntax1 = LocalEndianExplicit +TransferSyntax2 = OppositeEndianExplicit +TransferSyntax3 = LittleEndianImplicit + +[UncompressedOrZlib] +TransferSyntax1 = DeflatedLittleEndianExplicit +TransferSyntax2 = LocalEndianExplicit +TransferSyntax3 = OppositeEndianExplicit +TransferSyntax4 = LittleEndianImplicit + +[AnyTransferSyntax] +TransferSyntax1 = JPEG2000 +TransferSyntax2 = JPEG2000LosslessOnly +TransferSyntax3 = JPEGExtended:Process2+4 +TransferSyntax4 = JPEGBaseline +TransferSyntax5 = JPEGLossless:Non-hierarchical-1stOrderPrediction +TransferSyntax6 = JPEGLSLossy +TransferSyntax7 = JPEGLSLossless +TransferSyntax8 = RLELossless +TransferSyntax9 = MPEG2MainProfile@MainLevel +TransferSyntax10 = MPEG2MainProfile@HighLevel +TransferSyntax11 = MPEG4HighProfile/Level4.1 +TransferSyntax12 = MPEG4BDcompatibleHighProfile/Level4.1 +TransferSyntax13 = MPEG4HighProfile/Level4.2For2DVideo +TransferSyntax14 = MPEG4HighProfile/Level4.2For3DVideo +TransferSyntax15 = MPEG4StereoHighProfile/Level4.2 +TransferSyntax16 = HEVCMainProfile/Level5.1 +TransferSyntax17 = HEVCMain10Profile/Level5.1 +TransferSyntax18 = DeflatedLittleEndianExplicit +TransferSyntax19 = LocalEndianExplicit +TransferSyntax20 = OppositeEndianExplicit +TransferSyntax21 = LittleEndianImplicit + +# ============================================================================ +[[PresentationContexts]] +# ============================================================================ + +[GenericStorageSCP] +# +# Don't forget to support the Verification SOP Class. +# +PresentationContext1 = VerificationSOPClass\Uncompressed +# +# Accept image SOP classes with virtually any transfer syntax we know. +# Accept non-image SOP classes uncompressed or with zlib compression only. +# +PresentationContext2 = BreastTomosynthesisImageStorage\AnyTransferSyntax +PresentationContext3 = ComputedRadiographyImageStorage\AnyTransferSyntax +PresentationContext4 = CornealTopographyMapStorage\AnyTransferSyntax +PresentationContext5 = CTImageStorage\AnyTransferSyntax +PresentationContext6 = DigitalIntraOralXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext7 = DigitalIntraOralXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext8 = DigitalMammographyXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext9 = DigitalMammographyXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext10 = DigitalXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext11 = DigitalXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext12 = EnhancedCTImageStorage\AnyTransferSyntax +PresentationContext13 = EnhancedMRColorImageStorage\AnyTransferSyntax +PresentationContext14 = EnhancedMRImageStorage\AnyTransferSyntax +PresentationContext15 = EnhancedPETImageStorage\AnyTransferSyntax +PresentationContext16 = EnhancedUSVolumeStorage\AnyTransferSyntax +PresentationContext17 = EnhancedXAImageStorage\AnyTransferSyntax +PresentationContext18 = EnhancedXRFImageStorage\AnyTransferSyntax +PresentationContext19 = IntravascularOpticalCoherenceTomographyImageStorageForPresentation\AnyTransferSyntax +PresentationContext20 = IntravascularOpticalCoherenceTomographyImageStorageForProcessing\AnyTransferSyntax +PresentationContext21 = MRImageStorage\AnyTransferSyntax +PresentationContext22 = MultiframeGrayscaleByteSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext23 = MultiframeGrayscaleWordSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext24 = MultiframeSingleBitSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext25 = MultiframeTrueColorSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext26 = NuclearMedicineImageStorage\AnyTransferSyntax +PresentationContext27 = OphthalmicPhotography16BitImageStorage\AnyTransferSyntax +PresentationContext28 = OphthalmicPhotography8BitImageStorage\AnyTransferSyntax +PresentationContext29 = OphthalmicThicknessMapStorage\AnyTransferSyntax +PresentationContext30 = OphthalmicTomographyImageStorage\AnyTransferSyntax +PresentationContext31 = PositronEmissionTomographyImageStorage\AnyTransferSyntax +PresentationContext32 = RTImageStorage\AnyTransferSyntax +PresentationContext33 = SecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext34 = UltrasoundImageStorage\AnyTransferSyntax +PresentationContext35 = UltrasoundMultiframeImageStorage\AnyTransferSyntax +PresentationContext36 = VideoEndoscopicImageStorage\AnyTransferSyntax +PresentationContext37 = VideoMicroscopicImageStorage\AnyTransferSyntax +PresentationContext38 = VideoPhotographicImageStorage\AnyTransferSyntax +PresentationContext39 = VLEndoscopicImageStorage\AnyTransferSyntax +PresentationContext40 = VLMicroscopicImageStorage\AnyTransferSyntax +PresentationContext41 = VLPhotographicImageStorage\AnyTransferSyntax +PresentationContext42 = VLSlideCoordinatesMicroscopicImageStorage\AnyTransferSyntax +PresentationContext43 = VLWholeSlideMicroscopyImageStorage\AnyTransferSyntax +PresentationContext44 = XRay3DAngiographicImageStorage\AnyTransferSyntax +PresentationContext45 = XRay3DCraniofacialImageStorage\AnyTransferSyntax +PresentationContext46 = XRayAngiographicImageStorage\AnyTransferSyntax +PresentationContext47 = XRayRadiofluoroscopicImageStorage\AnyTransferSyntax +# retired +PresentationContext48 = RETIRED_HardcopyColorImageStorage\AnyTransferSyntax +PresentationContext49 = RETIRED_HardcopyGrayscaleImageStorage\AnyTransferSyntax +PresentationContext50 = RETIRED_NuclearMedicineImageStorage\AnyTransferSyntax +PresentationContext51 = RETIRED_UltrasoundImageStorage\AnyTransferSyntax +PresentationContext52 = RETIRED_UltrasoundMultiframeImageStorage\AnyTransferSyntax +PresentationContext53 = RETIRED_VLImageStorage\AnyTransferSyntax +PresentationContext54 = RETIRED_VLMultiframeImageStorage\AnyTransferSyntax +PresentationContext55 = RETIRED_XRayAngiographicBiPlaneImageStorage\AnyTransferSyntax +# +# the following presentation contexts are for non-image SOP classes +# +PresentationContext56 = AmbulatoryECGWaveformStorage\UncompressedOrZlib +PresentationContext57 = ArterialPulseWaveformStorage\UncompressedOrZlib +PresentationContext58 = AutorefractionMeasurementsStorage\UncompressedOrZlib +PresentationContext59 = BasicStructuredDisplayStorage\UncompressedOrZlib +PresentationContext60 = BasicTextSRStorage\UncompressedOrZlib +PresentationContext61 = BasicVoiceAudioWaveformStorage\UncompressedOrZlib +PresentationContext62 = BlendingSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext63 = CardiacElectrophysiologyWaveformStorage\UncompressedOrZlib +PresentationContext64 = ChestCADSRStorage\UncompressedOrZlib +PresentationContext65 = ColonCADSRStorage\UncompressedOrZlib +PresentationContext66 = ColorSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext67 = Comprehensive3DSRStorage\UncompressedOrZlib +PresentationContext68 = ComprehensiveSRStorage\UncompressedOrZlib +PresentationContext69 = DeformableSpatialRegistrationStorage\UncompressedOrZlib +PresentationContext70 = EncapsulatedCDAStorage\UncompressedOrZlib +PresentationContext71 = EncapsulatedPDFStorage\UncompressedOrZlib +PresentationContext72 = EnhancedSRStorage\UncompressedOrZlib +PresentationContext73 = GeneralAudioWaveformStorage\UncompressedOrZlib +PresentationContext74 = GeneralECGWaveformStorage\UncompressedOrZlib +PresentationContext75 = GenericImplantTemplateStorage\UncompressedOrZlib +PresentationContext76 = GrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext77 = HemodynamicWaveformStorage\UncompressedOrZlib +PresentationContext78 = ImplantAssemblyTemplateStorage\UncompressedOrZlib +PresentationContext79 = ImplantationPlanSRDocumentStorage\UncompressedOrZlib +PresentationContext80 = ImplantTemplateGroupStorage\UncompressedOrZlib +PresentationContext81 = IntraocularLensCalculationsStorage\UncompressedOrZlib +PresentationContext82 = KeratometryMeasurementsStorage\UncompressedOrZlib +PresentationContext83 = KeyObjectSelectionDocumentStorage\UncompressedOrZlib +PresentationContext84 = LensometryMeasurementsStorage\UncompressedOrZlib +PresentationContext85 = MacularGridThicknessAndVolumeReportStorage\UncompressedOrZlib +PresentationContext86 = MammographyCADSRStorage\UncompressedOrZlib +PresentationContext87 = MRSpectroscopyStorage\UncompressedOrZlib +PresentationContext88 = OphthalmicAxialMeasurementsStorage\UncompressedOrZlib +PresentationContext89 = OphthalmicVisualFieldStaticPerimetryMeasurementsStorage\UncompressedOrZlib +PresentationContext90 = ProcedureLogStorage\UncompressedOrZlib +PresentationContext91 = PseudoColorSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext92 = RawDataStorage\UncompressedOrZlib +PresentationContext93 = RealWorldValueMappingStorage\UncompressedOrZlib +PresentationContext94 = RespiratoryWaveformStorage\UncompressedOrZlib +PresentationContext95 = RTBeamsDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext96 = RTBeamsTreatmentRecordStorage\UncompressedOrZlib +PresentationContext97 = RTBrachyTreatmentRecordStorage\UncompressedOrZlib +PresentationContext98 = RTDoseStorage\UncompressedOrZlib +PresentationContext99 = RTIonBeamsTreatmentRecordStorage\UncompressedOrZlib +PresentationContext100 = RTIonPlanStorage\UncompressedOrZlib +PresentationContext101 = RTPlanStorage\UncompressedOrZlib +PresentationContext102 = RTStructureSetStorage\UncompressedOrZlib +PresentationContext103 = RTTreatmentSummaryRecordStorage\UncompressedOrZlib +PresentationContext104 = SegmentationStorage\UncompressedOrZlib +PresentationContext105 = SpatialFiducialsStorage\UncompressedOrZlib +PresentationContext106 = SpatialRegistrationStorage\UncompressedOrZlib +PresentationContext107 = SpectaclePrescriptionReportStorage\UncompressedOrZlib +PresentationContext108 = StereometricRelationshipStorage\UncompressedOrZlib +PresentationContext109 = SubjectiveRefractionMeasurementsStorage\UncompressedOrZlib +PresentationContext110 = SurfaceScanMeshStorage\UncompressedOrZlib +PresentationContext111 = SurfaceScanPointCloudStorage\UncompressedOrZlib +PresentationContext112 = SurfaceSegmentationStorage\UncompressedOrZlib +PresentationContext113 = TwelveLeadECGWaveformStorage\UncompressedOrZlib +PresentationContext114 = VisualAcuityMeasurementsStorage\UncompressedOrZlib +PresentationContext115 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext116 = XRayRadiationDoseSRStorage\UncompressedOrZlib +# retired +PresentationContext117 = RETIRED_StandaloneCurveStorage\UncompressedOrZlib +PresentationContext118 = RETIRED_StandaloneModalityLUTStorage\UncompressedOrZlib +PresentationContext119 = RETIRED_StandaloneOverlayStorage\UncompressedOrZlib +PresentationContext120 = RETIRED_StandalonePETCurveStorage\UncompressedOrZlib +PresentationContext121 = RETIRED_StandaloneVOILUTStorage\UncompressedOrZlib +PresentationContext122 = RETIRED_StoredPrintStorage\UncompressedOrZlib +# draft +PresentationContext123 = DRAFT_RTBeamsDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext124 = DRAFT_SRAudioStorage\UncompressedOrZlib +PresentationContext125 = DRAFT_SRComprehensiveStorage\UncompressedOrZlib +PresentationContext126 = DRAFT_SRDetailStorage\UncompressedOrZlib +PresentationContext127 = DRAFT_SRTextStorage\UncompressedOrZlib +PresentationContext128 = DRAFT_WaveformStorage\UncompressedOrZlib +# +# the following SOP classes are missing in the above list: +# +# - AcquisitionContextSRStorage +# - AdvancedBlendingPresentationStateStorage +# - BodyPositionWaveformStorage +# - BreastProjectionXRayImageStorageForPresentation +# - BreastProjectionXRayImageStorageForProcessing +# - CArmPhotonElectronRadiationRecordStorage +# - CArmPhotonElectronRadiationStorage +# - ColorPaletteStorage +# - CompositingPlanarMPRVolumetricPresentationStateStorage +# - ContentAssessmentResultsStorage +# - CTDefinedProcedureProtocolStorage +# - CTPerformedProcedureProtocolStorage +# - DermoscopicPhotographyImageStorage +# - ElectromyogramWaveformStorage +# - ElectrooculogramWaveformStorage +# - EncapsulatedMTLStorage +# - EncapsulatedOBJStorage +# - EncapsulatedSTLStorage +# - EnhancedXRayRadiationDoseSRStorage +# - ExtensibleSRStorage +# - GrayscalePlanarMPRVolumetricPresentationStateStorage +# - HangingProtocolStorage +# - LegacyConvertedEnhancedCTImageStorage +# - LegacyConvertedEnhancedMRImageStorage +# - LegacyConvertedEnhancedPETImageStorage +# - MicroscopyBulkSimpleAnnotationsStorage +# - MultichannelRespiratoryWaveformStorage +# - MultipleVolumeRenderingVolumetricPresentationStateStorage +# - OphthalmicOpticalCoherenceTomographyBscanVolumeAnalysisStorage +# - OphthalmicOpticalCoherenceTomographyEnFaceImageStorage +# - ParametricMapStorage +# - PatientRadiationDoseSRStorage +# - PerformedImagingAgentAdministrationSRStorage +# - PlannedImagingAgentAdministrationSRStorage +# - ProtocolApprovalStorage +# - RadiopharmaceuticalRadiationDoseSRStorage +# - RoboticArmRadiationStorage +# - RoboticRadiationRecordStorage +# - RoutineScalpElectroencephalogramWaveformStorage +# - RTBrachyApplicationSetupDeliveryInstructionStorage +# - RTPhysicianIntentStorage +# - RTRadiationRecordSetStorage +# - RTRadiationSalvageRecordStorage +# - RTRadiationSetDeliveryInstructionStorage +# - RTRadiationSetStorage +# - RTSegmentAnnotationStorage +# - RTTreatmentPreparationStorage +# - SegmentedVolumeRenderingVolumetricPresentationStateStorage +# - SimplifiedAdultEchoSRStorage +# - SleepElectroencephalogramWaveformStorage +# - TomotherapeuticRadiationRecordStorage +# - TomotherapeuticRadiationStorage +# - TractographyResultsStorage +# - VolumeRenderingVolumetricPresentationStateStorage +# - WideFieldOphthalmicPhotographyStereographicProjectionImageStorage +# - WideFieldOphthalmicPhotography3DCoordinatesImageStorage +# - XADefinedProcedureProtocolStorage +# - XAPerformedProcedureProtocolStorage +# +# - DICOS_2DAITStorage +# - DICOS_3DAITStorage +# - DICOS_CTImageStorage +# - DICOS_DigitalXRayImageStorageForPresentation +# - DICOS_DigitalXRayImageStorageForProcessing +# - DICOS_QuadrupoleResonanceStorage +# - DICOS_ThreatDetectionReportStorage +# +# - DICONDE_EddyCurrentImageStorage +# - DICONDE_EddyCurrentMultiframeImageStorage + +# ---------------------------------------------------------------------------- + +[AllDICOMStorageSCP] +# +# Same as "GenericStorageSCP" but limited to non-retired and non-draft SOP Classes. +# This allows for accepting (almost) all DICOM Storage SOP Classes that are currently +# defined in the standard (an exception is made for some very new DICOM objects because +# of the limitation of 128 Presentation Contexts for SCPs, see DCMTK Feature #540). +# +PresentationContext1 = VerificationSOPClass\Uncompressed +# +# DICOM images +# +PresentationContext2 = BreastProjectionXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext3 = BreastProjectionXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext4 = BreastTomosynthesisImageStorage\AnyTransferSyntax +PresentationContext5 = ComputedRadiographyImageStorage\AnyTransferSyntax +PresentationContext6 = CornealTopographyMapStorage\AnyTransferSyntax +PresentationContext7 = CTImageStorage\AnyTransferSyntax +PresentationContext8 = DigitalIntraOralXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext9 = DigitalIntraOralXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext10 = DigitalMammographyXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext11 = DigitalMammographyXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext12 = DigitalXRayImageStorageForPresentation\AnyTransferSyntax +PresentationContext13 = DigitalXRayImageStorageForProcessing\AnyTransferSyntax +PresentationContext14 = EnhancedCTImageStorage\AnyTransferSyntax +PresentationContext15 = EnhancedMRColorImageStorage\AnyTransferSyntax +PresentationContext16 = EnhancedMRImageStorage\AnyTransferSyntax +PresentationContext17 = EnhancedPETImageStorage\AnyTransferSyntax +PresentationContext18 = EnhancedUSVolumeStorage\AnyTransferSyntax +PresentationContext19 = EnhancedXAImageStorage\AnyTransferSyntax +PresentationContext20 = EnhancedXRFImageStorage\AnyTransferSyntax +PresentationContext21 = IntravascularOpticalCoherenceTomographyImageStorageForPresentation\AnyTransferSyntax +PresentationContext22 = IntravascularOpticalCoherenceTomographyImageStorageForProcessing\AnyTransferSyntax +PresentationContext23 = LegacyConvertedEnhancedCTImageStorage\AnyTransferSyntax +PresentationContext24 = LegacyConvertedEnhancedMRImageStorage\AnyTransferSyntax +PresentationContext25 = LegacyConvertedEnhancedPETImageStorage\AnyTransferSyntax +PresentationContext26 = MRImageStorage\AnyTransferSyntax +PresentationContext27 = MultiframeGrayscaleByteSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext28 = MultiframeGrayscaleWordSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext29 = MultiframeSingleBitSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext30 = MultiframeTrueColorSecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext31 = NuclearMedicineImageStorage\AnyTransferSyntax +PresentationContext32 = OphthalmicPhotography16BitImageStorage\AnyTransferSyntax +PresentationContext33 = OphthalmicPhotography8BitImageStorage\AnyTransferSyntax +PresentationContext34 = OphthalmicThicknessMapStorage\AnyTransferSyntax +PresentationContext35 = OphthalmicTomographyImageStorage\AnyTransferSyntax +PresentationContext36 = ParametricMapStorage\AnyTransferSyntax +PresentationContext37 = PositronEmissionTomographyImageStorage\AnyTransferSyntax +PresentationContext38 = RTImageStorage\AnyTransferSyntax +PresentationContext39 = SecondaryCaptureImageStorage\AnyTransferSyntax +PresentationContext40 = UltrasoundImageStorage\AnyTransferSyntax +PresentationContext41 = UltrasoundMultiframeImageStorage\AnyTransferSyntax +PresentationContext42 = VideoEndoscopicImageStorage\AnyTransferSyntax +PresentationContext43 = VideoMicroscopicImageStorage\AnyTransferSyntax +PresentationContext44 = VideoPhotographicImageStorage\AnyTransferSyntax +PresentationContext45 = VLEndoscopicImageStorage\AnyTransferSyntax +PresentationContext46 = VLMicroscopicImageStorage\AnyTransferSyntax +PresentationContext47 = VLPhotographicImageStorage\AnyTransferSyntax +PresentationContext48 = VLSlideCoordinatesMicroscopicImageStorage\AnyTransferSyntax +PresentationContext49 = VLWholeSlideMicroscopyImageStorage\AnyTransferSyntax +PresentationContext50 = WideFieldOphthalmicPhotographyStereographicProjectionImageStorage\AnyTransferSyntax +PresentationContext51 = WideFieldOphthalmicPhotography3DCoordinatesImageStorage\AnyTransferSyntax +PresentationContext52 = XRay3DAngiographicImageStorage\AnyTransferSyntax +PresentationContext53 = XRay3DCraniofacialImageStorage\AnyTransferSyntax +PresentationContext54 = XRayAngiographicImageStorage\AnyTransferSyntax +PresentationContext55 = XRayRadiofluoroscopicImageStorage\AnyTransferSyntax +# +# all other DICOM objects +# +PresentationContext56 = AcquisitionContextSRStorage\UncompressedOrZlib +PresentationContext57 = AmbulatoryECGWaveformStorage\UncompressedOrZlib +PresentationContext58 = ArterialPulseWaveformStorage\UncompressedOrZlib +PresentationContext59 = AutorefractionMeasurementsStorage\UncompressedOrZlib +PresentationContext60 = BasicStructuredDisplayStorage\UncompressedOrZlib +PresentationContext61 = BasicTextSRStorage\UncompressedOrZlib +PresentationContext62 = BasicVoiceAudioWaveformStorage\UncompressedOrZlib +PresentationContext63 = BlendingSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext64 = CardiacElectrophysiologyWaveformStorage\UncompressedOrZlib +PresentationContext65 = ChestCADSRStorage\UncompressedOrZlib +PresentationContext66 = ColonCADSRStorage\UncompressedOrZlib +PresentationContext67 = ColorSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext68 = CompositingPlanarMPRVolumetricPresentationStateStorage\UncompressedOrZlib +PresentationContext69 = Comprehensive3DSRStorage\UncompressedOrZlib +PresentationContext70 = ComprehensiveSRStorage\UncompressedOrZlib +PresentationContext71 = ContentAssessmentResultsStorage\UncompressedOrZlib +PresentationContext72 = CTDefinedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext73 = CTPerformedProcedureProtocolStorage\UncompressedOrZlib +PresentationContext74 = DeformableSpatialRegistrationStorage\UncompressedOrZlib +PresentationContext75 = EncapsulatedCDAStorage\UncompressedOrZlib +PresentationContext76 = EncapsulatedPDFStorage\UncompressedOrZlib +PresentationContext77 = EnhancedSRStorage\UncompressedOrZlib +PresentationContext78 = ExtensibleSRStorage\UncompressedOrZlib +PresentationContext79 = GeneralAudioWaveformStorage\UncompressedOrZlib +PresentationContext80 = GeneralECGWaveformStorage\UncompressedOrZlib +PresentationContext81 = GenericImplantTemplateStorage\UncompressedOrZlib +PresentationContext82 = GrayscalePlanarMPRVolumetricPresentationStateStorage\UncompressedOrZlib +PresentationContext83 = GrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext84 = HangingProtocolStorage\UncompressedOrZlib +PresentationContext85 = HemodynamicWaveformStorage\UncompressedOrZlib +PresentationContext86 = ImplantAssemblyTemplateStorage\UncompressedOrZlib +PresentationContext87 = ImplantationPlanSRDocumentStorage\UncompressedOrZlib +PresentationContext88 = ImplantTemplateGroupStorage\UncompressedOrZlib +PresentationContext89 = IntraocularLensCalculationsStorage\UncompressedOrZlib +PresentationContext90 = KeratometryMeasurementsStorage\UncompressedOrZlib +PresentationContext91 = KeyObjectSelectionDocumentStorage\UncompressedOrZlib +PresentationContext92 = LensometryMeasurementsStorage\UncompressedOrZlib +PresentationContext93 = MacularGridThicknessAndVolumeReportStorage\UncompressedOrZlib +PresentationContext94 = MammographyCADSRStorage\UncompressedOrZlib +PresentationContext95 = MRSpectroscopyStorage\UncompressedOrZlib +PresentationContext96 = OphthalmicAxialMeasurementsStorage\UncompressedOrZlib +PresentationContext97 = OphthalmicVisualFieldStaticPerimetryMeasurementsStorage\UncompressedOrZlib +PresentationContext98 = ProcedureLogStorage\UncompressedOrZlib +PresentationContext99 = PseudoColorSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext100 = RadiopharmaceuticalRadiationDoseSRStorage\UncompressedOrZlib +PresentationContext101 = RawDataStorage\UncompressedOrZlib +PresentationContext102 = RealWorldValueMappingStorage\UncompressedOrZlib +PresentationContext103 = RespiratoryWaveformStorage\UncompressedOrZlib +PresentationContext104 = RTBeamsDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext105 = RTBeamsTreatmentRecordStorage\UncompressedOrZlib +PresentationContext106 = RTBrachyApplicationSetupDeliveryInstructionStorage\UncompressedOrZlib +PresentationContext107 = RTBrachyTreatmentRecordStorage\UncompressedOrZlib +PresentationContext108 = RTDoseStorage\UncompressedOrZlib +PresentationContext109 = RTIonBeamsTreatmentRecordStorage\UncompressedOrZlib +PresentationContext110 = RTIonPlanStorage\UncompressedOrZlib +PresentationContext111 = RTPlanStorage\UncompressedOrZlib +PresentationContext112 = RTStructureSetStorage\UncompressedOrZlib +PresentationContext113 = RTTreatmentSummaryRecordStorage\UncompressedOrZlib +PresentationContext114 = SegmentationStorage\UncompressedOrZlib +PresentationContext115 = SimplifiedAdultEchoSRStorage\UncompressedOrZlib +PresentationContext116 = SpatialFiducialsStorage\UncompressedOrZlib +PresentationContext117 = SpatialRegistrationStorage\UncompressedOrZlib +PresentationContext118 = SpectaclePrescriptionReportStorage\UncompressedOrZlib +PresentationContext119 = StereometricRelationshipStorage\UncompressedOrZlib +PresentationContext120 = SubjectiveRefractionMeasurementsStorage\UncompressedOrZlib +PresentationContext121 = SurfaceScanMeshStorage\UncompressedOrZlib +PresentationContext122 = SurfaceScanPointCloudStorage\UncompressedOrZlib +PresentationContext123 = SurfaceSegmentationStorage\UncompressedOrZlib +PresentationContext124 = TractographyResultsStorage\UncompressedOrZlib +PresentationContext125 = TwelveLeadECGWaveformStorage\UncompressedOrZlib +PresentationContext126 = VisualAcuityMeasurementsStorage\UncompressedOrZlib +PresentationContext127 = XAXRFGrayscaleSoftcopyPresentationStateStorage\UncompressedOrZlib +PresentationContext128 = XRayRadiationDoseSRStorage\UncompressedOrZlib +# +# the following SOP classes are missing in the above list: +# +# - AdvancedBlendingPresentationStateStorage +# - BodyPositionWaveformStorage +# - CArmPhotonElectronRadiationRecordStorage +# - CArmPhotonElectronRadiationStorage +# - ColorPaletteStorage +# - DermoscopicPhotographyImageStorage +# - ElectromyogramWaveformStorage +# - ElectrooculogramWaveformStorage +# - EncapsulatedMTLStorage +# - EncapsulatedOBJStorage +# - EncapsulatedSTLStorage +# - EnhancedXRayRadiationDoseSRStorage +# - MicroscopyBulkSimpleAnnotationsStorage +# - MultichannelRespiratoryWaveformStorage +# - MultipleVolumeRenderingVolumetricPresentationStateStorage +# - OphthalmicOpticalCoherenceTomographyBscanVolumeAnalysisStorage +# - OphthalmicOpticalCoherenceTomographyEnFaceImageStorage +# - PatientRadiationDoseSRStorage +# - PerformedImagingAgentAdministrationSRStorage +# - PlannedImagingAgentAdministrationSRStorage +# - ProtocolApprovalStorage +# - RoboticArmRadiationStorage +# - RoboticRadiationRecordStorage +# - RoutineScalpElectroencephalogramWaveformStorage +# - RTPhysicianIntentStorage +# - RTRadiationRecordSetStorage +# - RTRadiationSalvageRecordStorage +# - RTRadiationSetDeliveryInstructionStorage +# - RTRadiationSetStorage +# - RTSegmentAnnotationStorage +# - RTTreatmentPreparationStorage +# - SegmentedVolumeRenderingVolumetricPresentationStateStorage +# - SleepElectroencephalogramWaveformStorage +# - TomotherapeuticRadiationRecordStorage +# - TomotherapeuticRadiationStorage +# - VolumeRenderingVolumetricPresentationStateStorage +# - XADefinedProcedureProtocolStorage +# - XAPerformedProcedureProtocolStorage +# +# - RETIRED_HardcopyColorImageStorage +# - RETIRED_HardcopyGrayscaleImageStorage +# - RETIRED_NuclearMedicineImageStorage +# - RETIRED_UltrasoundImageStorage +# - RETIRED_UltrasoundMultiframeImageStorage +# - RETIRED_VLImageStorage +# - RETIRED_VLMultiframeImageStorage +# - RETIRED_XRayAngiographicBiPlaneImageStorage +# +# - RETIRED_StandaloneCurveStorage +# - RETIRED_StandaloneModalityLUTStorage +# - RETIRED_StandaloneOverlayStorage +# - RETIRED_StandalonePETCurveStorage +# - RETIRED_StandaloneVOILUTStorage +# - RETIRED_StoredPrintStorage +# +# - DRAFT_RTBeamsDeliveryInstructionStorage +# - DRAFT_SRAudioStorage +# - DRAFT_SRComprehensiveStorage +# - DRAFT_SRDetailStorage +# - DRAFT_SRTextStorage +# - DRAFT_WaveformStorage +# +# - DICOS_2DAITStorage +# - DICOS_3DAITStorage +# - DICOS_CTImageStorage +# - DICOS_DigitalXRayImageStorageForPresentation +# - DICOS_DigitalXRayImageStorageForProcessing +# - DICOS_QuadrupoleResonanceStorage +# - DICOS_ThreatDetectionReportStorage +# +# - DICONDE_EddyCurrentImageStorage +# - DICONDE_EddyCurrentMultiframeImageStorage + +# ============================================================================ +[[Profiles]] +# ============================================================================ + +[Default] +PresentationContexts = GenericStorageSCP + +[AllDICOM] +PresentationContexts = AllDICOMStorageSCP diff --git a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt index e8637d3525..98f16f22aa 100644 --- a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt +++ b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt @@ -9,14 +9,19 @@ create_test_sourcelist(Tests ${KIT}CppTests.cpp ctkDICOMDatabaseTest5.cpp ctkDICOMDatabaseTest6.cpp ctkDICOMDatabaseTest7.cpp + ctkDICOMEchoTest1.cpp ctkDICOMItemTest1.cpp ctkDICOMIndexerTest1.cpp + ctkDICOMJobTest1.cpp + ctkDICOMJobResponseSetTest1.cpp ctkDICOMModelTest1.cpp ctkDICOMPersonNameTest1.cpp ctkDICOMQueryTest1.cpp ctkDICOMQueryTest2.cpp ctkDICOMRetrieveTest1.cpp ctkDICOMRetrieveTest2.cpp + ctkDICOMSchedulerTest1.cpp + ctkDICOMServerTest1.cpp ctkDICOMTesterTest1.cpp ctkDICOMTesterTest2.cpp ) @@ -39,6 +44,10 @@ target_link_libraries(${KIT}CppTests # Add Tests # +SIMPLE_TEST(ctkDICOMJobTest1) +SIMPLE_TEST(ctkDICOMJobResponseSetTest1) +SIMPLE_TEST(ctkDICOMServerTest1) + # ctkDICOMDatabase SIMPLE_TEST(ctkDICOMDatabaseTest1) SIMPLE_TEST(ctkDICOMDatabaseTest2 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA) @@ -52,6 +61,12 @@ SIMPLE_TEST(ctkDICOMDatabaseTest7) SIMPLE_TEST(ctkDICOMItemTest1) SIMPLE_TEST(ctkDICOMIndexerTest1 ) +# ctkDICOMEcho +SIMPLE_TEST(ctkDICOMEchoTest1 + ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000056.IMA + ) + # ctkDICOMModel SIMPLE_TEST(ctkDICOMModelTest1 ${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/ctkDICOMModelTest1-dicom.db @@ -81,6 +96,20 @@ SIMPLE_TEST( ctkDICOMCoreTest1 ${CMAKE_CURRENT_SOURCE_DIR}/../../Resources/dicom-sample.sql ) +# ctkDICOMScheduler +SIMPLE_TEST(ctkDICOMSchedulerTest1 + ${CTKData_DIR}/Data/DICOM/MRHEAD/000050.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000051.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000052.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000053.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000054.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000056.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000057.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000058.IMA + ${CTKData_DIR}/Data/DICOM/MRHEAD/000059.IMA + ) + # ctkDICOMTester SIMPLE_TEST( ctkDICOMTesterTest1 ) set_property(TEST "ctkDICOMTesterTest1" PROPERTY RESOURCE_LOCK "dcmqrscp") diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp new file mode 100644 index 0000000000..dcba0ac3fd --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp @@ -0,0 +1,95 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=============================================================================*/ + +// Qt includes +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMEcho.h" +#include "ctkDICOMTester.h" + +// STD includes +#include + +int ctkDICOMEchoTest1(int argc, char * argv []) { + + QCoreApplication app(argc, argv); + + // run tester server + ctkDICOMTester tester; + std::cerr << "ctkDICOMEchoTest1: Starting dcmqrscp\n"; + tester.startDCMQRSCP(); + QStringList arguments = app.arguments(); + arguments.pop_front(); // remove application name + if (!arguments.count()) + { + return EXIT_FAILURE; + } + std::cerr << "ctkDICOMEchoTest1: Storing data to dcmqrscp\n"; + tester.storeData(arguments); + + ctkDICOMEcho echo; + + // Test the default values + CHECK_QSTRING(echo.connectionName(), ""); + CHECK_QSTRING(echo.callingAETitle(), ""); + CHECK_QSTRING(echo.calledAETitle(), ""); + CHECK_QSTRING(echo.host(), ""); + CHECK_INT(echo.port(), 80); + CHECK_INT(echo.connectionTimeout(), 3); + + // Test setting and getting + echo.setConnectionName("connectionName"); + CHECK_QSTRING(echo.connectionName(), "connectionName"); + echo.setCallingAETitle("callingAETitle"); + CHECK_QSTRING(echo.callingAETitle(), "callingAETitle"); + echo.setCalledAETitle("calledAETitle"); + CHECK_QSTRING(echo.calledAETitle(), "calledAETitle"); + echo.setHost("host"); + CHECK_QSTRING(echo.host(), "host"); + echo.setPort(3000); + CHECK_INT(echo.port(), 3000); + echo.setConnectionTimeout(30); + CHECK_INT(echo.connectionTimeout(), 30); + + // Test echo + // this should print: Failed to establish association + CHECK_BOOL(echo.echo(), false); + + // this has to be successful + std::cerr << "ctkDICOMEchoTest1: Setting up echo\n"; + echo.setCallingAETitle("CTK_AE"); + echo.setCalledAETitle("CTK_AE"); + echo.setHost("localhost"); + echo.setPort(tester.dcmqrscpPort()); + + std::cerr << "ctkDICOMEchoTest1: Running echo\n"; + CHECK_BOOL(echo.echo(), true); + + return EXIT_SUCCESS; +} + diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp new file mode 100644 index 0000000000..1872387084 --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp @@ -0,0 +1,78 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=============================================================================*/ + +// Qt includes +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" + + +int ctkDICOMJobResponseSetTest1(int argc, char * argv []) { + + QCoreApplication app(argc, argv); + + // Query Job and virtual parents (ctkDICOMJob and ctkAbstractJob) + ctkDICOMJobResponseSet jobResponseSet; + + // Test the default values + CHECK_QSTRING(jobResponseSet.filePath(), ""); + CHECK_BOOL(jobResponseSet.copyFile(), false); + CHECK_BOOL(jobResponseSet.overwriteExistingDataset(), false); + CHECK_INT(jobResponseSet.typeOfJob(), ctkDICOMJobResponseSet::JobType::None); + CHECK_QSTRING(jobResponseSet.jobUID(), ""); + CHECK_QSTRING(jobResponseSet.patientID(), ""); + CHECK_QSTRING(jobResponseSet.studyInstanceUID(), ""); + CHECK_QSTRING(jobResponseSet.seriesInstanceUID(), ""); + CHECK_QSTRING(jobResponseSet.sopInstanceUID(), ""); + CHECK_QSTRING(jobResponseSet.connectionName(), ""); + + // Test setting and getting + jobResponseSet.setFilePath("filePath"); + CHECK_QSTRING(jobResponseSet.filePath(), "filePath"); + jobResponseSet.setCopyFile(true); + CHECK_BOOL(jobResponseSet.copyFile(), true); + jobResponseSet.setOverwriteExistingDataset(true); + CHECK_BOOL(jobResponseSet.overwriteExistingDataset(), true); + jobResponseSet.setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); + CHECK_INT(jobResponseSet.typeOfJob(), ctkDICOMJobResponseSet::JobType::RetrieveStudy); + jobResponseSet.setJobUID("JobUID"); + CHECK_QSTRING(jobResponseSet.jobUID(), "JobUID"); + jobResponseSet.setPatientID("patientID"); + CHECK_QSTRING(jobResponseSet.patientID(), "patientID"); + jobResponseSet.setStudyInstanceUID("studyInstanceUID"); + CHECK_QSTRING(jobResponseSet.studyInstanceUID(), "studyInstanceUID"); + jobResponseSet.setSeriesInstanceUID("seriesInstanceUID"); + CHECK_QSTRING(jobResponseSet.seriesInstanceUID(), "seriesInstanceUID"); + jobResponseSet.setSOPInstanceUID("sopInstanceUID"); + CHECK_QSTRING(jobResponseSet.sopInstanceUID(), "sopInstanceUID"); + jobResponseSet.setConnectionName("connectionName"); + CHECK_QSTRING(jobResponseSet.connectionName(), "connectionName"); + + return EXIT_SUCCESS; +} + diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp new file mode 100644 index 0000000000..1e3ede8685 --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp @@ -0,0 +1,142 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=============================================================================*/ + +// Qt includes +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMInserterJob.h" +#include "ctkDICOMQueryJob.h" +#include "ctkDICOMRetrieveJob.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMStorageListenerJob.h" + +int ctkDICOMJobTest1(int argc, char * argv []) { + + QCoreApplication app(argc, argv); + + // Query Job and virtual parents (ctkDICOMJob and ctkAbstractJob) + ctkDICOMQueryJob queryJob; + + // Test the default values + CHECK_INT(queryJob.status(), ctkAbstractJob::JobStatus::Initialized); + CHECK_BOOL(queryJob.isPersistent(), false); + CHECK_INT(queryJob.retryCounter(), 0); + CHECK_INT(queryJob.retryDelay(), 100); + CHECK_INT(queryJob.maximumNumberOfRetry(), 3); + CHECK_INT(queryJob.maximumConcurrentJobsPerType(), 20); + CHECK_INT(queryJob.priority(), QThread::Priority::LowPriority); + CHECK_INT(queryJob.dicomLevel(), ctkDICOMJob::DICOMLevels::Patients); + CHECK_QSTRING(queryJob.patientID(), ""); + CHECK_QSTRING(queryJob.studyInstanceUID(), ""); + CHECK_QSTRING(queryJob.seriesInstanceUID(), ""); + CHECK_QSTRING(queryJob.sopInstanceUID(), ""); + CHECK_INT(queryJob.maximumPatientsQuery(), 25); + CHECK_POINTER(queryJob.server(), nullptr); + + // Test setting and getting + queryJob.setStatus(ctkAbstractJob::JobStatus::Running); + CHECK_INT(queryJob.status(), ctkAbstractJob::JobStatus::Running); + queryJob.setIsPersistent(true); + CHECK_BOOL(queryJob.isPersistent(), true); + queryJob.setJobUID("JobUID"); + CHECK_QSTRING(queryJob.jobUID(), "JobUID"); + queryJob.setRetryCounter(3); + CHECK_INT(queryJob.retryCounter(), 3); + queryJob.setRetryDelay(300); + CHECK_INT(queryJob.retryDelay(), 300); + queryJob.setMaximumNumberOfRetry(5); + CHECK_INT(queryJob.maximumNumberOfRetry(), 5); + queryJob.setMaximumConcurrentJobsPerType(5); + CHECK_INT(queryJob.maximumConcurrentJobsPerType(), 5); + queryJob.setPriority(QThread::Priority::HighPriority); + CHECK_INT(queryJob.priority(), QThread::Priority::HighPriority); + queryJob.setDICOMLevel(ctkDICOMJob::DICOMLevels::Studies); + CHECK_INT(queryJob.dicomLevel(), ctkDICOMJob::DICOMLevels::Studies); + queryJob.setPatientID("patientID"); + CHECK_QSTRING(queryJob.patientID(), "patientID"); + queryJob.setStudyInstanceUID("studyInstanceUID"); + CHECK_QSTRING(queryJob.studyInstanceUID(), "studyInstanceUID"); + queryJob.setSeriesInstanceUID("seriesInstanceUID"); + CHECK_QSTRING(queryJob.seriesInstanceUID(), "seriesInstanceUID"); + queryJob.setSOPInstanceUID("sopInstanceUID"); + CHECK_QSTRING(queryJob.sopInstanceUID(), "sopInstanceUID"); + queryJob.setMaximumPatientsQuery(100); + CHECK_INT(queryJob.maximumPatientsQuery(), 100); + ctkDICOMServer server; + server.setConnectionName("server"); + queryJob.setServer(server); + CHECK_QSTRING(queryJob.server()->connectionName(), "server"); + + // Inserter Job + ctkDICOMInserterJob inserterJob; + + // Test the default values + CHECK_INT(inserterJob.maximumConcurrentJobsPerType(), 1); + CHECK_QSTRING(inserterJob.databaseFilename(), ""); + QStringList tagsToPrecache; + CHECK_QSTRINGLIST(inserterJob.tagsToPrecache(), tagsToPrecache) + QStringList tagsToExcludeFromStorage; + CHECK_QSTRINGLIST(inserterJob.tagsToExcludeFromStorage(), tagsToExcludeFromStorage) + + // Test setting and getting + inserterJob.setDatabaseFilename("databaseFilename"); + CHECK_QSTRING(inserterJob.databaseFilename(), "databaseFilename"); + tagsToPrecache.append("tagsToPrecache"); + inserterJob.setTagsToPrecache(tagsToPrecache); + CHECK_QSTRINGLIST(inserterJob.tagsToPrecache(), tagsToPrecache) + tagsToExcludeFromStorage.append("tagsToExcludeFromStorage"); + inserterJob.setTagsToExcludeFromStorage(tagsToExcludeFromStorage); + CHECK_QSTRINGLIST(inserterJob.tagsToExcludeFromStorage(), tagsToExcludeFromStorage) + + ctkDICOMRetrieveJob retrieveJob; + + // Test the default values + CHECK_POINTER(retrieveJob.server(), nullptr); + + // Test setting and getting + retrieveJob.setServer(server); + CHECK_QSTRING(retrieveJob.server()->connectionName(), "server"); + + ctkDICOMStorageListenerJob storageListenerJob; + + // Test the default values + CHECK_INT(storageListenerJob.port(), 11112); + CHECK_QSTRING(storageListenerJob.AETitle(), "CTKSTORE"); + CHECK_INT(storageListenerJob.connectionTimeout(), 1); + + // Test setting and getting + storageListenerJob.setPort(80); + CHECK_INT(storageListenerJob.port(), 80); + storageListenerJob.setAETitle("AETitle"); + CHECK_QSTRING(storageListenerJob.AETitle(), "AETitle"); + storageListenerJob.setConnectionTimeout(5); + CHECK_INT(storageListenerJob.connectionTimeout(), 5); + + return EXIT_SUCCESS; +} + diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp index e43dd32a96..74a0ff2b13 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp @@ -53,6 +53,13 @@ int ctkDICOMQueryTest2( int argc, char * argv [] ) tester.storeData(arguments); ctkDICOMDatabase database; + QString dbFile = "./ctkDICOM.sql"; + if (!database.openDatabase(dbFile)) + { + std::cout << "ctkDICOMDatabase::openDatabase() failed" << std::endl; + return EXIT_FAILURE; + } + database.cleanup(true); ctkDICOMQuery query; query.setCallingAETitle("CTK_AE"); diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp index 199c8734ec..a3e2b1a21c 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp @@ -91,7 +91,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] ) QSharedPointer dicomDatabase(new ctkDICOMDatabase); retrieve.setDatabase(dicomDatabase); - if (retrieve.database() != dicomDatabase) + if (retrieve.dicomDatabase() != dicomDatabase) { std::cerr << __LINE__ << ": ctkDICOMRetrieve::setDatabase() failed." << std::endl; diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp index bc38a3df5d..859ec37ef3 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp @@ -58,7 +58,14 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) std::cerr << "ctkDICOMRetrieveTest2: Storing data to dcmqrscp\n"; tester.storeData(arguments); - ctkDICOMDatabase queryDatabase; + ctkDICOMDatabase database; + QString dbFile = "./ctkDICOM.sql"; + if (!database.openDatabase(dbFile)) + { + std::cout << "ctkDICOMDatabase::openDatabase() failed" << std::endl; + return EXIT_FAILURE; + } + database.cleanup(true); std::cerr << "ctkDICOMRetrieveTest2: Setting up query\n"; ctkDICOMQuery query; @@ -68,7 +75,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) query.setPort(tester.dcmqrscpPort()); std::cerr << "ctkDICOMRetrieveTest2: Running query\n"; - bool res = query.query(queryDatabase); + bool res = query.query(database); if (!res) { std::cout << "ctkDICOMQuery::query() failed" << std::endl; @@ -81,10 +88,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) return EXIT_FAILURE; } - std::cerr << "ctkDICOMRetrieveTest2: Setting up retrieve database\n"; - QSharedPointer retrieveDatabase(new ctkDICOMDatabase); - retrieveDatabase->openDatabase( "./ctkDICOM.sql" ); - + std::cerr << "ctkDICOMRetrieveTest2: Setting up retrieve \n"; ctkDICOMRetrieve retrieve; retrieve.setCallingAETitle("CTK_AE"); retrieve.setCalledAETitle("CTK_AE"); @@ -92,7 +96,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) retrieve.setHost("localhost"); retrieve.setMoveDestinationAETitle("CTK_CLIENT_AE"); - retrieve.setDatabase(retrieveDatabase); + retrieve.setDatabase(database); std::cerr << "ctkDICOMRetrieveTest2: Retrieving\n"; typedef QPair StudyAndSeriesInstanceUIDPair; diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp new file mode 100644 index 0000000000..19667143cf --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -0,0 +1,158 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=============================================================================*/ + +// Qt includes +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMQuery.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMTester.h" + +int ctkDICOMSchedulerTest1(int argc, char * argv []) { + + QCoreApplication app(argc, argv); + + // run tester server + ctkDICOMTester tester; + std::cerr << "ctkDICOMSchedulerTest1: Starting dcmqrscp\n"; + tester.startDCMQRSCP(); + QStringList arguments = app.arguments(); + arguments.pop_front(); // remove application name + int numberOfImages = arguments.count(); + if (!numberOfImages) + { + return EXIT_FAILURE; + } + std::cerr << "ctkDICOMSchedulerTest1: Storing data to dcmqrscp\n"; + tester.storeData(arguments); + + ctkDICOMScheduler scheduler; + + // Test the default values + CHECK_INT(scheduler.maximumThreadCount(), 20); + CHECK_INT(scheduler.maximumNumberOfRetry(), 3); + CHECK_INT(scheduler.retryDelay(), 100); + CHECK_INT(scheduler.maximumPatientsQuery(), 25); + + // Test setting and getting + scheduler.setMaximumThreadCount(19); + CHECK_INT(scheduler.maximumThreadCount(), 19); + scheduler.setMaximumNumberOfRetry(5); + CHECK_INT(scheduler.maximumNumberOfRetry(), 5); + scheduler.setRetryDelay(300); + CHECK_INT(scheduler.retryDelay(), 300); + scheduler.setMaximumPatientsQuery(30); + CHECK_INT(scheduler.maximumPatientsQuery(), 30); + + // Test scheduler + std::cerr << "ctkDICOMSchedulerTest1: Setting up scheduler\n"; + ctkDICOMDatabase database; + QString dbFile = "./ctkDICOM.sql"; + QFile file(dbFile); + file.remove(); + QFile cacheFile("./ctkDICOMTagCache.sql"); + cacheFile.remove(); + CHECK_BOOL(database.openDatabase(dbFile), true); + CHECK_BOOL(database.isOpen(), true); + database.cleanup(true); + CHECK_INT(database.patients().count(), 0); + + scheduler.setDicomDatabase(database); + + ctkDICOMServer server; + server.setConnectionName("Test"); + server.setCallingAETitle("CTK_AE"); + server.setCalledAETitle("CTK_AE"); + server.setHost("localhost"); + server.setPort(tester.dcmqrscpPort()); + server.setRetrieveProtocol(ctkDICOMServer::RetrieveProtocol::CGET); + + scheduler.addServer(server); + + QMap filsers; + filsers.insert("ID", "Facial"); + scheduler.setFilters(filsers); + + std::cerr << "ctkDICOMSchedulerTest1: Running queryStudies\n"; + QString patientID = "Facial Expression"; + scheduler.queryStudies(patientID); + scheduler.waitForFinish(); + + CHECK_INT(database.patients().count(), 1); + + QString patientItem = database.patients()[0]; + QStringList studies = database.studiesForPatient(patientItem); + CHECK_INT(studies.count(), 1); + + std::cerr << "ctkDICOMSchedulerTest1: Running querySeries\n"; + QString studyIstanceUID = studies[0]; + scheduler.querySeries(patientID, studyIstanceUID); + scheduler.waitForFinish(); + + QStringList series = database.seriesForStudy(studyIstanceUID); + CHECK_INT(series.count(), 1); + + std::cerr << "ctkDICOMSchedulerTest1: Running queryInstances\n"; + QString seriesIstanceUID = series[0]; + scheduler.queryInstances(patientID, studyIstanceUID, seriesIstanceUID); + scheduler.waitForFinish(); + + QStringList instances = database.instancesForSeries(seriesIstanceUID); + QStringList files = database.filesForSeries(seriesIstanceUID); + files.removeAll(QString("")); + QStringList urls = database.urlsForSeries(seriesIstanceUID); + urls.removeAll(QString("")); + + CHECK_INT(instances.count(),numberOfImages); + CHECK_INT(files.count(), 0); + CHECK_INT(urls.count(), numberOfImages); + + std::cerr << "ctkDICOMSchedulerTest1: Running multiple retrieveSOPInstance." + " This will test " << numberOfImages << " retrieve concorrent jobs\n"; + foreach (QString sopIstanceUID, instances) + { + scheduler.retrieveSOPInstance(patientID, studyIstanceUID, seriesIstanceUID, sopIstanceUID); + } + + CHECK_INT(scheduler.numberOfJobs(), numberOfImages); + scheduler.waitForFinish(); + + instances = database.instancesForSeries(seriesIstanceUID); + files = database.filesForSeries(seriesIstanceUID); + files.removeAll(QString("")); + urls = database.urlsForSeries(seriesIstanceUID); + urls.removeAll(QString("")); + + CHECK_INT(instances.count(), numberOfImages); + CHECK_INT(files.count(), numberOfImages); + CHECK_INT(urls.count(), numberOfImages); + + return EXIT_SUCCESS; +} + diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp new file mode 100644 index 0000000000..4743910fda --- /dev/null +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp @@ -0,0 +1,78 @@ +/*============================================================================= + + Library: CTK + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=============================================================================*/ + +// Qt includes +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMServer.h" + +int ctkDICOMServerTest1(int argc, char * argv []) { + + QCoreApplication app(argc, argv); + + ctkDICOMServer server; + // Test the default values + CHECK_QSTRING(server.connectionName(), ""); + CHECK_QSTRING(server.callingAETitle(), ""); + CHECK_QSTRING(server.calledAETitle(), ""); + CHECK_QSTRING(server.host(), ""); + CHECK_QSTRING(server.retrieveProtocolAsString(), "CGET"); + CHECK_QSTRING(server.moveDestinationAETitle(), ""); + CHECK_INT(server.port(), 80); + CHECK_INT(server.connectionTimeout(), 10); + CHECK_BOOL(server.queryRetrieveEnabled(), true); + CHECK_BOOL(server.storageEnabled(), true); + CHECK_BOOL(server.keepAssociationOpen(), false); + + // Test setting and getting + server.setConnectionName("connectionName"); + CHECK_QSTRING(server.connectionName(), "connectionName"); + server.setCallingAETitle("callingAETitle"); + CHECK_QSTRING(server.callingAETitle(), "callingAETitle"); + server.setCalledAETitle("calledAETitle"); + CHECK_QSTRING(server.calledAETitle(), "calledAETitle"); + server.setHost("host"); + CHECK_QSTRING(server.host(), "host"); + server.setRetrieveProtocolAsString("CMOVE"); + CHECK_QSTRING(server.retrieveProtocolAsString(), "CMOVE"); + server.setMoveDestinationAETitle("moveDestinationAETitle"); + CHECK_QSTRING(server.moveDestinationAETitle(), "moveDestinationAETitle"); + server.setPort(11112); + CHECK_INT(server.port(), 11112); + server.setConnectionTimeout(30); + CHECK_INT(server.connectionTimeout(), 30); + server.setQueryRetrieveEnabled(false); + CHECK_BOOL(server.queryRetrieveEnabled(), false); + server.setStorageEnabled(false); + CHECK_BOOL(server.storageEnabled(), false); + server.setKeepAssociationOpen(true); + CHECK_BOOL(server.keepAssociationOpen(), true); + + return EXIT_SUCCESS; +} + diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index 63f6f8adea..9b6b61aa75 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -27,6 +27,7 @@ // CTK includes #include #include +#include // NOTE: // @@ -43,10 +44,145 @@ class ctkDICOMCorePythonQtDecorators : public QObject ctkDICOMCorePythonQtDecorators() { + PythonQt::self()->registerCPPClass("ctkJobDetail", 0, "CTKDICOMCore"); } public slots: + //---------------------------------------------------------------------------- + // ctkJobDetail + //---------------------------------------------------------------------------- + ctkJobDetail* new_ctkJobDetail() + { + return new ctkJobDetail(); + } + + void setTypeOfJob(ctkJobDetail* td, const ctkDICOMJobResponseSet::JobType& jobType) + { + td->TypeOfJob = jobType; + } + ctkDICOMJobResponseSet::JobType TypeOfJob(ctkJobDetail* td) + { + return td->TypeOfJob; + } + + void setJobUID(ctkJobDetail* td, const QString& jobUID) + { + td->JobUID = jobUID; + } + QString JobUID(ctkJobDetail* td) + { + return td->JobUID; + } + + void setPatientID(ctkJobDetail* td, const QString& patientID) + { + td->PatientID = patientID; + } + QString patientID(ctkJobDetail* td) + { + return td->PatientID; + } + + void setStudyInstanceUID(ctkJobDetail* td, const QString& studyInstanceUID) + { + td->StudyInstanceUID = studyInstanceUID; + } + QString studyInstanceUID(ctkJobDetail* td) + { + return td->StudyInstanceUID; + } + + void setSeriesInstanceUID(ctkJobDetail* td, const QString& seriesInstanceUID) + { + td->SeriesInstanceUID = seriesInstanceUID; + } + QString seriesInstanceUID(ctkJobDetail* td) + { + return td->SeriesInstanceUID; + } + + void setSOPInstanceUID(ctkJobDetail* td, const QString& sopInstanceUID) + { + td->SOPInstanceUID = sopInstanceUID; + } + QString sopInstanceUID(ctkJobDetail* td) + { + return td->SOPInstanceUID; + } + + void setConnectionName(ctkJobDetail* td, const QString& connectionName) + { + td->ConnectionName = connectionName; + } + QString connectionName(ctkJobDetail* td) + { + return td->ConnectionName; + } + + void setNumberOfDataSets(ctkJobDetail* td, const int& numberOfDataSets) + { + td->NumberOfDataSets = numberOfDataSets; + } + int numberOfDataSets(ctkJobDetail* td) + { + return td->NumberOfDataSets; + } + + //---------------------------------------------------------------------------- + // ctkDICOMJobResponseSet + //---------------------------------------------------------------------------- + void setFilePath(ctkDICOMJobResponseSet* ts, const QString& filePath) + { + ts->setFilePath(filePath); + } + + void setCopyFile(ctkDICOMJobResponseSet* ts, const bool& copyFile) + { + ts->setCopyFile(copyFile); + } + + void setOverwriteExistingDataset(ctkDICOMJobResponseSet* ts, const bool& overwriteExistingDataset) + { + ts->setOverwriteExistingDataset(overwriteExistingDataset); + } + + void setTypeOfJob(ctkDICOMJobResponseSet* ts, const ctkDICOMJobResponseSet::JobType& TypeOfJob) + { + ts->setTypeOfJob(TypeOfJob); + } + + void setJobUID(ctkDICOMJobResponseSet* ts, const QString& jobUID) + { + ts->setJobUID(jobUID); + } + + void setPatientID(ctkDICOMJobResponseSet* ts, const QString& patientID) + { + ts->setPatientID(patientID); + } + + void setStudyInstanceUID(ctkDICOMJobResponseSet* ts, const QString& studyInstanceUID) + { + ts->setStudyInstanceUID(studyInstanceUID); + } + + void setSeriesInstanceUID(ctkDICOMJobResponseSet* ts, const QString& seriesInstanceUID) + { + ts->setSeriesInstanceUID(seriesInstanceUID); + } + + void setSOPInstanceUID(ctkDICOMJobResponseSet* ts, const QString& sopInstanceUID) + { + ts->setSOPInstanceUID(sopInstanceUID); + } + + void setConnectionName(ctkDICOMJobResponseSet* ts, const QString& connectionName) + { + ts->setConnectionName(connectionName); + } + + //---------------------------------------------------------------------------- // ctkDICOMDisplayedFieldGeneratorRuleFactory diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index e02d692cab..04eb9cdd2f 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -36,6 +36,7 @@ #include "ctkDICOMDatabase_p.h" #include "ctkDICOMAbstractThumbnailGenerator.h" #include "ctkDICOMItem.h" +#include "ctkDICOMJobResponseSet.h" #include "ctkLogger.h" #include "ctkUtils.h" @@ -75,7 +76,6 @@ static QString TableFieldSeparator(":"); //------------------------------------------------------------------------------ ctkDICOMDatabasePrivate::ctkDICOMDatabasePrivate(ctkDICOMDatabase& o) : q_ptr(&o) - , LoggedExecVerbose(false) , DisplayedFieldsTableAvailable(false) , UseShortStoragePath(true) , ThumbnailGenerator(nullptr) @@ -162,15 +162,12 @@ bool ctkDICOMDatabasePrivate::loggedExec(QSqlQuery& query, const QString& queryS if (!success) { QSqlError sqlError = query.lastError(); - logger.debug( "SQL failed\n Bad SQL: " + query.lastQuery()); - logger.debug( "Error text: " + sqlError.text()); + logger.debug("SQL failed\n Bad SQL: " + query.lastQuery()); + logger.debug("Error text: " + sqlError.text()); } else { - if (LoggedExecVerbose) - { - logger.debug( "SQL worked!\n SQL: " + query.lastQuery()); - } + logger.debug("SQL worked!\n SQL: " + query.lastQuery()); } return (success); } @@ -188,10 +185,7 @@ bool ctkDICOMDatabasePrivate::loggedExecBatch(QSqlQuery& query) } else { - if (LoggedExecVerbose) - { - logger.debug( "SQL worked!\n SQL: " + query.lastQuery()); - } + logger.debug("SQL worked!\n SQL: " + query.lastQuery()); } return (success); } @@ -286,10 +280,7 @@ bool ctkDICOMDatabasePrivate::executeScript(const QString script) { if (! (*it).startsWith("--") ) { - if (LoggedExecVerbose) - { - qDebug() << *it << "\n"; - } + logger.debug(*it + "\n"); query.exec(*it); if (query.lastError().type()) { @@ -324,13 +315,10 @@ bool ctkDICOMDatabasePrivate::insertPatient(const ctkDICOMItem& dataset, int& db // Check if patient is already present in the db - QString patientsName, patientID, studyInstanceUID, seriesInstanceUID; - if (!this->uidsForDataSet(dataset, patientsName, patientID, studyInstanceUID, seriesInstanceUID)) - { - // error occurred, message is already logged - return false; - } - QString patientsBirthDate(dataset.GetElementAsString(DCM_PatientBirthDate)); + QString patientsName, patientID, patientsBirthDate; + patientsName = dataset.GetElementAsString(DCM_PatientName); + patientID = dataset.GetElementAsString(DCM_PatientID); + patientsBirthDate = dataset.GetElementAsString(DCM_PatientBirthDate); QSqlQuery checkPatientExistsQuery(this->Database); checkPatientExistsQuery.prepare("SELECT * FROM Patients WHERE PatientID = ? AND PatientsName = ?"); @@ -343,15 +331,12 @@ bool ctkDICOMDatabasePrivate::insertPatient(const ctkDICOMItem& dataset, int& db { // we found him dbPatientID = checkPatientExistsQuery.value(checkPatientExistsQuery.record().indexOf("UID")).toInt(); - if (this->LoggedExecVerbose) + logger.debug("Found patient in the database as UId: " + QString::number(dbPatientID)); + foreach(QString key, this->InsertedPatientsCompositeIDCache.keys()) { - qDebug() << "Found patient in the database as UId: " << dbPatientID; - foreach(QString key, this->InsertedPatientsCompositeIDCache.keys()) - { - qDebug() << "Patient ID cache item: " << key<< "->" << this->InsertedPatientsCompositeIDCache[key]; - } - qDebug() << "New patient ID cache item: " << compositeID << "->" << dbPatientID; + logger.debug("Patient ID cache item: " + key + "->" + this->InsertedPatientsCompositeIDCache[key]); } + logger.debug("New patient ID cache item: " + compositeID + "->" + dbPatientID); this->InsertedPatientsCompositeIDCache[compositeID] = dbPatientID; return false; } @@ -381,10 +366,7 @@ bool ctkDICOMDatabasePrivate::insertPatient(const ctkDICOMItem& dataset, int& db loggedExec(insertPatientStatement); dbPatientID = insertPatientStatement.lastInsertId().toInt(); this->InsertedPatientsCompositeIDCache[compositeID] = dbPatientID; - if (this->LoggedExecVerbose) - { - logger.debug("New patient inserted: database item ID = " + QString().setNum(dbPatientID)); - } + logger.debug("New patient inserted: database item ID = " + QString().setNum(dbPatientID)); return true; } } @@ -399,10 +381,7 @@ bool ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMItem& dataset, int dbPat checkStudyExistsQuery.exec(); if (!checkStudyExistsQuery.next()) { - if (this->LoggedExecVerbose) - { - qDebug() << "Need to insert new study: " << studyInstanceUID; - } + logger.debug("Need to insert new study: " + studyInstanceUID); QString studyID(dataset.GetElementAsString(DCM_StudyID) ); QString studyDate(dataset.GetElementAsString(DCM_StudyDate) ); @@ -433,7 +412,7 @@ bool ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMItem& dataset, int dbPat insertStudyStatement.addBindValue( QDateTime::currentDateTime() ); if (!insertStudyStatement.exec()) { - logger.error( "Error executing statement: " + insertStudyStatement.lastQuery() + " Error: " + insertStudyStatement.lastError().text() ); + logger.error("Error executing statement: " + insertStudyStatement.lastQuery() + " Error: " + insertStudyStatement.lastError().text() ); } else { @@ -444,10 +423,7 @@ bool ctkDICOMDatabasePrivate::insertStudy(const ctkDICOMItem& dataset, int dbPat } else { - if (this->LoggedExecVerbose) - { - qDebug() << "Used existing study: " << studyInstanceUID; - } + logger.debug("Used existing study: " + studyInstanceUID); this->InsertedStudyUIDsCache.insert(studyInstanceUID); return false; } @@ -460,17 +436,11 @@ bool ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMItem& dataset, QString QSqlQuery checkSeriesExistsQuery(this->Database); checkSeriesExistsQuery.prepare( "SELECT * FROM Series WHERE SeriesInstanceUID = ?" ); checkSeriesExistsQuery.bindValue( 0, seriesInstanceUID ); - if (this->LoggedExecVerbose) - { - logger.warn( "Statement: " + checkSeriesExistsQuery.lastQuery() ); - } + logger.debug("Statement: " + checkSeriesExistsQuery.lastQuery() ); checkSeriesExistsQuery.exec(); if (!checkSeriesExistsQuery.next()) { - if (this->LoggedExecVerbose) - { - qDebug() << "Need to insert new series: " << seriesInstanceUID; - } + logger.debug("Need to insert new series: " + seriesInstanceUID); QString seriesDate(dataset.GetElementAsString(DCM_SeriesDate) ); QString seriesTime(dataset.GetElementAsString(DCM_SeriesTime) ); @@ -520,10 +490,7 @@ bool ctkDICOMDatabasePrivate::insertSeries(const ctkDICOMItem& dataset, QString } else { - if (this->LoggedExecVerbose) - { - qDebug() << "Used existing series: " << seriesInstanceUID; - } + logger.debug("Used existing series: " + seriesInstanceUID); this->InsertedSeriesUIDsCache.insert(seriesInstanceUID); return false; } @@ -537,8 +504,14 @@ bool ctkDICOMDatabasePrivate::openTagCacheDatabase() { return true; } + QString tagCacheConnectionName = this->Database.connectionName() + "TagCache"; + if (QSqlDatabase::contains(tagCacheConnectionName)) + { + QSqlDatabase::removeDatabase(tagCacheConnectionName); + } + this->TagCacheDatabase = QSqlDatabase::addDatabase( - "QSQLITE", this->Database.connectionName() + "TagCache"); + "QSQLITE", tagCacheConnectionName); this->TagCacheDatabase.setDatabaseName(this->TagCacheDatabaseFilename); if ( !this->TagCacheDatabase.open() ) { @@ -583,20 +556,18 @@ void ctkDICOMDatabasePrivate::precacheTags(const ctkDICOMItem& dataset, const QS { value = dataset.GetAllElementValuesAsString(tagKey); } + sopInstanceUIDs << sopInstanceUID; tags << upperTag; values << value; } - this->TagCacheDatabase.transaction(); q->cacheTags(sopInstanceUIDs, tags, values); - this->TagCacheDatabase.commit(); } //------------------------------------------------------------------------------ bool ctkDICOMDatabasePrivate::removeImage(const QString& sopInstanceUID) { - Q_Q(ctkDICOMDatabase); QSqlQuery deleteFile(Database); deleteFile.prepare("DELETE FROM Images WHERE SOPInstanceUID == :sopInstanceUID"); deleteFile.bindValue(":sopInstanceUID", sopInstanceUID); @@ -661,10 +632,7 @@ bool ctkDICOMDatabasePrivate::storeDatasetFile(const ctkDICOMItem& dataset, cons if (originalFilePath.isEmpty()) { - if (this->LoggedExecVerbose) - { - logger.debug("Saving file: " + storedFilePath); - } + logger.debug("Saving file: " + storedFilePath); if (!dataset.SaveToFile(storedFilePath)) { logger.error("Error saving file: " + storedFilePath); @@ -676,10 +644,7 @@ bool ctkDICOMDatabasePrivate::storeDatasetFile(const ctkDICOMItem& dataset, cons // we're inserting an existing file QFile currentFile(originalFilePath); currentFile.copy(storedFilePath); - if (this->LoggedExecVerbose) - { - logger.debug("Copy file from: " + originalFilePath + " to: " + storedFilePath); - } + logger.debug("Copy file from: " + originalFilePath + " to: " + storedFilePath); } return true; @@ -689,7 +654,6 @@ bool ctkDICOMDatabasePrivate::storeDatasetFile(const ctkDICOMItem& dataset, cons bool ctkDICOMDatabasePrivate::indexingStatusForFile(const QString& filePath, const QString& sopInstanceUID, bool& datasetInDatabase, bool& datasetUpToDate, QString& databaseFilename) { - Q_Q(ctkDICOMDatabase); datasetInDatabase = false; datasetUpToDate = false; databaseFilename.clear(); @@ -756,20 +720,15 @@ bool ctkDICOMDatabasePrivate::insertPatientStudySeries(const ctkDICOMItem& datas } else { - if (this->LoggedExecVerbose) - { - qDebug() << "Insert new patient if not already in database: " << patientID << " " << patientsName; - } + logger.debug("Insert new patient if not already in database: " + patientID + " " + patientsName); if (this->insertPatient(dataset, dbPatientID)) { databaseWasChanged = true; emit q->patientAdded(dbPatientID, patientID, patientsName, patientsBirthDate); } } - if (this->LoggedExecVerbose) - { - qDebug() << "Going to insert this instance with dbPatientID: " << dbPatientID; - } + + logger.debug("Going to insert this instance with dbPatientID: " + QString::number(dbPatientID)); // Insert new study if needed QString studyInstanceUID(dataset.GetElementAsString(DCM_StudyInstanceUID)); @@ -777,10 +736,7 @@ bool ctkDICOMDatabasePrivate::insertPatientStudySeries(const ctkDICOMItem& datas { if (this->insertStudy(dataset, dbPatientID)) { - if (this->LoggedExecVerbose) - { - qDebug() << "Study Added"; - } + logger.debug("Study Added"); databaseWasChanged = true; // let users of this class track when things happen emit q->studyAdded(studyInstanceUID); @@ -792,10 +748,7 @@ bool ctkDICOMDatabasePrivate::insertPatientStudySeries(const ctkDICOMItem& datas { if (this->insertSeries(dataset, studyInstanceUID)) { - if (this->LoggedExecVerbose) - { - qDebug() << "Series Added"; - } + logger.debug("Series Added"); databaseWasChanged = true; emit q->seriesAdded(seriesInstanceUID); } @@ -836,7 +789,6 @@ bool ctkDICOMDatabasePrivate::storeThumbnailFile(const QString& originalFilePath bool ctkDICOMDatabasePrivate::uidsForDataSet(const ctkDICOMItem& dataset, QString& patientsName, QString& patientID, QString& studyInstanceUID, QString& seriesInstanceUID) { - Q_Q(ctkDICOMDatabase); // If the following fields can not be evaluated, cancel evaluation of the DICOM file patientsName = dataset.GetElementAsString(DCM_PatientName); patientID = dataset.GetElementAsString(DCM_PatientID); @@ -848,7 +800,6 @@ bool ctkDICOMDatabasePrivate::uidsForDataSet(const ctkDICOMItem& dataset, //------------------------------------------------------------------------------ bool ctkDICOMDatabasePrivate::uidsForDataSet(QString& patientsName, QString& patientID, QString& studyInstanceUID) { - Q_Q(ctkDICOMDatabase); if (patientID.isEmpty() && !studyInstanceUID.isEmpty()) { // Use study instance uid as patient id if patient id is empty - can happen on anonymized datasets @@ -883,20 +834,19 @@ void ctkDICOMDatabasePrivate::insert(const ctkDICOMItem& dataset, const QString& QString sopInstanceUID(dataset.GetElementAsString(DCM_SOPInstanceUID)); // Check to see if the file has already been loaded - if (this->LoggedExecVerbose) - { - qDebug() << "inserting filePath: " << filePath; - } + logger.debug("inserting filePath: " + filePath); // Check if the file has been already indexed and skip indexing if it is bool datasetInDatabase = false; bool datasetUpToDate = false; QString databaseFilename; + if (!indexingStatusForFile(filePath, sopInstanceUID, datasetInDatabase, datasetUpToDate, databaseFilename)) { // error occurred, message is already logged return; } + if (datasetInDatabase) { if (datasetUpToDate) @@ -932,20 +882,16 @@ void ctkDICOMDatabasePrivate::insert(const ctkDICOMItem& dataset, const QString& } bool databaseWasChanged = this->insertPatientStudySeries(dataset, patientID, patientsName); - - if (!storedFilePath.isEmpty() && !seriesInstanceUID.isEmpty()) + if (!sopInstanceUID.isEmpty() && !seriesInstanceUID.isEmpty() && !storedFilePath.isEmpty()) { - if (this->LoggedExecVerbose) - { - qDebug() << "Maybe add Instance"; - } + logger.debug("Maybe add Instance"); bool alreadyInserted = false; if (!storeFile) { // file is linked, maybe it is already inserted QSqlQuery checkImageExistsQuery(Database); - checkImageExistsQuery.prepare("SELECT * FROM Images WHERE Filename = ?"); - checkImageExistsQuery.addBindValue(storedFilePath); + checkImageExistsQuery.prepare("SELECT * FROM Images WHERE SOPInstanceUID = ?"); + checkImageExistsQuery.addBindValue(sopInstanceUID); checkImageExistsQuery.exec(); alreadyInserted = checkImageExistsQuery.next(); } @@ -971,17 +917,23 @@ void ctkDICOMDatabasePrivate::insert(const ctkDICOMItem& dataset, const QString& insertImageStatement.addBindValue(QString("")); insertImageStatement.addBindValue(seriesInstanceUID); insertImageStatement.addBindValue(QDateTime::currentDateTime()); - insertImageStatement.exec(); - // insert was needed, so cache any application-requested tags - this->precacheTags(dataset, sopInstanceUID); + if ( !insertImageStatement.exec() ) + { + logger.error("Error executing statement: " + + insertImageStatement.lastQuery() + + " Error: " + insertImageStatement.lastError().text()); + } + else + { + // insert was needed, so cache any application-requested tags + this->precacheTags(dataset, sopInstanceUID); + } // let users of this class track when things happen emit q->instanceAdded(sopInstanceUID); - if (this->LoggedExecVerbose) - { - qDebug() << "Instance Added"; - } + + logger.debug("Instance Added"); databaseWasChanged = true; } if (generateThumbnail) @@ -995,154 +947,6 @@ void ctkDICOMDatabasePrivate::insert(const ctkDICOMItem& dataset, const QString& } } -//------------------------------------------------------------------------------ -void ctkDICOMDatabase::insert(const QList& indexingResults) -{ - Q_D(ctkDICOMDatabase); - bool databaseWasChanged = false; - - d->TagCacheDatabase.transaction(); - d->Database.transaction(); - - QDir databaseDirectory(this->databaseDirectory()); - foreach(const ctkDICOMDatabase::IndexingResult & indexingResult, indexingResults) - { - const ctkDICOMItem& dataset = *indexingResult.dataset.data(); - QString filePath = indexingResult.filePath; - bool generateThumbnail = false; // thumbnail will be generated when needed, don't slow down import with that - bool storeFile = indexingResult.copyFile; - - // Check to see if the file has already been loaded - QString sopInstanceUID(dataset.GetElementAsString(DCM_SOPInstanceUID)); - bool datasetInDatabase = false; - bool datasetUpToDate = false; - if (indexingResult.overwriteExistingDataset) - { - // overwrite was requested based on exact file match - datasetInDatabase = true; - datasetUpToDate = false; - } - else - { - // there is no exact file match, but there may be still a different file in the database - // for the same SOP instance UID - QString databaseFilename; - if (!d->indexingStatusForFile(filePath, sopInstanceUID, datasetInDatabase, datasetUpToDate, databaseFilename)) - { - // error occurred, message is already logged - continue; - } - } - - if (datasetInDatabase) - { - if (datasetUpToDate) - { - continue; - } - // File is updated, delete record and re-index - if (!d->removeImage(sopInstanceUID)) - { - logger.error("Failed to insert file into database (cannot update pre-existing item): " + filePath); - continue; - } - } - - // Verify that minimum required fields are present - QString patientsName, patientID, studyInstanceUID, seriesInstanceUID; - if (!d->uidsForDataSet(dataset, patientsName, patientID, studyInstanceUID, seriesInstanceUID)) - { - logger.error("Failed to insert file into database (required fields missing): " + filePath); - continue; - } - - // Store a copy of the dataset - QString storedFilePath = filePath; - if (storeFile && !seriesInstanceUID.isEmpty() && !this->isInMemory()) - { - if (!d->storeDatasetFile(dataset, filePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID, storedFilePath)) - { - continue; - } - } - - if (d->insertPatientStudySeries(dataset, patientID, patientsName)) - { - databaseWasChanged = true; - } - - if (!storedFilePath.isEmpty() && !seriesInstanceUID.isEmpty()) - { - // Insert all pre-cached fields into tag cache - QSqlQuery insertTags(d->TagCacheDatabase); - insertTags.prepare("INSERT OR REPLACE INTO TagCache VALUES(?,?,?)"); - insertTags.bindValue(0, sopInstanceUID); - foreach(const QString & tag, d->TagsToPrecache) - { - QString upperTag = tag.toUpper(); - unsigned short group, element; - this->tagToGroupElement(upperTag, group, element); - DcmTagKey tagKey(group, element); - QString value; - if (d->TagsToExcludeFromStorage.contains(upperTag)) - { - if (dataset.TagExists(tagKey)) - { - value = ValueIsNotStored; - } - else - { - value = TagNotInInstance; - } - } - else - { - value = dataset.GetAllElementValuesAsString(tagKey); - } - insertTags.bindValue(1, upperTag); - if (value.isEmpty()) - { - insertTags.bindValue(2, TagNotInInstance); - } - else - { - insertTags.bindValue(2, value); - } - insertTags.exec(); - } - - // Insert image files - QSqlQuery insertImageStatement(d->Database); - insertImageStatement.prepare("INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'URL', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ?, ? )"); - insertImageStatement.addBindValue(sopInstanceUID); - insertImageStatement.addBindValue(d->internalPathFromAbsolute(storedFilePath)); - insertImageStatement.addBindValue(QString("")); - insertImageStatement.addBindValue(seriesInstanceUID); - insertImageStatement.addBindValue(QDateTime::currentDateTime()); - insertImageStatement.exec(); - emit instanceAdded(sopInstanceUID); - if (d->LoggedExecVerbose) - { - qDebug() << "Instance Added"; - } - databaseWasChanged = true; - - if (generateThumbnail) - { - d->storeThumbnailFile(storedFilePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID); - } - } - } - - d->Database.commit(); - d->TagCacheDatabase.commit(); - - if (databaseWasChanged && this->isInMemory()) - { - emit this->databaseChanged(); - } -} - //------------------------------------------------------------------------------ QString ctkDICOMDatabasePrivate::getDisplayPatientFieldsKey(const QString& patientID, const QString& patientsName, const QString& patientsBirthDate, @@ -1335,6 +1139,10 @@ bool ctkDICOMDatabasePrivate::applyDisplayedFieldsChanges( QMap currentStudy = displayedFieldsMapStudy[currentStudyInstanceUid]; QSqlQuery displayStudiesQuery(this->Database); displayStudiesQuery.prepare("SELECT StudyInstanceUID FROM Studies WHERE StudyInstanceUID = ? ;"); @@ -1388,6 +1196,10 @@ bool ctkDICOMDatabasePrivate::applyDisplayedFieldsChanges( QMap currentSeries = displayedFieldsMapSeries[currentSeriesInstanceUid]; QSqlQuery displaySeriesQuery(this->Database); @@ -1485,7 +1297,8 @@ ctkDICOMDatabase::~ctkDICOMDatabase() } //------------------------------------------------------------------------------ -void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& connectionName ) +bool ctkDICOMDatabase::openDatabase(const QString& databaseFile, + const QString& connectionName) { Q_D(ctkDICOMDatabase); bool wasOpen = this->isOpen(); @@ -1510,16 +1323,22 @@ void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& c { verifiedConnectionName = QUuid::createUuid().toString(); } + + if (QSqlDatabase::contains(verifiedConnectionName)) + { + QSqlDatabase::removeDatabase(verifiedConnectionName); + } + d->Database = QSqlDatabase::addDatabase("QSQLITE", verifiedConnectionName); d->Database.setDatabaseName(databaseFile); - if ( ! (d->Database.open()) ) + if (!(d->Database.open())) { d->LastError = d->Database.lastError().text(); if (wasOpen) { emit closed(); } - return; + return false; } // Disable synchronous writing to make modifications faster @@ -1536,7 +1355,7 @@ void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& c { emit closed(); } - return; + return false; } } d->resetLastInsertedValues(); @@ -1567,6 +1386,7 @@ void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& c this->setTagsToPrecache(tags); emit opened(); + return true; } //------------------------------------------------------------------------------ @@ -2366,17 +2186,29 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag) { Q_D(ctkDICOMDatabase); + if (fileName.isEmpty()) + { + return ""; + } + // Read from cache, if available // first, try treating argument as filePath QString sopInstanceUID = this->instanceForFile(fileName); // second, try treating argument as a url + bool isUrl = false; if (sopInstanceUID.isEmpty()) { + isUrl = true; sopInstanceUID = this->instanceForURL(fileName); } + if (sopInstanceUID.isEmpty()) + { + return ""; + } + // third, look for the value tag = tag.toUpper(); QString value = this->cachedTag(sopInstanceUID, tag); @@ -2389,7 +2221,12 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag) return value; } - // Read value from file as a fallback (won't work if fileName is a URL) + if (isUrl) + { + return ""; + } + + // Read value from file as a fallback value = d->readValueFromFile(fileName, sopInstanceUID, tag); return value; } @@ -2397,7 +2234,6 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag) //------------------------------------------------------------------------------ QString ctkDICOMDatabase::fileValue(const QString fileName, const unsigned short group, const unsigned short element) { - Q_D(ctkDICOMDatabase); QString tag = this->groupElementToTag(group, element); return this->fileValue(fileName, tag); } @@ -2438,7 +2274,14 @@ bool ctkDICOMDatabase::instanceValueExists(const QString sopInstanceUID, const u bool ctkDICOMDatabase::fileValueExists(const QString fileName, QString tag) { Q_D(ctkDICOMDatabase); + + if (fileName.isEmpty()) + { + return false; + } + tag = tag.toUpper(); + QString sopInstanceUID = this->instanceForFile(fileName); QString value = this->cachedTag(sopInstanceUID, tag); if (value == TagNotInInstance || value == ValueIsEmptyString) @@ -2458,7 +2301,6 @@ bool ctkDICOMDatabase::fileValueExists(const QString fileName, QString tag) //------------------------------------------------------------------------------ bool ctkDICOMDatabase::fileValueExists(const QString fileName, const unsigned short group, const unsigned short element) { - Q_D(ctkDICOMDatabase); QString tag = this->groupElementToTag(group, element); return this->fileValueExists(fileName, tag); } @@ -2521,14 +2363,6 @@ void ctkDICOMDatabase::insert( const ctkDICOMItem& dataset, bool storeFile, bool d->insert(dataset, QString(), storeFile, generateThumbnail); } -//------------------------------------------------------------------------------ -void ctkDICOMDatabase::insert(const QString& filePath, const ctkDICOMItem& dataset, - bool storeFile, bool generateThumbnail) -{ - Q_D(ctkDICOMDatabase); - d->insert(dataset, filePath, storeFile, generateThumbnail); -} - //------------------------------------------------------------------------------ void ctkDICOMDatabase::insert( const QString& filePath, bool storeFile, bool generateThumbnail, bool createHierarchy, const QString& destinationDirectoryName) { @@ -2543,10 +2377,7 @@ void ctkDICOMDatabase::insert( const QString& filePath, bool storeFile, bool gen return; } - if (d->LoggedExecVerbose) - { - logger.debug( "Processing " + filePath ); - } + logger.debug( "Processing " + filePath ); ctkDICOMItem dataset; @@ -2562,10 +2393,423 @@ void ctkDICOMDatabase::insert( const QString& filePath, bool storeFile, bool gen } //------------------------------------------------------------------------------ -void ctkDICOMDatabase::setTagsToPrecache(const QStringList tags) +void ctkDICOMDatabase::insert(const QList& indexingResults) { Q_D(ctkDICOMDatabase); - if (d->TagsToPrecache == tags) + bool databaseWasChanged = false; + + d->TagCacheDatabase.transaction(); + d->Database.transaction(); + + QDir databaseDirectory(this->databaseDirectory()); + foreach(const ctkDICOMDatabase::IndexingResult & indexingResult, indexingResults) + { + const ctkDICOMItem& dataset = *indexingResult.dataset.data(); + QString filePath = indexingResult.filePath; + bool generateThumbnail = false; // thumbnail will be generated when needed, don't slow down import with that + bool storeFile = indexingResult.copyFile; + + // Check to see if the file has already been loaded + QString sopInstanceUID(dataset.GetElementAsString(DCM_SOPInstanceUID)); + bool datasetInDatabase = false; + bool datasetUpToDate = false; + if (indexingResult.overwriteExistingDataset) + { + // overwrite was requested based on exact file match + datasetInDatabase = true; + datasetUpToDate = false; + } + else + { + // there is no exact file match, but there may be still a different file in the database + // for the same SOP instance UID + QString databaseFilename; + if (!d->indexingStatusForFile(filePath, sopInstanceUID, datasetInDatabase, datasetUpToDate, databaseFilename)) + { + // error occurred, message is already logged + continue; + } + } + + if (datasetInDatabase) + { + if (datasetUpToDate) + { + continue; + } + // File is updated, delete record and re-index + if (!d->removeImage(sopInstanceUID)) + { + logger.error("Failed to insert file into database (cannot update pre-existing item): " + filePath); + continue; + } + } + + // Verify that minimum required fields are present + QString patientsName, patientID, studyInstanceUID, seriesInstanceUID; + if (!d->uidsForDataSet(dataset, patientsName, patientID, studyInstanceUID, seriesInstanceUID)) + { + logger.error("Failed to insert file into database (required fields missing): " + filePath); + continue; + } + + // Store a copy of the dataset + QString storedFilePath = filePath; + if (storeFile && !seriesInstanceUID.isEmpty() && !this->isInMemory()) + { + if (!d->storeDatasetFile(dataset, filePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID, storedFilePath)) + { + continue; + } + } + + if (d->insertPatientStudySeries(dataset, patientID, patientsName)) + { + databaseWasChanged = true; + } + + if (!storedFilePath.isEmpty() && !seriesInstanceUID.isEmpty()) + { + // Insert all pre-cached fields into tag cache + QSqlQuery insertTags(d->TagCacheDatabase); + insertTags.prepare("INSERT OR REPLACE INTO TagCache VALUES(?,?,?)"); + insertTags.bindValue(0, sopInstanceUID); + foreach(const QString & tag, d->TagsToPrecache) + { + unsigned short group, element; + this->tagToGroupElement(tag, group, element); + DcmTagKey tagKey(group, element); + QString value; + if (d->TagsToExcludeFromStorage.contains(tag)) + { + if (dataset.TagExists(tagKey)) + { + value = ValueIsNotStored; + } + else + { + value = TagNotInInstance; + } + } + else + { + value = dataset.GetAllElementValuesAsString(tagKey); + } + insertTags.bindValue(1, tag); + if (value.isEmpty()) + { + insertTags.bindValue(2, TagNotInInstance); + } + else + { + insertTags.bindValue(2, value); + } + insertTags.exec(); + } + + // Insert image files + QSqlQuery insertImageStatement(d->Database); + insertImageStatement.prepare("INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'URL', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ?, ? )"); + insertImageStatement.addBindValue(sopInstanceUID); + insertImageStatement.addBindValue(d->internalPathFromAbsolute(storedFilePath)); + insertImageStatement.addBindValue(QString("")); + insertImageStatement.addBindValue(seriesInstanceUID); + insertImageStatement.addBindValue(QDateTime::currentDateTime()); + insertImageStatement.exec(); + emit instanceAdded(sopInstanceUID); + logger.debug( "Instance Added" ); + databaseWasChanged = true; + + if (generateThumbnail) + { + d->storeThumbnailFile(storedFilePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID); + } + } + } + + d->Database.commit(); + d->TagCacheDatabase.commit(); + + if (databaseWasChanged && this->isInMemory()) + { + emit this->databaseChanged(); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabase::insert(QList> jobResponseSets) +{ + Q_D(ctkDICOMDatabase); + + bool databaseWasChanged = false; + + d->TagCacheDatabase.transaction(); + d->Database.transaction(); + + QDir databaseDirectory(this->databaseDirectory()); + foreach (QSharedPointer jobResponseSet, jobResponseSets) + { + ctkDICOMJobResponseSet::JobType typeOfTask = jobResponseSet->typeOfJob(); + QString filePath = jobResponseSet->filePath(); + QString url; + bool generateThumbnail = false; // thumbnail will be generated when needed, don't slow down import with that + bool storeFile = jobResponseSet->copyFile(); + + QMap datasets = jobResponseSet->datasets(); + for(QString key : datasets.keys()) + { + ctkDICOMItem* dataset = datasets.value(key); + if (!dataset) + { + continue; + } + QString patientID, patientName, studyInstanceUID, seriesInstanceUID, sopInstanceUID; + patientName = dataset->GetElementAsString(DCM_PatientName); + patientID = dataset->GetElementAsString(DCM_PatientID); + studyInstanceUID = dataset->GetElementAsString(DCM_StudyInstanceUID); + seriesInstanceUID = dataset->GetElementAsString(DCM_SeriesInstanceUID); + sopInstanceUID = dataset->GetElementAsString(DCM_SOPInstanceUID); + + if (patientID.isEmpty()) + { + if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryPatients) + { + patientID = key; + } + else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryStudies || + typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + { + patientID = jobResponseSet->patientID(); + } + + dataset->SetElementAsString(DCM_PatientID, patientID); + } + + if (studyInstanceUID.isEmpty()) + { + if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryStudies) + { + studyInstanceUID = key; + } + else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + { + studyInstanceUID = jobResponseSet->studyInstanceUID(); + } + + dataset->SetElementAsString(DCM_StudyInstanceUID, studyInstanceUID); + } + + if (patientName.isEmpty() && !studyInstanceUID.isEmpty()) + { + QString patientUID = this->patientForStudy(studyInstanceUID); + patientName = this->nameForPatient(patientUID); + dataset->SetElementAsString(DCM_PatientName, patientName); + } + + if (seriesInstanceUID.isEmpty()) + { + if (typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries) + { + seriesInstanceUID = key; + } + else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + { + seriesInstanceUID = jobResponseSet->seriesInstanceUID(); + } + + dataset->SetElementAsString(DCM_SeriesInstanceUID, seriesInstanceUID); + } + + if (sopInstanceUID.isEmpty()) + { + if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances) + { + sopInstanceUID = key; + } + else if (typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + typeOfTask == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) + { + sopInstanceUID = jobResponseSet->sopInstanceUID(); + } + + dataset->SetElementAsString(DCM_SOPInstanceUID, sopInstanceUID); + } + + if (patientID.isEmpty()) + { + logger.error("ctkDICOMDatabase::insert: dataset has no patientID"); + continue; + } + + if (patientName.isEmpty()) + { + logger.error("ctkDICOMDatabase::insert: dataset has no patientName"); + continue; + } + + if (studyInstanceUID.isEmpty() && typeOfTask != ctkDICOMJobResponseSet::JobType::QueryPatients) + { + logger.error("ctkDICOMDatabase::insert: dataset has no studyInstanceUID"); + continue; + } + + if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances) + { + url = "dimse+ctk://" + jobResponseSet->connectionName(); + if (!studyInstanceUID.isEmpty()) + { + url += "/" + studyInstanceUID; + } + if (!seriesInstanceUID.isEmpty()) + { + url += "/" + seriesInstanceUID; + } + if (!sopInstanceUID.isEmpty()) + { + url += "/" + sopInstanceUID; + } + } + + // Check to see if the file has already been loaded + bool datasetInDatabase = false; + bool datasetUpToDate = false; + if (jobResponseSet->overwriteExistingDataset()) + { + // overwrite was requested based on exact file match + datasetInDatabase = true; + datasetUpToDate = false; + } + else + { + // there is no exact file match, but there may be still a different file in the database + // for the same SOP instance UID + QString databaseFilename; + if (!d->indexingStatusForFile(filePath, sopInstanceUID, datasetInDatabase, datasetUpToDate, databaseFilename)) + { + // error occurred, message is already logged + continue; + } + } + + if (datasetInDatabase) + { + if (datasetUpToDate) + { + continue; + } + // File is updated, delete record and re-index + if (!d->removeImage(sopInstanceUID)) + { + logger.error("Failed to insert file into database (cannot update pre-existing item): " + filePath); + continue; + } + } + + // Store a copy of the dataset + QString storedFilePath = filePath; + if (storeFile && !seriesInstanceUID.isEmpty() && !this->isInMemory()) + { + if (!d->storeDatasetFile(*dataset, filePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID, storedFilePath)) + { + continue; + } + } + + if (d->insertPatientStudySeries(*dataset, patientID, patientName)) + { + databaseWasChanged = true; + } + + if (!sopInstanceUID.isEmpty() && + !seriesInstanceUID.isEmpty() && + (!storedFilePath.isEmpty() || + !url.isEmpty())) + { + logger.debug( "Maybe add Instance" ); + bool alreadyInserted = false; + if (!storeFile) + { + // file is linked, maybe it is already inserted + QSqlQuery checkImageExistsQuery(d->Database); + checkImageExistsQuery.prepare("SELECT * FROM Images WHERE SOPInstanceUID = ?"); + checkImageExistsQuery.addBindValue(sopInstanceUID); + checkImageExistsQuery.exec(); + alreadyInserted = checkImageExistsQuery.next(); + } + if (!alreadyInserted) + { + // Get filename that will be stored in the database. + // Use relative path if a copy is stored in the database to make the database relocatable. + QString storedFilePathInDatabase; + if (storeFile) + { + QDir databaseDirectory(this->databaseDirectory()); + storedFilePathInDatabase = databaseDirectory.relativeFilePath(storedFilePath); + } + else + { + storedFilePathInDatabase = storedFilePath; + } + + QSqlQuery insertImageStatement(d->Database); + insertImageStatement.prepare("INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'URL', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ?, ? )"); + insertImageStatement.addBindValue(sopInstanceUID); + insertImageStatement.addBindValue(storedFilePathInDatabase); + insertImageStatement.addBindValue("url"); + insertImageStatement.addBindValue(seriesInstanceUID); + insertImageStatement.addBindValue(QDateTime::currentDateTime()); + + if ( !insertImageStatement.exec() ) + { + logger.error( "Error executing statement: " + + insertImageStatement.lastQuery() + + " Error: " + insertImageStatement.lastError().text() ); + } + else + { + // insert was needed, so cache any application-requested tags + d->precacheTags(*dataset, sopInstanceUID); + } + + // let users of this class track when things happen + emit instanceAdded(sopInstanceUID); + logger.debug( "Instance Added" ); + databaseWasChanged = true; + } + if (generateThumbnail) + { + d->storeThumbnailFile(storedFilePath, studyInstanceUID, seriesInstanceUID, sopInstanceUID); + } + } + } + } + + d->Database.commit(); + d->TagCacheDatabase.commit(); + + if (databaseWasChanged && this->isInMemory()) + { + emit this->databaseChanged(); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabase::setTagsToPrecache(const QStringList tags) +{ + Q_D(ctkDICOMDatabase); + if (d->TagsToPrecache == tags) { return; } @@ -2659,7 +2903,7 @@ bool ctkDICOMDatabase::isInMemory() const } //------------------------------------------------------------------------------ -bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID, bool clearCachedTags/*=true*/) +bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID, bool clearCachedTags/*=false*/, bool cleanup/*=true*/) { Q_D(ctkDICOMDatabase); @@ -2722,22 +2966,23 @@ bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID, bool clear if (QFileInfo(dbFilePath).isRelative()) { QString absPath = d->absolutePathFromInternal(dbFilePath); - if (QFile(absPath).remove()) + QFile file(absPath); + if (file.exists()) { - if (d->LoggedExecVerbose) + if (file.remove()) { logger.debug("Removed file " + absPath); + QString fileFolder = QFileInfo(absPath).absoluteDir().path(); + if (foldersToRemove.isEmpty() || foldersToRemove.last() != fileFolder) + { + foldersToRemove << fileFolder; + } } - QString fileFolder = QFileInfo(absPath).absoluteDir().path(); - if (foldersToRemove.isEmpty() || foldersToRemove.last() != fileFolder) + else { - foldersToRemove << fileFolder; + logger.warn("Failed to remove file " + absPath); } } - else - { - logger.warn("Failed to remove file " + absPath); - } } // Remove thumbnail (if exists) QFile thumbnailFile(d->absolutePathFromInternal(thumbnailPath)); @@ -2762,7 +3007,10 @@ bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID, bool clear QDir().rmpath(folderToRemove); } - this->cleanup(); + if (cleanup) + { + this->cleanup(); + } d->resetLastInsertedValues(); @@ -2790,7 +3038,7 @@ bool ctkDICOMDatabase::cleanup(bool vacuum/*=false*/) } //------------------------------------------------------------------------------ -bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID) +bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID, bool cleanup/*=true*/) { Q_D(ctkDICOMDatabase); @@ -2807,7 +3055,7 @@ bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID) while ( seriesForStudy.next() ) { QString seriesInstanceUID = seriesForStudy.value(seriesForStudy.record().indexOf("SeriesInstanceUID")).toString(); - if ( ! this->removeSeries(seriesInstanceUID) ) + if ( ! this->removeSeries(seriesInstanceUID, false, cleanup) ) { result = false; } @@ -2823,7 +3071,7 @@ bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID) } //------------------------------------------------------------------------------ -bool ctkDICOMDatabase::removePatient(const QString& patientID) +bool ctkDICOMDatabase::removePatient(const QString& patientID, bool cleanup/*=true*/) { Q_D(ctkDICOMDatabase); @@ -2840,7 +3088,7 @@ bool ctkDICOMDatabase::removePatient(const QString& patientID) while ( studiesForPatient.next() ) { QString studyInstanceUID = studiesForPatient.value(studiesForPatient.record().indexOf("StudyInstanceUID")).toString(); - if ( ! this->removeStudy(studyInstanceUID) ) + if ( ! this->removeStudy(studyInstanceUID, cleanup) ) { result = false; } @@ -3356,3 +3604,31 @@ QString ctkDICOMDatabase::compositePatientID(const QString& patientID, const QSt { return QString("%1~%2~%3").arg(patientID).arg(patientsBirthDate).arg(patientsName); } + +//------------------------------------------------------------------------------ +void ctkDICOMDatabase::setLoadedSeries(const QStringList &seriesList) +{ + Q_D(ctkDICOMDatabase); + d->LoadedSeries = seriesList; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMDatabase::loadedSeries() const +{ + Q_D(const ctkDICOMDatabase); + return d->LoadedSeries; +} + +//------------------------------------------------------------------------------ +void ctkDICOMDatabase::setVisibleSeries(const QStringList &seriesList) +{ + Q_D(ctkDICOMDatabase); + d->VisibleSeries = seriesList; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMDatabase::visibleSeries() const +{ + Q_D(const ctkDICOMDatabase); + return d->VisibleSeries; +} diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.h b/Libs/DICOM/Core/ctkDICOMDatabase.h index 6f6a30d41d..03bbeca5be 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.h +++ b/Libs/DICOM/Core/ctkDICOMDatabase.h @@ -34,6 +34,7 @@ class ctkDICOMDatabasePrivate; class DcmDataset; class ctkDICOMAbstractThumbnailGenerator; class ctkDICOMDisplayedFieldGenerator; +class ctkDICOMJobResponseSet; /// \ingroup DICOM_Core /// @@ -52,7 +53,6 @@ class ctkDICOMDisplayedFieldGenerator; /// parallel to "dicom" directory called "thumbs". class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject { - Q_OBJECT Q_PROPERTY(bool isOpen READ isOpen) Q_PROPERTY(bool isInMemory READ isInMemory) @@ -64,6 +64,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject Q_PROPERTY(QStringList patientFieldNames READ patientFieldNames) Q_PROPERTY(QStringList studyFieldNames READ studyFieldNames) Q_PROPERTY(QStringList seriesFieldNames READ seriesFieldNames) + Q_PROPERTY(QStringList loadedSeries READ loadedSeries WRITE setLoadedSeries) + Q_PROPERTY(QStringList visibleSeries READ visibleSeries WRITE setVisibleSeries) Q_PROPERTY(bool useShortStoragePath READ useShortStoragePath WRITE setUseShortStoragePath) public: @@ -118,7 +120,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// must be avoided as it breaks previously created database object /// that used the same connection name). /// @param update the schema if it is found to be out of date - Q_INVOKABLE virtual void openDatabase(const QString databaseFile, + Q_INVOKABLE virtual bool openDatabase(const QString& databaseFile, const QString& connectionName = ""); /// Close the database. It must not be used afterwards. @@ -259,11 +261,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject bool storeFile = true, bool generateThumbnail = true, bool createHierarchy = true, const QString& destinationDirectoryName = QString() ); - - Q_INVOKABLE void insert(const QString& filePath, const ctkDICOMItem& ctkDataset, - bool storeFile = true, bool generateThumbnail = true); - Q_INVOKABLE void insert(const QList& indexingResults); + Q_INVOKABLE void insert(QList> jobResponseSets); /// When a DICOM file is stored in the database (insert is called with storeFile=true) then /// path is constructed from study, series, and SOP instance UID. @@ -309,9 +308,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// if set to False the they are left in the database unchanged. /// By default clearCachedTags is disabled because it significantly increases deletion time /// on large databases. - Q_INVOKABLE bool removeSeries(const QString& seriesInstanceUID, bool clearCachedTags=false); - Q_INVOKABLE bool removeStudy(const QString& studyInstanceUID); - Q_INVOKABLE bool removePatient(const QString& patientID); + Q_INVOKABLE bool removeSeries(const QString& seriesInstanceUID, bool clearCachedTags=false, bool cleanup=true); + Q_INVOKABLE bool removeStudy(const QString& studyInstanceUID, bool cleanup=true); + Q_INVOKABLE bool removePatient(const QString& patientID, bool cleanup=true); /// Remove all patients, studies, series, which do not have associated images. /// If vacuum is set to true then the whole database content is attempted to /// cleaned from remnants of all previously deleted data from the file. @@ -404,6 +403,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject /// inserted under the same patient. Q_INVOKABLE static QString compositePatientID(const QString& patientID, const QString& patientsName, const QString& patientsBirthDate); + /// Set a list of loaded series + void setLoadedSeries(const QStringList& seriesList); + QStringList loadedSeries() const; + + /// Set a list of visible series + void setVisibleSeries(const QStringList& seriesList); + QStringList visibleSeries() const; + Q_SIGNALS: /// Things inserted to database. diff --git a/Libs/DICOM/Core/ctkDICOMDatabase_p.h b/Libs/DICOM/Core/ctkDICOMDatabase_p.h index e76b94480a..f9595ff0fd 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase_p.h +++ b/Libs/DICOM/Core/ctkDICOMDatabase_p.h @@ -53,7 +53,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabasePrivate bool loggedExec(QSqlQuery& query); bool loggedExec(QSqlQuery& query, const QString& queryString); bool loggedExecBatch(QSqlQuery& query); - bool LoggedExecVerbose; bool removeImage(const QString& sopInstanceUID); @@ -172,6 +171,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabasePrivate /// Facilitate using custom schema with the database without subclassing QString SchemaVersion; + + QStringList LoadedSeries; + QStringList VisibleSeries; }; #endif diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp new file mode 100644 index 0000000000..96c508b6e7 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -0,0 +1,271 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMEcho.h" +#include "ctkLogger.h" + +// DCMTK includes +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for class OFStandard */ +#include /* for class DicomDirInterface */ + +static ctkLogger logger ( "org.commontk.dicom.DICOMEcho" ); + +//------------------------------------------------------------------------------ +class ctkDICOMEchoPrivate +{ +public: + ctkDICOMEchoPrivate(); + ~ctkDICOMEchoPrivate(); + + QString ConnectionName; + QString CallingAETitle; + QString CalledAETitle; + QString Host; + int Port; + DcmSCU SCU; +}; + +//------------------------------------------------------------------------------ +// ctkDICOMEchoPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMEchoPrivate::ctkDICOMEchoPrivate() +{ + this->ConnectionName = ""; + this->CallingAETitle = ""; + this->CalledAETitle = ""; + this->Host = ""; + this->Port = 80; + + this->SCU.setACSETimeout(3); + this->SCU.setConnectionTimeout(3); +} + +//------------------------------------------------------------------------------ +ctkDICOMEchoPrivate::~ctkDICOMEchoPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMEcho methods + +//------------------------------------------------------------------------------ +ctkDICOMEcho::ctkDICOMEcho(QObject* parentObject) + : QObject(parentObject) + , d_ptr(new ctkDICOMEchoPrivate) +{ + Q_D(ctkDICOMEcho); + + d->SCU.setVerbosePCMode(false); + + this->setDCMTKLogLevel(logger.logLevel()); +} + +//------------------------------------------------------------------------------ +ctkDICOMEcho::~ctkDICOMEcho() +{ +} + +//----------------------------------------------------------------------------- +void ctkDICOMEcho::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) +{ + OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; + if (level == ctkErrorLogLevel::LogLevel::Fatal) + { + dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Critical || + level == ctkErrorLogLevel::LogLevel::Error) + { + dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Warning) + { + dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Info) + { + dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Debug) + { + dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Trace || + level == ctkErrorLogLevel::LogLevel::Status) + { + dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; + } + + OFLog::configure(dcmtkLogLevel); +} + +//----------------------------------------------------------------------------- +ctkErrorLogLevel::LogLevel ctkDICOMEcho::DCMTKLogLevel() const +{ + return logger.logLevel(); +} + +/// Set methods for connectivity +//------------------------------------------------------------------------------ +void ctkDICOMEcho::setConnectionName(const QString& connectionName) +{ + Q_D(ctkDICOMEcho); + d->ConnectionName = connectionName; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMEcho::connectionName() const +{ + Q_D(const ctkDICOMEcho); + return d->ConnectionName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMEcho::setCallingAETitle(const QString& callingAETitle) +{ + Q_D(ctkDICOMEcho); + d->CallingAETitle = callingAETitle; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMEcho::callingAETitle() const +{ + Q_D(const ctkDICOMEcho); + return d->CallingAETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMEcho::setCalledAETitle(const QString& calledAETitle) +{ + Q_D(ctkDICOMEcho); + d->CalledAETitle = calledAETitle; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMEcho::calledAETitle()const +{ + Q_D(const ctkDICOMEcho); + return d->CalledAETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMEcho::setHost(const QString& host) +{ + Q_D(ctkDICOMEcho); + d->Host = host; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMEcho::host() const +{ + Q_D(const ctkDICOMEcho); + return d->Host; +} + +//------------------------------------------------------------------------------ +void ctkDICOMEcho::setPort(int port) +{ + Q_D(ctkDICOMEcho); + d->Port = port; +} + +//------------------------------------------------------------------------------ +int ctkDICOMEcho::port() const +{ + Q_D(const ctkDICOMEcho); + return d->Port; +} + +//----------------------------------------------------------------------------- +void ctkDICOMEcho::setConnectionTimeout(const int timeout) +{ + Q_D(ctkDICOMEcho); + d->SCU.setACSETimeout(timeout); + d->SCU.setConnectionTimeout(timeout); +} + +//----------------------------------------------------------------------------- +int ctkDICOMEcho::connectionTimeout() const +{ + Q_D(const ctkDICOMEcho); + return d->SCU.getConnectionTimeout(); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMEcho::echo() +{ + Q_D(ctkDICOMEcho); + + d->SCU.setPeerAETitle(OFString(this->calledAETitle().toStdString().c_str())); + d->SCU.setPeerHostName(OFString(this->host().toStdString().c_str())); + d->SCU.setPeerPort(this->port()); + + logger.debug("Setting Transfer Syntaxes"); + + OFList transferSyntaxes; + transferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax); + + d->SCU.addPresentationContext(UID_VerificationSOPClass, transferSyntaxes); + if (!d->SCU.initNetwork().good()) + { + logger.error("Error initializing the network"); + return false; + } + logger.debug("Negotiating Association"); + + OFCondition result = d->SCU.negotiateAssociation(); + if (result.bad()) + { + logger.error("Error negotiating the association: " + QString(result.text())); + return false; + } + + logger.debug("Seding Echo"); + // Issue ECHO request and let scu find presentation context itself (0) + OFCondition status = d->SCU.sendECHORequest(0); + if (!status.good()) + { + logger.error("Echo failed"); + d->SCU.releaseAssociation(); + return false; + } + + d->SCU.releaseAssociation(); + + return true; +} diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h new file mode 100644 index 0000000000..2e8d034cb4 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -0,0 +1,90 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkDICOMEcho_h +#define __ctkDICOMEcho_h + +// Qt includes +#include +#include +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkErrorLogLevel.h" + +class ctkDICOMEchoPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMEcho : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName); + Q_PROPERTY(QString callingAETitle READ callingAETitle WRITE setCallingAETitle); + Q_PROPERTY(QString calledAETitle READ calledAETitle WRITE setCalledAETitle); + Q_PROPERTY(QString host READ host WRITE setHost); + Q_PROPERTY(int port READ port WRITE setPort); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + +public: + explicit ctkDICOMEcho(QObject* parent = 0); + virtual ~ctkDICOMEcho(); + + /// Set methods for connectivity. + /// Empty by default + void setConnectionName(const QString& connectionName); + QString connectionName() const; + /// Empty by default + void setCallingAETitle(const QString& callingAETitle); + QString callingAETitle()const; + /// Empty by default + void setCalledAETitle(const QString& calledAETitle); + QString calledAETitle() const; + /// Empty by default + void setHost(const QString& host); + QString host() const; + /// Specify a port for the packet headers. + /// \a port ranges from 0 to 65535. + /// 80 by default. + void setPort(int port); + int port() const; + /// connection timeout, default 3 sec. + void setConnectionTimeout(const int timeout); + int connectionTimeout() const; + + /// Log level for dcmtk. Default: Error. + Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); + Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; + + /// Echo connection. + bool echo(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMEcho); + Q_DISABLE_COPY(ctkDICOMEcho); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMIndexer.cpp b/Libs/DICOM/Core/ctkDICOMIndexer.cpp index 7304150b32..36419794d7 100644 --- a/Libs/DICOM/Core/ctkDICOMIndexer.cpp +++ b/Libs/DICOM/Core/ctkDICOMIndexer.cpp @@ -138,8 +138,8 @@ void ctkDICOMIndexerPrivateWorker::start() imagesCountAfter = database.imagesCount(); double elapsedTimeInSeconds = timeProbe.elapsed() / 1000.0; - qDebug() << QString("DICOM indexer has updated display fields for %1 files [%2s]") - .arg(imagesCountAfter-imagesCountBefore).arg(QString::number(elapsedTimeInSeconds, 'f', 2)); + logger.debug(QString("DICOM indexer has updated display fields for %1 files [%2s]") + .arg(imagesCountAfter-imagesCountBefore).arg(QString::number(elapsedTimeInSeconds, 'f', 2))); // restart if new requests has been queued during displayed fields update } while (!this->RequestQueue->isEmpty()); @@ -250,8 +250,8 @@ void ctkDICOMIndexerPrivateWorker::processIndexingRequest(DICOMIndexingQueue::In } float elapsedTimeInSeconds = timeProbe.elapsed() / 1000.0; - qDebug() << QString("DICOM indexer has successfully processed %1 files [%2s]") - .arg(currentFileIndex).arg(QString::number(elapsedTimeInSeconds, 'f', 2)); + logger.debug(QString("DICOM indexer has successfully processed %1 files [%2s]") + .arg(currentFileIndex).arg(QString::number(elapsedTimeInSeconds, 'f', 2))); } @@ -279,8 +279,8 @@ void ctkDICOMIndexerPrivateWorker::writeIndexingResultsToDatabase(ctkDICOMDataba this->NumberOfInstancesInserted = 0; float elapsedTimeInSeconds = timeProbe.elapsed() / 1000.0; - qDebug() << QString("DICOM indexer has successfully inserted %1 files [%2s]") - .arg(indexingResults.count()).arg(QString::number(elapsedTimeInSeconds, 'f', 2)); + logger.debug(QString("DICOM indexer has successfully inserted %1 files [%2s]") + .arg(indexingResults.count()).arg(QString::number(elapsedTimeInSeconds, 'f', 2))); } @@ -634,10 +634,9 @@ bool ctkDICOMIndexer::addDicomdir(const QString& directoryName, bool copyFile/*= } } float elapsedTimeInSeconds = timeProbe.elapsed() / 1000.0; - qDebug() - << QString("DICOM indexer has successfully processed DICOMDIR in %1 [%2s]") - .arg(directoryName) - .arg(QString::number(elapsedTimeInSeconds,'f', 2)); + logger.debug(QString("DICOM indexer has successfully processed DICOMDIR in %1 [%2s]") + .arg(directoryName) + .arg(QString::number(elapsedTimeInSeconds,'f', 2))); this->addListOfFiles(listOfInstances, copyFile); } return success; diff --git a/Libs/DICOM/Core/ctkDICOMIndexer_p.h b/Libs/DICOM/Core/ctkDICOMIndexer_p.h index bec7104153..3357a046e6 100644 --- a/Libs/DICOM/Core/ctkDICOMIndexer_p.h +++ b/Libs/DICOM/Core/ctkDICOMIndexer_p.h @@ -259,7 +259,6 @@ class ctkDICOMIndexerPrivate : public QObject Q_SIGNALS: void startWorker(); -//public Q_SLOTS: public: DICOMIndexingQueue RequestQueue; diff --git a/Libs/DICOM/Core/ctkDICOMInserter.cpp b/Libs/DICOM/Core/ctkDICOMInserter.cpp new file mode 100644 index 0000000000..f4267a98fc --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserter.cpp @@ -0,0 +1,167 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include + +// ctkDICOMCore includes +#include "ctkLogger.h" +#include "ctkDICOMDatabase.h" +#include "ctkDICOMInserter.h" +#include "ctkDICOMJobResponseSet.h" + +static ctkLogger logger ( "org.commontk.dicom.DICOMQuery" ); + +//------------------------------------------------------------------------------ +class ctkDICOMInserterPrivate +{ +public: + ctkDICOMInserterPrivate(); + ~ctkDICOMInserterPrivate(); + + bool Canceled; + QString DatabaseFilename; + QStringList TagsToPrecache; + QStringList TagsToExcludeFromStorage; +}; + +//------------------------------------------------------------------------------ +// ctkDICOMInserterPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMInserterPrivate::ctkDICOMInserterPrivate() +{ + this->Canceled = false; +} + +//------------------------------------------------------------------------------ +ctkDICOMInserterPrivate::~ctkDICOMInserterPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMInserter methods + +//------------------------------------------------------------------------------ +ctkDICOMInserter::ctkDICOMInserter(QObject* parentObject) + : QObject(parentObject) + , d_ptr(new ctkDICOMInserterPrivate) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMInserter::~ctkDICOMInserter() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserter::setDatabaseFilename(const QString &databaseFilename) +{ + Q_D(ctkDICOMInserter); + d->DatabaseFilename = databaseFilename; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMInserter::databaseFilename() const +{ + Q_D(const ctkDICOMInserter); + return d->DatabaseFilename; +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserter::setTagsToPrecache(const QStringList &tagsToPrecache) +{ + Q_D(ctkDICOMInserter); + d->TagsToPrecache = tagsToPrecache; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMInserter::tagsToPrecache() const +{ + Q_D(const ctkDICOMInserter); + return d->TagsToPrecache; +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserter::setTagsToExcludeFromStorage(const QStringList &tagsToExcludeFromStorage) +{ + Q_D(ctkDICOMInserter); + d->TagsToExcludeFromStorage = tagsToExcludeFromStorage; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMInserter::tagsToExcludeFromStorage() const +{ + Q_D(const ctkDICOMInserter); + return d->TagsToExcludeFromStorage; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMInserter::wasCanceled() +{ + Q_D(const ctkDICOMInserter); + return d->Canceled; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMInserter::addJobResponseSets(QList> jobResponseSets) +{ + Q_D(const ctkDICOMInserter); + if (d->Canceled) + { + return false; + } + + emit updatingDatabase(true); + + ctkDICOMDatabase database; + QString dbConnectionName = + "db_" + QString::number(reinterpret_cast(QThread::currentThreadId()), 16); + + database.openDatabase(d->DatabaseFilename, dbConnectionName); + database.setTagsToPrecache(d->TagsToPrecache); + database.setTagsToExcludeFromStorage(d->TagsToExcludeFromStorage); + + // To Do: We should ensure that only one write operation occurs at a time. + // In the ctkDICOMScheduler, we ensure this by utilizing a job queue, preventing multiple inserter jobs from running concurrently. + // Similarly, it would be necessary to implement a check in the insert method of ctkDICOMDatabase + // to determine if any other process is currently writing (for example, a UI element writing the patient's name into the database). + // Therefore, we propose the inclusion of a static variable in ctkDICOMDatabase that indicates ongoing write operations + // for each DatabaseFilename, except in cases where it is an in-memory database. + database.insert(jobResponseSets); + database.updateDisplayedFields(); + + database.closeDatabase(); + + emit updatingDatabase(false); + emit done(); + + return true; +} + +//---------------------------------------------------------------------------- +void ctkDICOMInserter::cancel() +{ + Q_D(ctkDICOMInserter); + d->Canceled = true; +} diff --git a/Libs/DICOM/Core/ctkDICOMInserter.h b/Libs/DICOM/Core/ctkDICOMInserter.h new file mode 100644 index 0000000000..6a0c74a9b5 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserter.h @@ -0,0 +1,84 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMInserter_h +#define __ctkDICOMInserter_h + +// Qt includes +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" + +class ctkDICOMInserterPrivate; +class ctkDICOMJobResponseSet; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMInserter : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString databaseFilename READ databaseFilename WRITE setDatabaseFilename); + Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache); + Q_PROPERTY(QStringList tagsToExcludeFromStorage READ tagsToExcludeFromStorage WRITE setTagsToExcludeFromStorage); + +public: + explicit ctkDICOMInserter(QObject* parent = 0); + virtual ~ctkDICOMInserter(); + + /// Database Filename + void setDatabaseFilename(const QString& databaseFilename); + QString databaseFilename() const; + + /// Database TagsToPrecache + void setTagsToPrecache(const QStringList& tagsToPrecache); + QStringList tagsToPrecache() const; + + /// Database TagsToPrecache + void setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage); + QStringList tagsToExcludeFromStorage() const; + + /// operation is canceled? + Q_INVOKABLE bool wasCanceled(); + + /// add JobResponseSets from queries and retrieves + Q_INVOKABLE bool addJobResponseSets(QList> jobResponseSets); + +Q_SIGNALS: + void updatingDatabase(bool); + void done(); + +public Q_SLOTS: + void cancel(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMInserter); + Q_DISABLE_COPY(ctkDICOMInserter); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp new file mode 100644 index 0000000000..ccf76d705a --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -0,0 +1,162 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMInserterJob.h" +#include "ctkDICOMInserterWorker.h" +#include "ctkLogger.h" + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMInserterJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMInserterJob methods + +//------------------------------------------------------------------------------ +ctkDICOMInserterJob::ctkDICOMInserterJob() +{ + this->DatabaseFilename = ""; + this->MaximumConcurrentJobsPerType = 1; +} + +//------------------------------------------------------------------------------ +ctkDICOMInserterJob::~ctkDICOMInserterJob() +{ +} + +//------------------------------------------------------------------------------ +QString ctkDICOMInserterJob::loggerReport(const QString &status) const +{ + switch (this->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + return QString("ctkDICOMInserterJob: insert job at patients level %1.\n" + "JobUID: %2\n" + "PatientID: %3") + .arg(status) + .arg(this->jobUID()) + .arg(this->patientID()); + case ctkDICOMJob::DICOMLevels::Studies: + return QString("ctkDICOMInserterJob: insert job at studies level %1.\n" + "JobUID: %2\n" + "PatientID: %3\n" + "StudyInstanceUID: %4") + .arg(status) + .arg(this->jobUID()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()); + case ctkDICOMJob::DICOMLevels::Series: + return QString("ctkDICOMInserterJob: insert job at series level %1.\n" + "JobUID: %2\n" + "PatientID: %3\n" + "StudyInstanceUID: %4\n" + "SeriesInstanceUID: %5") + .arg(status) + .arg(this->jobUID()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()) + .arg(this->seriesInstanceUID()); + case ctkDICOMJob::DICOMLevels::Instances: + return QString("ctkDICOMInserterJob: insert job at instances level %1.\n" + "JobUID: %2\n" + "PatientID: %3\n" + "StudyInstanceUID: %4\n" + "SeriesInstanceUID: %5 \n" + "SOPInstanceUID: %6.") + .arg(status) + .arg(this->jobUID()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()) + .arg(this->seriesInstanceUID()) + .arg(this->sopInstanceUID()); + default: + return QString(""); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserterJob::setDatabaseFilename(const QString &databaseFilename) +{ + this->DatabaseFilename = databaseFilename; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMInserterJob::databaseFilename() const +{ + return this->DatabaseFilename; +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserterJob::setTagsToPrecache(const QStringList &tagsToPrecache) +{ + this->TagsToPrecache = tagsToPrecache; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMInserterJob::tagsToPrecache() const +{ + return this->TagsToPrecache; +} + +//------------------------------------------------------------------------------ +void ctkDICOMInserterJob::setTagsToExcludeFromStorage(const QStringList &tagsToExcludeFromStorage) +{ + this->TagsToExcludeFromStorage = tagsToExcludeFromStorage; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMInserterJob::tagsToExcludeFromStorage() const +{ + return this->TagsToExcludeFromStorage; +} + +//------------------------------------------------------------------------------ +ctkDICOMJob* ctkDICOMInserterJob::generateCopy() const +{ + ctkDICOMInserterJob* newInserterJob = new ctkDICOMInserterJob; + newInserterJob->setDICOMLevel(this->dicomLevel()); + newInserterJob->setPatientID(this->patientID()); + newInserterJob->setStudyInstanceUID(this->studyInstanceUID()); + newInserterJob->setSeriesInstanceUID(this->seriesInstanceUID()); + newInserterJob->setSOPInstanceUID(this->sopInstanceUID()); + newInserterJob->setMaximumNumberOfRetry(this->maximumNumberOfRetry()); + newInserterJob->setRetryDelay(this->retryDelay()); + newInserterJob->setRetryCounter(this->retryCounter()); + newInserterJob->setIsPersistent(this->isPersistent()); + newInserterJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); + newInserterJob->setPriority(this->priority()); + newInserterJob->setDatabaseFilename(this->databaseFilename()); + newInserterJob->setTagsToPrecache(this->tagsToPrecache()); + newInserterJob->setTagsToExcludeFromStorage(this->tagsToExcludeFromStorage()); + + return newInserterJob; +} + +//------------------------------------------------------------------------------ +ctkDICOMWorker *ctkDICOMInserterJob::createWorker() +{ + ctkDICOMInserterWorker* worker = + new ctkDICOMInserterWorker; + worker->setJob(*this); + return worker; +} + diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h new file mode 100644 index 0000000000..ec243e9cfa --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -0,0 +1,81 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMInserterJob_h +#define __ctkDICOMInserterJob_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" + +#include + +class ctkDICOMServer; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob +{ + Q_OBJECT + Q_PROPERTY(QString databaseFilename READ databaseFilename WRITE setDatabaseFilename); + Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache); + Q_PROPERTY(QStringList tagsToExcludeFromStorage READ tagsToExcludeFromStorage WRITE setTagsToExcludeFromStorage); + +public: + typedef ctkDICOMJob Superclass; + explicit ctkDICOMInserterJob(); + virtual ~ctkDICOMInserterJob(); + + /// Logger report string formatting for specific task + Q_INVOKABLE QString loggerReport(const QString& status) const; + + /// Database Filename + void setDatabaseFilename(const QString& databaseFilename); + QString databaseFilename() const; + + /// Database TagsToPrecache + void setTagsToPrecache(const QStringList& tagsToPrecache); + QStringList tagsToPrecache() const; + + /// Database TagsToPrecache + void setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage); + QStringList tagsToExcludeFromStorage() const; + + /// Create a copy of the object + Q_INVOKABLE ctkDICOMJob* generateCopy() const; + + /// Generate worker for job + Q_INVOKABLE ctkDICOMWorker* createWorker(); + +protected: + QString DatabaseFilename; + QStringList TagsToPrecache; + QStringList TagsToExcludeFromStorage; + +private: + Q_DISABLE_COPY(ctkDICOMInserterJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp new file mode 100644 index 0000000000..c86345e657 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -0,0 +1,168 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" +#include "ctkDICOMInserterWorker_p.h" +#include "ctkDICOMInserterJob.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkDICOMScheduler.h" +#include "ctkLogger.h" + +static ctkLogger logger ("org.commontk.dicom.ctkDICOMInserterWorker"); + +//------------------------------------------------------------------------------ +// ctkDICOMInserterWorkerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMInserterWorkerPrivate::ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWorker* object) + : q_ptr(object) +{ + this->Inserter = QSharedPointer(new ctkDICOMInserter); +} + +//------------------------------------------------------------------------------ +ctkDICOMInserterWorkerPrivate::~ctkDICOMInserterWorkerPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMInserterWorker methods + +//------------------------------------------------------------------------------ +ctkDICOMInserterWorker::ctkDICOMInserterWorker() + : d_ptr(new ctkDICOMInserterWorkerPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMInserterWorker::ctkDICOMInserterWorker(ctkDICOMInserterWorkerPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMInserterWorker::~ctkDICOMInserterWorker() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMInserterWorker::cancel() +{ + Q_D(const ctkDICOMInserterWorker); + d->Inserter->cancel(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMInserterWorker::run() +{ + Q_D(const ctkDICOMInserterWorker); + QSharedPointer inserterJob = + qobject_cast>(this->Job); + if (!inserterJob) + { + return; + } + + if (inserterJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit inserterJob->canceled(); + this->onJobCanceled(); + inserterJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + inserterJob->setStatus(ctkAbstractJob::JobStatus::Running); + emit inserterJob->started(); + + logger.debug("ctkDICOMInserterWorker : running job on thread id " + + QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + + QList> jobResponseSets = inserterJob->jobResponseSetsShared(); + d->Inserter->addJobResponseSets(jobResponseSets); + + if (inserterJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit inserterJob->canceled(); + this->onJobCanceled(); + inserterJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + foreach (QSharedPointer jobResponseSet, jobResponseSets) + { + emit inserterJob->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + } + + inserterJob->setStatus(ctkAbstractJob::JobStatus::Finished); + emit inserterJob->finished(); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +void ctkDICOMInserterWorker::setJob(ctkAbstractJob &job) +{ + this->setJob(QSharedPointer(&job, skipDelete)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMInserterWorker::setJob(QSharedPointer job) +{ + Q_D(const ctkDICOMInserterWorker); + QSharedPointer inserterJob = + qobject_cast>(job); + if (!inserterJob) + { + return; + } + + Superclass::setJob(job); + + d->Inserter->setDatabaseFilename(inserterJob->databaseFilename()); + d->Inserter->setTagsToPrecache(inserterJob->tagsToPrecache()); + d->Inserter->setTagsToExcludeFromStorage(inserterJob->tagsToExcludeFromStorage()); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMInserterWorker::inserterShared() const +{ + Q_D(const ctkDICOMInserterWorker); + return d->Inserter; +} + +//------------------------------------------------------------------------------ +ctkDICOMInserter* ctkDICOMInserterWorker::inserter() const +{ + Q_D(const ctkDICOMInserterWorker); + return d->Inserter.data(); +} diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h new file mode 100644 index 0000000000..19469b9644 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -0,0 +1,80 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMInserterWorker_h +#define __ctkDICOMInserterWorker_h + +// Qt includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMWorker.h" + +class ctkDICOMDatabase; +class ctkDICOMInserter; +class ctkDICOMInserterWorkerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkDICOMWorker +{ + Q_OBJECT + +public: + typedef ctkDICOMWorker Superclass; + explicit ctkDICOMInserterWorker(); + virtual ~ctkDICOMInserterWorker(); + + /// Execute worker + void run(); + + /// Cancel worker + void cancel(); + + /// Job + Q_INVOKABLE void setJob(ctkAbstractJob& job); + void setJob(QSharedPointer job); + + /// Inserter + QSharedPointer inserterShared() const; + Q_INVOKABLE ctkDICOMInserter* inserter() const; + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMInserterWorker(ctkDICOMInserterWorkerPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMInserterWorker); + Q_DISABLE_COPY(ctkDICOMInserterWorker); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h new file mode 100644 index 0000000000..9b1c138298 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h @@ -0,0 +1,47 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMInserterWorkerPrivate_h +#define __ctkDICOMInserterWorkerPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMInserter.h" +#include "ctkDICOMInserterWorker.h" + +//------------------------------------------------------------------------------ +class ctkDICOMInserterWorkerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMInserterWorker) + +protected: + ctkDICOMInserterWorker* const q_ptr; + +public: + ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWorker* object); + ~ctkDICOMInserterWorkerPrivate(); + + QSharedPointer Inserter; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMItem.cpp b/Libs/DICOM/Core/ctkDICOMItem.cpp index 441275ea1b..17b5549e3a 100644 --- a/Libs/DICOM/Core/ctkDICOMItem.cpp +++ b/Libs/DICOM/Core/ctkDICOMItem.cpp @@ -247,6 +247,12 @@ DcmItem& ctkDICOMItem::GetDcmItem() const return *d->m_DcmItem; } +DcmItem* ctkDICOMItem::GetDcmItemPointer() const +{ + const Q_D(ctkDICOMItem); + return d->m_DcmItem; +} + OFCondition ctkDICOMItem::findAndGetElement(const DcmTag& tag, DcmElement*& element, const OFBool searchIntoSub) const { EnsureDcmDataSetIsInitialized(); diff --git a/Libs/DICOM/Core/ctkDICOMItem.h b/Libs/DICOM/Core/ctkDICOMItem.h index f6bb26db9d..d139c4c123 100644 --- a/Libs/DICOM/Core/ctkDICOMItem.h +++ b/Libs/DICOM/Core/ctkDICOMItem.h @@ -247,6 +247,16 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMItem /// static QString TagVR( const DcmTag& tag ); + /// + /// \brief return dcm item + /// + DcmItem& GetDcmItem() const; + + /// + /// \brief return dcm item pointer + /// + DcmItem* GetDcmItemPointer() const; + protected: /// @@ -267,8 +277,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMItem QScopedPointer d_ptr; - DcmItem& GetDcmItem() const; - private: Q_DECLARE_PRIVATE(ctkDICOMItem); }; diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp new file mode 100644 index 0000000000..6527406d85 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -0,0 +1,162 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMJob.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkLogger.h" + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMJob methods + +//------------------------------------------------------------------------------ +ctkDICOMJob::ctkDICOMJob() +{ + this->DICOMLevel = DICOMLevels::Patients; + this->PatientID = ""; + this->StudyInstanceUID = ""; + this->SeriesInstanceUID = ""; + this->SOPInstanceUID = ""; +} + +//------------------------------------------------------------------------------ +ctkDICOMJob::~ctkDICOMJob() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMJob::setDICOMLevel(const DICOMLevels& dicomLevel) +{ + this->DICOMLevel = dicomLevel; +} + +//------------------------------------------------------------------------------ +ctkDICOMJob::DICOMLevels ctkDICOMJob::dicomLevel() const +{ + return this->DICOMLevel; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJob::setPatientID(const QString &patientID) +{ + this->PatientID = patientID; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMJob::patientID() const +{ + return this->PatientID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJob::setStudyInstanceUID(const QString& studyInstanceUID) +{ + this->StudyInstanceUID = studyInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJob::studyInstanceUID() const +{ + return this->StudyInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJob::setSeriesInstanceUID(const QString& seriesInstanceUID) +{ + this->SeriesInstanceUID = seriesInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJob::seriesInstanceUID() const +{ + return this->SeriesInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJob::setSOPInstanceUID(const QString& sopInstanceUID) +{ + this->SOPInstanceUID = sopInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJob::sopInstanceUID() const +{ + return this->SOPInstanceUID; +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//------------------------------------------------------------------------------ +QList ctkDICOMJob::jobResponseSets() const +{ + QList jobResponseSets; + foreach(QSharedPointer jobResponseSet, this->JobResponseSets) + { + jobResponseSets.append(jobResponseSet.data()); + } + + return jobResponseSets; +} + +//------------------------------------------------------------------------------ +QList> ctkDICOMJob::jobResponseSetsShared() const +{ + return this->JobResponseSets; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJob::setJobResponseSets(QList jobResponseSets) +{ + this->JobResponseSets.clear(); + foreach(ctkDICOMJobResponseSet* jobResponseSet, jobResponseSets) + { + this->JobResponseSets.append(QSharedPointer(jobResponseSet, skipDelete)); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMJob::setJobResponseSets(QList> jobResponseSets) +{ + this->JobResponseSets = jobResponseSets; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJob::copyJobResponseSets(QList> jobResponseSets) +{ + this->JobResponseSets.clear(); + foreach(QSharedPointer jobResponseSet, jobResponseSets) + { + QSharedPointer jobResponseSetCopy = + QSharedPointer(new ctkDICOMJobResponseSet); + jobResponseSetCopy->deepCopy(jobResponseSet.data()); + this->JobResponseSets.append(jobResponseSetCopy); + } +} diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h new file mode 100644 index 0000000000..f51504adda --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -0,0 +1,115 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMJob_h +#define __ctkDICOMJob_h + +// Qt includes +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMWorker.h" + +#include + +class ctkDICOMServer; +class ctkDICOMJobResponseSet; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob +{ + Q_OBJECT + Q_ENUMS(DICOMLevel) + Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); + Q_PROPERTY(QString seriesInstanceUID READ seriesInstanceUID WRITE setSeriesInstanceUID); + Q_PROPERTY(QString sopInstanceUID READ sopInstanceUID WRITE setSOPInstanceUID); + Q_PROPERTY(DICOMLevels dicomLevel READ dicomLevel WRITE setDICOMLevel); + +public: + typedef ctkAbstractJob Superclass; + explicit ctkDICOMJob(); + virtual ~ctkDICOMJob(); + + enum DICOMLevels{ + Patients, + Studies, + Series, + Instances + }; + + /// DICOM Level + void setDICOMLevel(const DICOMLevels& dicomLevel); + DICOMLevels dicomLevel() const; + + /// Patient ID + void setPatientID(const QString& patientID); + QString patientID() const; + + /// Study instance UID + void setStudyInstanceUID(const QString& studyInstanceUID); + QString studyInstanceUID() const; + + /// Series instance UID + void setSeriesInstanceUID(const QString& seriesInstanceUID); + QString seriesInstanceUID() const; + + /// SOP instance UID + void setSOPInstanceUID(const QString& sopInstanceUID); + QString sopInstanceUID() const; + + /// Access the list of responses. + Q_INVOKABLE QList jobResponseSets() const; + QList> jobResponseSetsShared() const; + Q_INVOKABLE void setJobResponseSets(QList jobResponseSets); + void setJobResponseSets(QList> jobResponseSets); + void copyJobResponseSets(QList> jobResponseSets); + + /// Logger report string formatting for specific task + Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; + + /// Create a copy of the object + Q_INVOKABLE virtual ctkDICOMJob* generateCopy() const = 0; + + /// Generate worker for job + Q_INVOKABLE virtual ctkDICOMWorker* createWorker() = 0; + +Q_SIGNALS: + void progressJobDetail(QVariant); + void finishedJobDetail(QVariant); + +protected: + QString PatientID; + QString StudyInstanceUID; + QString SeriesInstanceUID; + QString SOPInstanceUID; + ctkDICOMJob::DICOMLevels DICOMLevel; + QList> JobResponseSets; + +private: + Q_DISABLE_COPY(ctkDICOMJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp new file mode 100644 index 0000000000..f31124311c --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -0,0 +1,412 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMItem.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkLogger.h" + +// DCMTK includes +#include + +static ctkLogger logger("org.commontk.dicom.DICOMTaskResults"); + +//------------------------------------------------------------------------------ +class ctkDICOMJobResponseSetPrivate: public QObject +{ + Q_DECLARE_PUBLIC(ctkDICOMJobResponseSet); + +protected: + ctkDICOMJobResponseSet* const q_ptr; + +public: + ctkDICOMJobResponseSetPrivate(ctkDICOMJobResponseSet& obj); + ~ctkDICOMJobResponseSetPrivate(); + + QString FilePath; + bool CopyFile; + bool OverwriteExistingDataset; + + ctkDICOMJobResponseSet::JobType TypeOfJob; + QString JobUID; + QString PatientID; + QString StudyInstanceUID; + QString SeriesInstanceUID; + QString SOPInstanceUID; + QString ConnectionName; + QMap> Datasets; +}; + +//------------------------------------------------------------------------------ +// ctkDICOMJobResponseSetPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSetPrivate::ctkDICOMJobResponseSetPrivate(ctkDICOMJobResponseSet& obj) + : q_ptr(&obj) +{ + this->TypeOfJob = ctkDICOMJobResponseSet::JobType::None; + this->JobUID = ""; + this->PatientID = ""; + this->StudyInstanceUID = ""; + this->SeriesInstanceUID = ""; + this->SOPInstanceUID = ""; + this->ConnectionName = ""; + this->CopyFile = false; + this->OverwriteExistingDataset = false; + this->FilePath = ""; +} + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSetPrivate::~ctkDICOMJobResponseSetPrivate() +{ + this->Datasets.clear(); +} + +//------------------------------------------------------------------------------ +// ctkDICOMJobResponseSet methods + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSet::ctkDICOMJobResponseSet(QObject* parent) + : QObject(parent), + d_ptr(new ctkDICOMJobResponseSetPrivate(*this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSet::~ctkDICOMJobResponseSet() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setFilePath(const QString &filePath) +{ + Q_D(ctkDICOMJobResponseSet); + d->FilePath = filePath; + + if (d->FilePath.isEmpty()) + { + return; + } + + QSharedPointer dataset = + QSharedPointer(new ctkDICOMItem); + dataset->InitializeFromFile(filePath); + + DcmItem dcmItem = dataset->GetDcmItem(); + OFString SOPInstanceUID; + dcmItem.findAndGetOFString(DCM_SOPInstanceUID, SOPInstanceUID); + + d->Datasets.insert(QString(SOPInstanceUID.c_str()), dataset); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMJobResponseSet::filePath() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->FilePath; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setCopyFile(const bool ©File) +{ + Q_D(ctkDICOMJobResponseSet); + d->CopyFile = copyFile; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMJobResponseSet::copyFile() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->CopyFile; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setOverwriteExistingDataset(const bool &overwriteExistingDataset) +{ + Q_D(ctkDICOMJobResponseSet); + d->OverwriteExistingDataset = overwriteExistingDataset; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMJobResponseSet::overwriteExistingDataset() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->OverwriteExistingDataset; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setTypeOfJob(const ctkDICOMJobResponseSet::JobType &typeOfJob) +{ + Q_D(ctkDICOMJobResponseSet); + d->TypeOfJob = typeOfJob; +} + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSet::JobType ctkDICOMJobResponseSet::typeOfJob() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->TypeOfJob; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setJobUID(const QString &jobUID) +{ + Q_D(ctkDICOMJobResponseSet); + d->JobUID = jobUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::jobUID() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->JobUID; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setPatientID(const QString& patientID) +{ + Q_D(ctkDICOMJobResponseSet); + d->PatientID = patientID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::patientID() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->PatientID; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setStudyInstanceUID(const QString& studyInstanceUID) +{ + Q_D(ctkDICOMJobResponseSet); + d->StudyInstanceUID = studyInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::studyInstanceUID() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->StudyInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setSeriesInstanceUID(const QString& seriesInstanceUID) +{ + Q_D(ctkDICOMJobResponseSet); + d->SeriesInstanceUID = seriesInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::seriesInstanceUID() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->SeriesInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMJobResponseSet::setSOPInstanceUID(const QString& sopInstanceUID) +{ + Q_D(ctkDICOMJobResponseSet); + d->SOPInstanceUID = sopInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::sopInstanceUID() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->SOPInstanceUID; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setConnectionName(const QString &connectionName) +{ + Q_D(ctkDICOMJobResponseSet); + d->ConnectionName = connectionName; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMJobResponseSet::connectionName() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->ConnectionName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setDataset(DcmItem *dcmItem, bool takeOwnership) +{ + Q_D(ctkDICOMJobResponseSet); + if (!dcmItem) + { + return; + } + + QSharedPointer dataset = + QSharedPointer(new ctkDICOMItem); + dataset->InitializeFromItem(dcmItem, takeOwnership); + + OFString SOPInstanceUID; + dcmItem->findAndGetOFString(DCM_SOPInstanceUID, SOPInstanceUID); + d->Datasets.insert(QString(SOPInstanceUID.c_str()), dataset); +} + +//------------------------------------------------------------------------------ +ctkDICOMItem *ctkDICOMJobResponseSet::dataset() const +{ + Q_D(const ctkDICOMJobResponseSet); + if (d->Datasets.count() == 0) + { + return nullptr; + } + return d->Datasets.first().data(); +} + +//------------------------------------------------------------------------------ +QSharedPointer ctkDICOMJobResponseSet::datasetShared() const +{ + Q_D(const ctkDICOMJobResponseSet); + if (d->Datasets.count() == 0) + { + return nullptr; + } + return d->Datasets.first(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::setDatasets(QMap dcmItems, bool takeOwnership) +{ + Q_D(ctkDICOMJobResponseSet); + for(QString key : dcmItems.keys()) + { + DcmItem *dcmItem = dcmItems.value(key); + if (!dcmItem) + { + continue; + } + + QSharedPointer dataset = + QSharedPointer(new ctkDICOMItem); + dataset->InitializeFromItem(dcmItem, takeOwnership); + + d->Datasets.insert(key, dataset); + } +} + +//------------------------------------------------------------------------------ +QMap ctkDICOMJobResponseSet::datasets() const +{ + Q_D(const ctkDICOMJobResponseSet); + QMap datasets; + + for(QString key : d->Datasets.keys()) + { + QSharedPointer dcmItem = d->Datasets.value(key); + if (!dcmItem) + { + continue; + } + + datasets.insert(key, dcmItem.data()); + } + + return datasets; +} + +//------------------------------------------------------------------------------ +QMap> ctkDICOMJobResponseSet::datasetsShared() const +{ + Q_D(const ctkDICOMJobResponseSet); + return d->Datasets; +} + +//------------------------------------------------------------------------------ +void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) +{ + Q_D(ctkDICOMJobResponseSet); + + if (!node) + { + return; + } + + this->setFilePath(node->filePath()); + this->setCopyFile(node->copyFile()); + this->setOverwriteExistingDataset(node->overwriteExistingDataset()); + this->setTypeOfJob(node->typeOfJob()); + this->setJobUID(node->jobUID()); + this->setPatientID(node->patientID()); + this->setStudyInstanceUID(node->studyInstanceUID()); + this->setSeriesInstanceUID(node->seriesInstanceUID()); + this->setSOPInstanceUID(node->sopInstanceUID()); + this->setConnectionName(node->connectionName()); + + d->Datasets.clear(); + + QMap nodeDatasets = node->datasets(); + for(QString key : nodeDatasets.keys()) + { + ctkDICOMItem* nodeDataset = nodeDatasets.value(key); + if (!nodeDataset) + { + continue; + } + + DcmItem* nodedcmItem = nodeDataset->GetDcmItemPointer(); + DcmDataset* nodedcmDataset = dynamic_cast(nodedcmItem); + DcmItem* dcmItem = nullptr; + if (nodedcmDataset) + { + dcmItem = new DcmDataset(); + dcmItem->copyFrom(*nodedcmDataset); + } + else + { + dcmItem = new DcmItem(); + dcmItem->copyFrom(*nodedcmItem); + } + + QSharedPointer dataset = + QSharedPointer(new ctkDICOMItem); + dataset->InitializeFromItem(dcmItem, true); + d->Datasets.insert(key, dataset); + } +} + +//------------------------------------------------------------------------------ +QVariant ctkDICOMJobResponseSet::jobResponseSetToDetail() +{ + ctkJobDetail td; + td.TypeOfJob = this->typeOfJob(); + td.JobUID = this->jobUID(); + td.PatientID = this->patientID(); + td.StudyInstanceUID = this->studyInstanceUID(); + td.SeriesInstanceUID = this->seriesInstanceUID(); + td.SOPInstanceUID = this->sopInstanceUID(); + td.ConnectionName = this->connectionName(); + td.NumberOfDataSets = this->datasets().count(); + + QVariant data; + data.setValue(td); + + return data; +} diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h new file mode 100644 index 0000000000..99c32ebfcc --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -0,0 +1,154 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMJobResponseSet_h +#define __ctkDICOMJobResponseSet_h + + +// Qt includes +#include +#include +#include + +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMItem.h" + +class ctkDICOMJobResponseSetPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject +{ + Q_OBJECT + Q_ENUMS(JobType) + Q_PROPERTY(QString filePath READ filePath WRITE setFilePath); + Q_PROPERTY(bool copyFile READ copyFile WRITE setCopyFile); + Q_PROPERTY(bool overwriteExistingDataset READ overwriteExistingDataset WRITE setOverwriteExistingDataset); + Q_PROPERTY(JobType typeOfJob READ typeOfJob WRITE setTypeOfJob); + Q_PROPERTY(QString jobUID READ jobUID WRITE setJobUID); + Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); + Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); + Q_PROPERTY(QString seriesInstanceUID READ seriesInstanceUID WRITE setSeriesInstanceUID); + Q_PROPERTY(QString sopInstanceUID READ sopInstanceUID WRITE setSOPInstanceUID); + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName); + +public: + explicit ctkDICOMJobResponseSet(QObject* parent = 0); + virtual ~ctkDICOMJobResponseSet(); + + /// File Path + void setFilePath(const QString& filePath); + QString filePath() const; + + /// Copy File + /// false as default + void setCopyFile(const bool& copyFile); + bool copyFile() const; + + /// Overwrite existing dataset + /// false as default + void setOverwriteExistingDataset(const bool& overwriteExistingDataset); + bool overwriteExistingDataset() const; + + /// Job type + enum JobType { + None = 0, + QueryPatients, + QueryStudies, + QuerySeries, + QueryInstances, + RetrieveStudy, + RetrieveSeries, + RetrieveSOPInstance, + StoreSOPInstance + }; + void setTypeOfJob(const JobType& typeOfJob); + JobType typeOfJob() const; + + /// Task UID + void setJobUID(const QString& jobUID); + QString jobUID() const; + + /// Patient ID + void setPatientID(const QString& patientID); + QString patientID() const; + + /// Study instance UID + void setStudyInstanceUID(const QString& studyInstanceUID); + QString studyInstanceUID() const; + + /// Series instance UID + void setSeriesInstanceUID(const QString& seriesInstanceUID); + QString seriesInstanceUID() const; + + /// SOP instance UID + void setSOPInstanceUID(const QString& sopInstanceUID); + QString sopInstanceUID() const; + + /// Connection name + void setConnectionName(const QString& connectionName); + QString connectionName() const; + + /// DCM datasets + Q_INVOKABLE void setDataset(DcmItem* dcmItem, bool takeOwnership = true); + Q_INVOKABLE ctkDICOMItem* dataset() const; + QSharedPointer datasetShared() const; + Q_INVOKABLE void setDatasets(QMap dcmItems, bool takeOwnership = true); + Q_INVOKABLE QMap datasets() const; + QMap> datasetsShared() const; + + /// Copy object + Q_INVOKABLE void deepCopy(ctkDICOMJobResponseSet* node); + + /// Utility method to transform/pass informations between threads by qt signals + Q_INVOKABLE QVariant jobResponseSetToDetail(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMJobResponseSet); + Q_DISABLE_COPY(ctkDICOMJobResponseSet); +}; + +struct CTK_DICOM_CORE_EXPORT ctkJobDetail { + explicit ctkJobDetail(): + TypeOfJob(ctkDICOMJobResponseSet::JobType::None), + JobUID(""), + PatientID(""), + StudyInstanceUID(""), + SeriesInstanceUID(""), + SOPInstanceUID(""), + ConnectionName(""), + NumberOfDataSets(0){} + virtual ~ctkJobDetail(){}; + + ctkDICOMJobResponseSet::JobType TypeOfJob; + QString JobUID; + QString PatientID; + QString StudyInstanceUID; + QString SeriesInstanceUID; + QString SOPInstanceUID; + QString ConnectionName; + int NumberOfDataSets; +}; Q_DECLARE_METATYPE(ctkJobDetail); +#endif diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index 565a962f9f..64c1c8eb48 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -33,25 +33,21 @@ // ctkDICOMCore includes #include "ctkDICOMQuery.h" -#include "ctkDICOMUtil.h" #include "ctkLogger.h" +#include "ctkDICOMJobResponseSet.h" // DCMTK includes -#include "dcmtk/dcmnet/dimse.h" -#include "dcmtk/dcmnet/diutil.h" -#include "dcmtk/dcmnet/scu.h" - -#include +#include #include #include #include +#include #include #include #include #include /* for class OFStandard */ #include /* for class DicomDirInterface */ - static ctkLogger logger ( "org.commontk.dicom.DICOMQuery" ); //------------------------------------------------------------------------------ @@ -70,13 +66,14 @@ class ctkDICOMQuerySCUPrivate : public DcmSCU QRResponse *response, OFBool &waitForNextResponse) { - if (this->query) - { - logger.debug ( "FIND RESPONSE" ); - emit this->query->debug(/*no tr*/"Got a find response!"); - return this->DcmSCU::handleFINDResponse(presID, response, waitForNextResponse); - } - return DIMSE_NULLKEY; + if (!this->query || this->query->wasCanceled()) + { + return EC_IllegalCall; + } + + logger.debug ( "FIND RESPONSE" ); + emit this->query->debug(/*no tr*/"Got a find response!"); + return this->DcmSCU::handleFINDResponse(presID, response, waitForNextResponse); }; }; @@ -88,22 +85,25 @@ class ctkDICOMQueryPrivate ~ctkDICOMQueryPrivate(); /// Add a StudyInstanceUID to be queried - void addStudyInstanceUIDAndDataset(const QString& StudyInstanceUID, DcmDataset* dataset ); + void addStudyInstanceUIDAndDataset(const QString& studyInstanceUID, DcmDataset* dataset ); /// Add StudyInstanceUID and SeriesInstanceUID that may be further retrieved - void addStudyAndSeriesInstanceUID( const QString& StudyInstanceUID, const QString& SeriesInstanceUID ); - - QString CallingAETitle; - QString CalledAETitle; - QString Host; - int Port; - bool PreferCGET; - QMap Filters; + void addStudyAndSeriesInstanceUID( const QString& studyInstanceUID, const QString& seriesInstanceUID ); + + QString ConnectionName; + QString CallingAETitle; + QString CalledAETitle; + QString Host; + int Port; + QMap Filters; ctkDICOMQuerySCUPrivate SCU; - DcmDataset* Query; - QList< QPair > StudyAndSeriesInstanceUIDPairList; - QStringList StudyInstanceUIDList; - QList StudyDatasetList; - bool Canceled; + Uint16 PresentationContext; + QSharedPointer QueryDcmDataset; + QList> StudyAndSeriesInstanceUIDPairList; + QMap StudyDatasets; + bool Canceled; + int MaximumPatientsQuery; + QString JobUID; + QList> JobResponseSets; }; //------------------------------------------------------------------------------ @@ -112,29 +112,32 @@ class ctkDICOMQueryPrivate //------------------------------------------------------------------------------ ctkDICOMQueryPrivate::ctkDICOMQueryPrivate() { - this->Query = new DcmDataset(); + this->QueryDcmDataset = QSharedPointer(new DcmDataset); + this->PresentationContext = 0; this->Port = 0; this->Canceled = false; - this->PreferCGET = false; + this->MaximumPatientsQuery = 25; + + this->SCU.setACSETimeout(10); + this->SCU.setConnectionTimeout(10); } //------------------------------------------------------------------------------ ctkDICOMQueryPrivate::~ctkDICOMQueryPrivate() { - delete this->Query; + this->JobResponseSets.clear(); } //------------------------------------------------------------------------------ -void ctkDICOMQueryPrivate::addStudyAndSeriesInstanceUID( const QString& study, const QString& series ) +void ctkDICOMQueryPrivate::addStudyAndSeriesInstanceUID( const QString& studyInstanceUID, const QString& seriesInstanceUID ) { - this->StudyAndSeriesInstanceUIDPairList.push_back (qMakePair( study, series ) ); + this->StudyAndSeriesInstanceUIDPairList.push_back (qMakePair( studyInstanceUID, seriesInstanceUID ) ); } //------------------------------------------------------------------------------ -void ctkDICOMQueryPrivate::addStudyInstanceUIDAndDataset( const QString& study, DcmDataset* dataset ) +void ctkDICOMQueryPrivate::addStudyInstanceUIDAndDataset( const QString& studyInstanceUID, DcmDataset* dataset ) { - this->StudyInstanceUIDList.append ( study ); - this->StudyDatasetList.append ( dataset ); + this->StudyDatasets[studyInstanceUID] = dataset; } //------------------------------------------------------------------------------ @@ -146,7 +149,11 @@ ctkDICOMQuery::ctkDICOMQuery(QObject* parentObject) , d_ptr(new ctkDICOMQueryPrivate) { Q_D(ctkDICOMQuery); + + d->SCU.setVerbosePCMode(false); d->SCU.query = this; // give the dcmtk level access to this for emitting signals + + this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -154,9 +161,63 @@ ctkDICOMQuery::~ctkDICOMQuery() { } +//----------------------------------------------------------------------------- +void ctkDICOMQuery::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) +{ + OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; + if (level == ctkErrorLogLevel::LogLevel::Fatal) + { + dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Critical || + level == ctkErrorLogLevel::LogLevel::Error) + { + dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Warning) + { + dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Info) + { + dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Debug) + { + dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Trace || + level == ctkErrorLogLevel::LogLevel::Status) + { + dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; + } + + OFLog::configure(dcmtkLogLevel); +} + +//----------------------------------------------------------------------------- +ctkErrorLogLevel::LogLevel ctkDICOMQuery::DCMTKLogLevel() const +{ + return logger.logLevel(); +} + /// Set methods for connectivity //------------------------------------------------------------------------------ -void ctkDICOMQuery::setCallingAETitle( const QString& callingAETitle ) +void ctkDICOMQuery::setConnectionName(const QString& connectionName) +{ + Q_D(ctkDICOMQuery); + d->ConnectionName = connectionName; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMQuery::connectionName() const +{ + Q_D(const ctkDICOMQuery); + return d->ConnectionName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMQuery::setCallingAETitle(const QString& callingAETitle) { Q_D(ctkDICOMQuery); d->CallingAETitle = callingAETitle; @@ -170,7 +231,7 @@ QString ctkDICOMQuery::callingAETitle() const } //------------------------------------------------------------------------------ -void ctkDICOMQuery::setCalledAETitle( const QString& calledAETitle ) +void ctkDICOMQuery::setCalledAETitle(const QString& calledAETitle) { Q_D(ctkDICOMQuery); d->CalledAETitle = calledAETitle; @@ -184,7 +245,7 @@ QString ctkDICOMQuery::calledAETitle()const } //------------------------------------------------------------------------------ -void ctkDICOMQuery::setHost( const QString& host ) +void ctkDICOMQuery::setHost(const QString& host) { Q_D(ctkDICOMQuery); d->Host = host; @@ -198,7 +259,7 @@ QString ctkDICOMQuery::host() const } //------------------------------------------------------------------------------ -void ctkDICOMQuery::setPort ( int port ) +void ctkDICOMQuery::setPort(int port) { Q_D(ctkDICOMQuery); d->Port = port; @@ -211,18 +272,40 @@ int ctkDICOMQuery::port()const return d->Port; } +//----------------------------------------------------------------------------- +void ctkDICOMQuery::setConnectionTimeout(const int timeout) +{ + Q_D(ctkDICOMQuery); + d->SCU.setACSETimeout(timeout); + d->SCU.setConnectionTimeout(timeout); +} + +//----------------------------------------------------------------------------- +int ctkDICOMQuery::connectionTimeout() const +{ + Q_D(const ctkDICOMQuery); + return d->SCU.getConnectionTimeout(); +} + //------------------------------------------------------------------------------ -void ctkDICOMQuery::setPreferCGET ( bool preferCGET ) +void ctkDICOMQuery::setMaximumPatientsQuery(const int maximumPatientsQuery) { Q_D(ctkDICOMQuery); - d->PreferCGET = preferCGET; + d->MaximumPatientsQuery = maximumPatientsQuery; +} + +//------------------------------------------------------------------------------ +int ctkDICOMQuery::maximumPatientsQuery() +{ + Q_D(const ctkDICOMQuery); + return d->MaximumPatientsQuery; } //------------------------------------------------------------------------------ -bool ctkDICOMQuery::preferCGET()const +bool ctkDICOMQuery::wasCanceled() { Q_D(const ctkDICOMQuery); - return d->PreferCGET; + return d->Canceled; } //------------------------------------------------------------------------------ @@ -247,276 +330,862 @@ QList< QPair > ctkDICOMQuery::studyAndSeriesInstanceUIDQueried( } //------------------------------------------------------------------------------ -bool ctkDICOMQuery::query(ctkDICOMDatabase& database ) +QList ctkDICOMQuery::jobResponseSets() const +{ + Q_D(const ctkDICOMQuery); + QList jobResponseSets; + foreach(QSharedPointer jobResponseSet, d->JobResponseSets) + { + jobResponseSets.append(jobResponseSet.data()); + } + + return jobResponseSets; +} + +//------------------------------------------------------------------------------ +QList> ctkDICOMQuery::jobResponseSetsShared() const +{ + Q_D(const ctkDICOMQuery); + return d->JobResponseSets; +} + +//------------------------------------------------------------------------------ +void ctkDICOMQuery::setJobUID(const QString &jobUID) +{ + Q_D(ctkDICOMQuery); + d->JobUID = jobUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMQuery::jobUID() const { - // turn on logging if needed for debug: - //ctk::setDICOMLogLevel(ctkErrorLogLevel::Debug); + Q_D(const ctkDICOMQuery); + return d->JobUID; +} - // ctkDICOMDatabase::setDatabase ( database ); +//------------------------------------------------------------------------------ +bool ctkDICOMQuery::query(ctkDICOMDatabase& database) +{ Q_D(ctkDICOMQuery); // In the following, we emit progress(int) after progress(QString), this // is in case the connected object doesn't refresh its ui when the progress // message is updated but only if the progress value is (e.g. QProgressDialog) - if ( database.database().isOpen() ) + if (database.database().isOpen()) { - logger.debug ( "DB open in Query" ); + logger.debug("DB open in Query"); emit progress(tr("DB open in Query")); } else { - logger.debug ( "DB not open in Query" ); + logger.debug("DB not open in Query"); emit progress(tr("DB not open in Query")); } emit progress(0); - if (d->Canceled) {return false;} - - d->StudyAndSeriesInstanceUIDPairList.clear(); - d->StudyInstanceUIDList.clear(); - d->SCU.setAETitle ( OFString(this->callingAETitle().toStdString().c_str()) ); - d->SCU.setPeerAETitle ( OFString(this->calledAETitle().toStdString().c_str()) ); - d->SCU.setPeerHostName ( OFString(this->host().toStdString().c_str()) ); - d->SCU.setPeerPort ( this->port() ); - - logger.error ( "Setting Transfer Syntaxes" ); - emit progress(tr("Setting Transfer Syntaxes")); - emit progress(10); - if (d->Canceled) {return false;} - - OFList transferSyntaxes; - transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax ); - transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax ); - transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax ); - - d->SCU.addPresentationContext ( UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes ); - // d->SCU.addPresentationContext ( UID_VerificationSOPClass, transferSyntaxes ); - if ( !d->SCU.initNetwork().good() ) + if (d->Canceled) { - logger.error( "Error initializing the network" ); - emit progress(tr("Error initializing the network")); - emit progress(100); return false; } - logger.debug ( "Negotiating Association" ); - emit progress(tr("Negotiating Association")); - emit progress(20); - if (d->Canceled) {return false;} - OFCondition result = d->SCU.negotiateAssociation(); - if (result.bad()) + d->StudyAndSeriesInstanceUIDPairList.clear(); + d->StudyDatasets.clear(); + + // initSCU + if (!this->initializeSCU()) { - logger.error( "Error negotiating the association: " + QString(result.text()) ); - emit progress(tr("Error negotiating the association")); - emit progress(100); return false; } // Clear the query - d->Query->clear(); + d->QueryDcmDataset->clear(); // Insert all keys that we like to receive values for - d->Query->insertEmptyElement ( DCM_PatientID ); - d->Query->insertEmptyElement ( DCM_PatientName ); - d->Query->insertEmptyElement ( DCM_PatientBirthDate ); - d->Query->insertEmptyElement ( DCM_StudyID ); - d->Query->insertEmptyElement ( DCM_StudyInstanceUID ); - d->Query->insertEmptyElement ( DCM_StudyDescription ); - d->Query->insertEmptyElement ( DCM_StudyDate ); - d->Query->insertEmptyElement ( DCM_StudyTime ); - d->Query->insertEmptyElement ( DCM_ModalitiesInStudy ); - d->Query->insertEmptyElement ( DCM_AccessionNumber ); - d->Query->insertEmptyElement ( DCM_NumberOfStudyRelatedInstances ); // Number of images in the series - d->Query->insertEmptyElement ( DCM_NumberOfStudyRelatedSeries ); // Number of series in the study + d->QueryDcmDataset->insertEmptyElement(DCM_PatientID); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientName); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientBirthDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDescription); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyTime); + d->QueryDcmDataset->insertEmptyElement(DCM_ModalitiesInStudy); + d->QueryDcmDataset->insertEmptyElement(DCM_AccessionNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfStudyRelatedInstances); // Number of images in the series + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfStudyRelatedSeries); // Number of series in the study // Make clear we define our search values in ISO Latin 1 (default would be ASCII) - d->Query->putAndInsertOFStringArray(DCM_SpecificCharacterSet, "ISO_IR 100"); + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, "ISO_IR 100"); - d->Query->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" ); - - /* Now, for all keys that the user provided for filtering on STUDY level, - * overwrite empty keys with value. For now, only Patient's Name, Patient ID, - * Study Description, Modalities in Study, and Study Date are used. - */ - QString seriesDescription; - foreach( QString key, d->Filters.keys() ) - { - if ( key == QString("Name") && !d->Filters[key].toString().isEmpty()) - { - // make the filter a wildcard in dicom style - d->Query->putAndInsertString( DCM_PatientName, - (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); - } - else if ( key == QString("Study") && !d->Filters[key].toString().isEmpty()) - { - // make the filter a wildcard in dicom style - d->Query->putAndInsertString( DCM_StudyDescription, - (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); - } - else if ( key == QString("ID") && !d->Filters[key].toString().isEmpty()) - { - // make the filter a wildcard in dicom style - d->Query->putAndInsertString( DCM_PatientID, - (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); - } - else if (key == QString("AccessionNumber") && !d->Filters[key].toString().isEmpty()) - { - // make the filter a wildcard in dicom style - d->Query->putAndInsertString(DCM_AccessionNumber, - (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); - } - else if ( key == QString("Modalities") && !d->Filters[key].toString().isEmpty()) - { - // make the filter be an "OR" of modalities using backslash (dicom-style) - QString modalitySearch(""); - foreach (const QString& modality, d->Filters[key].toStringList()) - { - modalitySearch += modality + QString("\\"); - } - modalitySearch.chop(1); // remove final backslash - logger.debug("modalityInStudySearch " + modalitySearch); - d->Query->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toLatin1().data() ); - } - // Remember Series Description for later series query if we go through the keys now - else if ( key == QString("Series") && !d->Filters[key].toString().isEmpty()) - { - // make the filter a wildcard in dicom style - seriesDescription = "*" + d->Filters[key].toString() + "*"; - } - else - { - logger.debug("Ignoring unknown search key: " + key); - } - } + d->QueryDcmDataset->putAndInsertString (DCM_QueryRetrieveLevel, "STUDY"); - if ( d->Filters.keys().contains("StartDate") && d->Filters.keys().contains("EndDate") ) + QString seriesDescription = this->applyFilters(); + if (d->Canceled) { - QString dateRange = d->Filters["StartDate"].toString() + - QString("-") + - d->Filters["EndDate"].toString(); - d->Query->putAndInsertString ( DCM_StudyDate, dateRange.toLatin1().data() ); - logger.debug("Query on study date " + dateRange); + return false; } - emit progress(30); - if (d->Canceled) {return false;} OFList responses; Uint16 presentationContext = 0; // Check for any accepted presentation context for FIND in study root (don't care about transfer syntax) - presentationContext = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, ""); - if ( presentationContext == 0 ) + presentationContext = d->SCU.findPresentationContextID(UID_FINDStudyRootQueryRetrieveInformationModel, ""); + if (presentationContext == 0) { - logger.error ( "Failed to find acceptable presentation context" ); + logger.error("Failed to find acceptable presentation context"); emit progress(tr("Failed to find acceptable presentation context")); } else { - logger.info ( "Found useful presentation context" ); + logger.info("Found useful presentation context"); emit progress(tr("Found useful presentation context")); } emit progress(40); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; + } - OFCondition status = d->SCU.sendFINDRequest ( presentationContext, d->Query, &responses ); - if ( !status.good() ) + OFCondition status = d->SCU.sendFINDRequest(presentationContext, d->QueryDcmDataset.data(), &responses); + if (!status.good()) { - logger.error ( "Find failed" ); + logger.error("Find failed"); emit progress(tr("Find failed")); - d->SCU.closeAssociation ( DCMSCU_RELEASE_ASSOCIATION ); + d->SCU.releaseAssociation(); emit progress(100); return false; } - logger.debug ( "Find succeeded"); + logger.debug("Find succeeded"); emit progress(tr("Find succeeded")); emit progress(50); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; + } - for ( OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++ ) + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) { DcmDataset *dataset = (*it)->m_dataset; - if ( dataset != NULL ) // the last response is always empty + if (dataset != NULL) // the last response is always empty { - database.insert ( dataset, false /* do not store to disk*/, false /* no thumbnail*/); + database.insert(dataset, false /* do not store to disk*/, false /* no thumbnail*/); OFString StudyInstanceUID; - dataset->findAndGetOFString ( DCM_StudyInstanceUID, StudyInstanceUID ); - d->addStudyInstanceUIDAndDataset ( StudyInstanceUID.c_str(), dataset ); - emit progress(tr("Processing Study: ") + QString(StudyInstanceUID.c_str())); + dataset->findAndGetOFString(DCM_StudyInstanceUID, StudyInstanceUID); + d->addStudyInstanceUIDAndDataset(StudyInstanceUID.c_str(), dataset); + emit progress(tr("Processing: ") + QString(StudyInstanceUID.c_str())); emit progress(50); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; + } } } /* Only ask for series attributes now. This requires kicking out the rest of former query. */ - d->Query->clear(); - d->Query->insertEmptyElement ( DCM_SeriesNumber ); - d->Query->insertEmptyElement ( DCM_SeriesDescription ); - d->Query->insertEmptyElement ( DCM_SeriesInstanceUID ); - d->Query->insertEmptyElement ( DCM_SeriesDate ); - d->Query->insertEmptyElement ( DCM_SeriesTime ); - d->Query->insertEmptyElement ( DCM_Modality ); - d->Query->insertEmptyElement ( DCM_NumberOfSeriesRelatedInstances ); // Number of images in the series + d->QueryDcmDataset->clear(); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesDescription); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesDate); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesTime); + d->QueryDcmDataset->insertEmptyElement(DCM_Modality); + d->QueryDcmDataset->insertEmptyElement(DCM_Rows); + d->QueryDcmDataset->insertEmptyElement(DCM_Columns); + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfSeriesRelatedInstances); // Number of images in the series /* Add user-defined filters */ - d->Query->putAndInsertOFStringArray(DCM_SeriesDescription, seriesDescription.toLatin1().data()); + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SeriesDescription, seriesDescription.toLatin1().data()); // Now search each within each Study that was identified - d->Query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" ); - float progressRatio = 25. / d->StudyInstanceUIDList.count(); + d->QueryDcmDataset->putAndInsertString(DCM_QueryRetrieveLevel, "SERIES"); + float progressRatio = 25. / d->StudyDatasets.count(); int i = 0; - QListIterator datasetIterator(d->StudyDatasetList); - Q_FOREACH(const QString & StudyInstanceUID, d->StudyInstanceUIDList ) + foreach(QString studyInstanceUID, d->StudyDatasets.keys()) { - DcmDataset *studyDataset = datasetIterator.next(); + DcmDataset *studyDataset = d->StudyDatasets.value(studyInstanceUID); DcmElement *patientName, *patientID; studyDataset->findAndGetElement(DCM_PatientName, patientName); studyDataset->findAndGetElement(DCM_PatientID, patientID); - logger.debug ( "Starting Series C-FIND for Study: " + StudyInstanceUID ); - emit progress(tr("Starting Series C-FIND for Study: ") + StudyInstanceUID); + logger.debug("Starting Series C-FIND for Study: " + studyInstanceUID); + emit progress(tr("Starting Series C-FIND for Study: ") + studyInstanceUID); emit progress(50 + (progressRatio * i++)); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; + } - d->Query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() ); + d->QueryDcmDataset->putAndInsertString (DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str()); OFList responses; - status = d->SCU.sendFINDRequest ( presentationContext, d->Query, &responses ); - if ( status.good() ) + status = d->SCU.sendFINDRequest(presentationContext, d->QueryDcmDataset.data(), &responses); + if (status.good()) { - for ( OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++ ) + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) { DcmDataset *dataset = (*it)->m_dataset; - if ( dataset != NULL ) + if (dataset != NULL) { - OFString SeriesInstanceUID; - dataset->findAndGetOFString ( DCM_SeriesInstanceUID, SeriesInstanceUID ); - d->addStudyAndSeriesInstanceUID ( StudyInstanceUID.toStdString().c_str(), SeriesInstanceUID.c_str() ); + OFString seriesInstanceUID; + dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID); + d->addStudyAndSeriesInstanceUID(studyInstanceUID.toStdString().c_str(), seriesInstanceUID.c_str()); // add the patient elements not provided for the series level query - dataset->insert( patientName, true ); - dataset->insert( patientID, true ); + dataset->insert(patientName, true); + dataset->insert(patientID, true); // insert series dataset - database.insert ( dataset, false /* do not store */, false /* no thumbnail */ ); + database.insert(dataset, false /* do not store */, false /* no thumbnail */); } } - logger.debug ( "Find succeeded on Series level for Study: " + StudyInstanceUID ); - emit progress(tr("Find succeeded on Series level for Study: ") + StudyInstanceUID); + logger.debug ("Find succeeded on Series level for Study: " + studyInstanceUID); + emit progress(tr("Find succeeded on Series level for Study: ") + studyInstanceUID); emit progress(50 + (progressRatio * i++)); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; + } } else { - logger.error ( "Find on Series level failed for Study: " + StudyInstanceUID ); - emit progress(tr("Find on Series level failed for Study: ") + StudyInstanceUID); + logger.error ("Find on Series level failed for Study: " + studyInstanceUID); + emit progress(tr("Find on Series level failed for Study: ") + studyInstanceUID); } emit progress(50 + (progressRatio * i++)); - if (d->Canceled) {return false;} + if (d->Canceled) + { + return false; } - d->SCU.closeAssociation ( DCMSCU_RELEASE_ASSOCIATION ); + } + d->SCU.releaseAssociation(); emit progress(100); return true; } //---------------------------------------------------------------------------- -void ctkDICOMQuery::cancel() +bool ctkDICOMQuery::queryPatients() { Q_D(ctkDICOMQuery); - d->Canceled = true; + + // In the following, we emit progress(int) after progress(QString), this + // is in case the connected object doesn't refresh its ui when the progress + // message is updated but only if the progress value is (e.g. QProgressDialog) + emit progress(0); + if (d->Canceled) + { + return false; + } + + d->JobResponseSets.clear(); + + // initSCU + if (!this->initializeSCU()) + { + return false; + } + + // Clear the query + d->QueryDcmDataset->clear(); + + // Insert all keys that we like to receive values for + d->QueryDcmDataset->insertEmptyElement(DCM_PatientID); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientName); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientBirthDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDescription); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyTime); + d->QueryDcmDataset->insertEmptyElement(DCM_ModalitiesInStudy); + d->QueryDcmDataset->insertEmptyElement(DCM_AccessionNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfStudyRelatedSeries); // Number of series in the study + + // Make clear we define our search values in ISO Latin 1 (default would be ASCII) + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, "ISO_IR 100"); + + d->QueryDcmDataset->putAndInsertString(DCM_QueryRetrieveLevel, "PATIENT"); + + QString seriesDescription = this->applyFilters(); + if (d->Canceled) + { + return false; + } + + Uint16 presentationContext = 0; + // Check for any accepted presentation context for FIND in study root (don't care about transfer syntax) + presentationContext = d->SCU.findPresentationContextID(UID_FINDStudyRootQueryRetrieveInformationModel, ""); + if (presentationContext == 0) + { + logger.error("Failed to find acceptable presentation context"); + emit progress(tr("Failed to find acceptable presentation context")); + } + else + { + logger.debug("Found useful presentation context"); + emit progress(tr("Found useful presentation context")); + } + emit progress(40); + if (d->Canceled) + { + return false; + } + + logger.debug("Starting Patients C-FIND"); + emit progress(tr("Starting Patients C-FIND")); + emit progress(50); + if (d->Canceled) + { + return false; + } + + QSharedPointer JobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryPatients); + JobResponseSet->setConnectionName(d->ConnectionName); + JobResponseSet->setJobUID(d->JobUID); + + QMap datasetsMap; + OFList responses; + OFCondition status = d->SCU.sendFINDRequest(presentationContext, d->QueryDcmDataset.data(), &responses); + if (status.good()) + { + int contResponses = 0; + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) + { + contResponses++; + if (contResponses > d->MaximumPatientsQuery) + { + logger.warn(QString("ctkDICOMQuery: the number of responses of the query task at patients level " + "surpassed the maximum value of permitted results (i.e. %1).").arg(d->MaximumPatientsQuery)); + + break; + } + DcmDataset *dataset = (*it)->m_dataset; + if ( dataset != NULL ) + { + OFString patientID; + dataset->findAndGetOFString(DCM_PatientID, patientID); + datasetsMap.insert(patientID.c_str(), dataset); + } + } + + JobResponseSet->setDatasets(datasetsMap); + d->JobResponseSets.append(JobResponseSet); + + logger.debug("Find succeeded on Patient level"); + emit progress(tr("Find succeeded on Patient level")); + } + else + { + logger.error("Find on Patient level failed"); + emit progress(tr("Find on Patient level failed")); + } + + emit progress(100); + if (d->Canceled) + { + return false; + } + + d->SCU.releaseAssociation(); + return true; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMQuery::queryStudies(const QString& patientID) +{ + Q_D(ctkDICOMQuery); + // In the following, we emit progress(int) after progress(QString), this + // is in case the connected object doesn't refresh its ui when the progress + // message is updated but only if the progress value is (e.g. QProgressDialog) + emit progress(0); + if (d->Canceled) + { + return false; + } + + d->JobResponseSets.clear(); + + // initSCU + if (!this->initializeSCU()) + { + return false; + } + + // Clear the query + d->QueryDcmDataset->clear(); + + // Insert all keys that we like to receive values for + d->QueryDcmDataset->insertEmptyElement(DCM_PatientID); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientName); + d->QueryDcmDataset->insertEmptyElement(DCM_PatientBirthDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDescription); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyDate); + d->QueryDcmDataset->insertEmptyElement(DCM_StudyTime); + d->QueryDcmDataset->insertEmptyElement(DCM_ModalitiesInStudy); + d->QueryDcmDataset->insertEmptyElement(DCM_AccessionNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfStudyRelatedSeries); // Number of series in the study + + // Make clear we define our search values in ISO Latin 1 (default would be ASCII) + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SpecificCharacterSet, "ISO_IR 100"); + + d->QueryDcmDataset->putAndInsertString(DCM_QueryRetrieveLevel, "STUDY"); + + QString seriesDescription = this->applyFilters(); + if (d->Canceled) + { + return false; + } + + Uint16 presentationContext = 0; + // Check for any accepted presentation context for FIND in study root (don't care about transfer syntax) + presentationContext = d->SCU.findPresentationContextID(UID_FINDStudyRootQueryRetrieveInformationModel, ""); + if (presentationContext == 0) + { + logger.error("Failed to find acceptable presentation context"); + emit progress(tr("Failed to find acceptable presentation context")); + } + else + { + logger.debug("Found useful presentation context"); + emit progress(tr("Found useful presentation context")); + } + emit progress(40); + if (d->Canceled) + { + return false; + } + + logger.debug("Starting Studies C-FIND for Patient: " + patientID); + emit progress(tr("Starting Studies C-FIND for Patient: ") + patientID); + emit progress(50); + if (d->Canceled) + { + return false; + } + + d->QueryDcmDataset->putAndInsertString(DCM_PatientID, patientID.toStdString().c_str()); + + QSharedPointer JobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryStudies); + JobResponseSet->setPatientID(patientID.toStdString().c_str()); + JobResponseSet->setConnectionName(d->ConnectionName); + JobResponseSet->setJobUID(d->JobUID); + + QMap datasetsMap; + + OFList responses; + OFCondition status = d->SCU.sendFINDRequest(presentationContext, d->QueryDcmDataset.data(), &responses); + if (status.good()) + { + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) + { + DcmDataset *dataset = (*it)->m_dataset; + if ( dataset != NULL ) + { + OFString studyInstanceUID; + dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID); + datasetsMap.insert(studyInstanceUID.c_str(), dataset); + } + } + + JobResponseSet->setDatasets(datasetsMap); + d->JobResponseSets.append(JobResponseSet); + + logger.debug("Find succeeded on Study level for Patient: " + patientID); + emit progress(tr("Find succeeded on Study level for Patient: ") + patientID); + } + else + { + logger.error("Find on Study level failed for Patient: " + patientID); + emit progress(tr("Find on Study level failed for Patient: ") + patientID); + } + + emit progress(100); + if (d->Canceled) + { + return false; + } + + d->SCU.releaseAssociation(); + return true; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMQuery::querySeries(const QString& patientID, + const QString& studyInstanceUID) +{ + Q_D(ctkDICOMQuery); + + // In the following, we emit progress(int) after progress(QString), this + // is in case the connected object doesn't refresh its ui when the progress + // message is updated but only if the progress value is (e.g. QProgressDialog) + emit progress(0); + if (d->Canceled) + { + return false; + } + + d->JobResponseSets.clear(); + + // initSCU + if (!this->initializeSCU()) + { + return false; + } + + // Insert all keys that we like to receive values for + d->QueryDcmDataset->clear(); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesDescription); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesDate); + d->QueryDcmDataset->insertEmptyElement(DCM_SeriesTime); + d->QueryDcmDataset->insertEmptyElement(DCM_Modality); + d->QueryDcmDataset->insertEmptyElement(DCM_NumberOfSeriesRelatedInstances); // Number of images in the series + + QString seriesDescription = this->applyFilters(); + if (d->Canceled) + { + return false; + } + + /* Add user-defined filters */ + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SeriesDescription, seriesDescription.toLatin1().data()); + + // Now search each within each Study that was identified + d->QueryDcmDataset->putAndInsertString(DCM_QueryRetrieveLevel, "SERIES"); + + Uint16 presentationContext = 0; + // Check for any accepted presentation context for FIND in study root (don't care about transfer syntax) + presentationContext = d->SCU.findPresentationContextID(UID_FINDStudyRootQueryRetrieveInformationModel, ""); + if (presentationContext == 0) + { + logger.error("Failed to find acceptable presentation context"); + emit progress(tr("Failed to find acceptable presentation context")); + } + else + { + logger.debug("Found useful presentation context"); + emit progress(tr("Found useful presentation context")); + } + emit progress(40); + if (d->Canceled) + { + return false; + } + + logger.debug("Starting Series C-FIND for Study: " + studyInstanceUID); + emit progress(tr("Starting Series C-FIND for Study: ") + studyInstanceUID); + emit progress(50); + if (d->Canceled) + { + return false; + } + + d->QueryDcmDataset->putAndInsertString(DCM_PatientID, patientID.toStdString().c_str()); + d->QueryDcmDataset->putAndInsertString(DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str()); + + QSharedPointer JobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QuerySeries); + JobResponseSet->setPatientID(patientID.toStdString().c_str()); + JobResponseSet->setStudyInstanceUID(studyInstanceUID.toStdString().c_str()); + JobResponseSet->setConnectionName(d->ConnectionName); + JobResponseSet->setJobUID(d->JobUID); + + QMap datasetsMap; + + OFList responses; + OFCondition status = d->SCU.sendFINDRequest(presentationContext, d->QueryDcmDataset.data(), &responses); + if (status.good()) + { + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) + { + DcmDataset *dataset = (*it)->m_dataset; + if ( dataset != NULL ) + { + OFString seriesInstanceUID; + dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID); + datasetsMap.insert(seriesInstanceUID.c_str(), dataset); + } + } + + JobResponseSet->setDatasets(datasetsMap); + d->JobResponseSets.append(JobResponseSet); + + logger.debug("Find succeeded on Series level for Study: " + studyInstanceUID); + emit progress(tr("Find succeeded on Series level for Study: ") + studyInstanceUID); + } + else + { + logger.error("Find on Series level failed for Study: " + studyInstanceUID); + emit progress(tr("Find on Series level failed for Study: ") + studyInstanceUID); + } + + emit progress(100); + if (d->Canceled) + { + return false; + } + + d->SCU.releaseAssociation(); + return true; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMQuery::queryInstances(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID) +{ + Q_D(ctkDICOMQuery); + + // In the following, we emit progress(int) after progress(QString), this + // is in case the connected object doesn't refresh its ui when the progress + // message is updated but only if the progress value is (e.g. QProgressDialog) + emit progress(0); + if (d->Canceled) + { + return false; + } + + d->JobResponseSets.clear(); + + // initSCU + if (!this->initializeSCU()) + { + return false; + } + + // Insert all keys that we like to receive values for + d->QueryDcmDataset->clear(); + d->QueryDcmDataset->insertEmptyElement(DCM_InstanceNumber); + d->QueryDcmDataset->insertEmptyElement(DCM_SOPInstanceUID); + d->QueryDcmDataset->insertEmptyElement(DCM_Rows); + d->QueryDcmDataset->insertEmptyElement(DCM_Columns); + + QString seriesDescription = this->applyFilters(); + if (d->Canceled) + { + return false; + } + + /* Add user-defined filters */ + d->QueryDcmDataset->putAndInsertOFStringArray(DCM_SeriesDescription, seriesDescription.toLatin1().data()); + + // Now search each within each Study that was identified + d->QueryDcmDataset->putAndInsertString(DCM_QueryRetrieveLevel, "IMAGE"); + + // Check for any accepted presentation context for FIND in study root (don't care about transfer syntax) + d->PresentationContext = d->SCU.findPresentationContextID(UID_FINDStudyRootQueryRetrieveInformationModel, ""); + if (d->PresentationContext == 0) + { + logger.error("Failed to find acceptable presentation context"); + emit progress(tr("Failed to find acceptable presentation context")); + } + else + { + logger.debug("Found useful presentation context"); + emit progress(tr("Found useful presentation context")); + } + emit progress(40); + if (d->Canceled) + { + return false; + } + + logger.debug("Starting Instances C-FIND for Series: " + seriesInstanceUID); + emit progress(tr("Starting Instances C-FIND for Series: ") + seriesInstanceUID); + emit progress(50); + if (d->Canceled) + { + return false; + } + + d->QueryDcmDataset->putAndInsertString(DCM_PatientID, patientID.toStdString().c_str()); + d->QueryDcmDataset->putAndInsertString(DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str()); + d->QueryDcmDataset->putAndInsertString(DCM_SeriesInstanceUID, seriesInstanceUID.toStdString().c_str()); + + QSharedPointer JobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryInstances); + JobResponseSet->setPatientID(patientID.toStdString().c_str()); + JobResponseSet->setStudyInstanceUID(studyInstanceUID.toStdString().c_str()); + JobResponseSet->setSeriesInstanceUID(seriesInstanceUID.toStdString().c_str()); + JobResponseSet->setConnectionName(d->ConnectionName); + JobResponseSet->setJobUID(d->JobUID); + + QMap datasetsMap; + + OFList responses; + OFCondition status = d->SCU.sendFINDRequest(d->PresentationContext, d->QueryDcmDataset.data(), &responses); + if (status.good()) + { + for (OFListIterator(QRResponse*) it = responses.begin(); it != responses.end(); it++) + { + DcmItem *dataset = (*it)->m_dataset; + if (dataset != NULL) + { + OFString SOPInstanceUID; + dataset->findAndGetOFString(DCM_SOPInstanceUID, SOPInstanceUID); + datasetsMap.insert(SOPInstanceUID.c_str(), dataset); + } + } + logger.debug("Find succeeded on Series level for Series: " + seriesInstanceUID); + emit progress(tr("Find succeeded on Series level for Series: ") + seriesInstanceUID); + } + else + { + logger.error("Find on Series level failed for Series: " + seriesInstanceUID); + emit progress(tr("Find on Series level failed for Series: ") + seriesInstanceUID); + } + + JobResponseSet->setDatasets(datasetsMap); + d->JobResponseSets.append(JobResponseSet); + + emit progress(100); + if (d->Canceled) + { + return false; + } + + d->SCU.releaseAssociation(); + return true; +} + +//---------------------------------------------------------------------------- +void ctkDICOMQuery::cancel() +{ + Q_D(ctkDICOMQuery); + d->Canceled = true; + + if (d->PresentationContext != 0) + { + d->SCU.sendCANCELRequest(d->PresentationContext); + d->PresentationContext = 0; + } +} + +//---------------------------------------------------------------------------- +bool ctkDICOMQuery::initializeSCU() +{ + Q_D(ctkDICOMQuery); + + d->SCU.setAETitle(OFString(this->callingAETitle().toStdString().c_str())); + d->SCU.setPeerAETitle(OFString(this->calledAETitle().toStdString().c_str())); + d->SCU.setPeerHostName(OFString(this->host().toStdString().c_str())); + d->SCU.setPeerPort(this->port()); + + logger.debug("Setting Transfer Syntaxes"); + emit progress(tr("Setting Transfer Syntaxes")); + emit progress(10); + if (d->Canceled) + { + return false; + } + + OFList transferSyntaxes; + transferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax); + + d->SCU.addPresentationContext(UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes); + if (!d->SCU.initNetwork().good()) + { + logger.error("Error initializing the network"); + emit progress(tr("Error initializing the network")); + emit progress(100); + return false; + } + logger.debug("Negotiating Association"); + emit progress(tr("Negotiating Association")); + emit progress(20); + if (d->Canceled) + { + return false; + } + + OFCondition result = d->SCU.negotiateAssociation(); + if (result.bad()) + { + logger.error("Error negotiating the association: " + QString(result.text())); + emit progress(tr("Error negotiating the association")); + emit progress(100); + return false; + } + + return true; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMQuery::applyFilters() +{ + Q_D(ctkDICOMQuery); + + /* Now, for all keys that the user provided for filtering on STUDY level, + * overwrite empty keys with value. For now, only Patient's Name, Patient ID, + * Study Description, Modalities in Study, and Study Date are used. + */ + QString seriesDescription; + foreach(QString key, d->Filters.keys()) + { + if ( key == QString("Name") && !d->Filters[key].toString().isEmpty()) + { + // make the filter a wildcard in dicom style + d->QueryDcmDataset->putAndInsertString( DCM_PatientName, + (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); + } + else if ( key == QString("Study") && !d->Filters[key].toString().isEmpty()) + { + // make the filter a wildcard in dicom style + d->QueryDcmDataset->putAndInsertString( DCM_StudyDescription, + (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); + } + else if ( key == QString("ID") && !d->Filters[key].toString().isEmpty()) + { + // make the filter a wildcard in dicom style + d->QueryDcmDataset->putAndInsertString( DCM_PatientID, + (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); + } + else if (key == QString("AccessionNumber") && !d->Filters[key].toString().isEmpty()) + { + // make the filter a wildcard in dicom style + d->QueryDcmDataset->putAndInsertString(DCM_AccessionNumber, + (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data()); + } + else if ( key == QString("Modalities") && !d->Filters[key].toString().isEmpty()) + { + // make the filter be an "OR" of modalities using backslash (dicom-style) + QString modalitySearch(""); + foreach (const QString& modality, d->Filters[key].toStringList()) + { + modalitySearch += modality + QString("\\"); + } + modalitySearch.chop(1); // remove final backslash + logger.debug("modalityInStudySearch " + modalitySearch); + d->QueryDcmDataset->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toLatin1().data() ); + } + // Remember Series Description for later series query if we go through the keys now + else if ( key == QString("Series") && !d->Filters[key].toString().isEmpty()) + { + // make the filter a wildcard in dicom style + seriesDescription = "*" + d->Filters[key].toString() + "*"; + } + else + { + logger.debug("Ignoring unknown search key: " + key); + } + } + + if ( d->Filters.keys().contains("StartDate") && d->Filters.keys().contains("EndDate") ) + { + QString dateRange = d->Filters["StartDate"].toString() + + QString("-") + + d->Filters["EndDate"].toString(); + d->QueryDcmDataset->putAndInsertString ( DCM_StudyDate, dateRange.toLatin1().data() ); + logger.debug("Query on study date " + dateRange); + } + + emit progress(30); + + return seriesDescription; } diff --git a/Libs/DICOM/Core/ctkDICOMQuery.h b/Libs/DICOM/Core/ctkDICOMQuery.h index 652a3c4708..559cbe0f98 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.h +++ b/Libs/DICOM/Core/ctkDICOMQuery.h @@ -25,58 +25,61 @@ #include #include #include -#include // CTK includes #include +// ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMDatabase.h" +#include "ctkErrorLogLevel.h" class ctkDICOMQueryPrivate; +class ctkDICOMJobResponseSet; /// \ingroup DICOM_Core class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject { Q_OBJECT + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName); Q_PROPERTY(QString callingAETitle READ callingAETitle WRITE setCallingAETitle); Q_PROPERTY(QString calledAETitle READ calledAETitle WRITE setCalledAETitle); Q_PROPERTY(QString host READ host WRITE setHost); Q_PROPERTY(int port READ port WRITE setPort); - Q_PROPERTY(bool preferCGET READ preferCGET WRITE setPreferCGET); - Q_PROPERTY(QList< QPair > studyAndSeriesInstanceUIDQueried READ studyAndSeriesInstanceUIDQueried); - Q_PROPERTY(QMap filters READ filters WRITE setFilters); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + Q_PROPERTY(int maximumPatientsQuery READ maximumPatientsQuery WRITE setMaximumPatientsQuery); + Q_PROPERTY(QList> studyAndSeriesInstanceUIDQueried READ studyAndSeriesInstanceUIDQueried); + Q_PROPERTY(QString jobUID READ jobUID WRITE setJobUID); public: explicit ctkDICOMQuery(QObject* parent = 0); virtual ~ctkDICOMQuery(); - /// Set methods for connectivity + /// Set methods for connectivity. /// Empty by default - void setCallingAETitle ( const QString& callingAETitle ); + void setConnectionName(const QString& connectionName); + QString connectionName() const; + /// Empty by default + void setCallingAETitle(const QString& callingAETitle); QString callingAETitle()const; /// Empty by default - void setCalledAETitle ( const QString& calledAETitle ); - QString calledAETitle()const; + void setCalledAETitle(const QString& calledAETitle); + QString calledAETitle() const; /// Empty by default - void setHost ( const QString& host ); - QString host()const; + void setHost(const QString& host); + QString host() const; /// Specify a port for the packet headers. /// \a port ranges from 0 to 65535. /// 0 by default. - void setPort ( int port ); - int port()const; - /// Prefer CGET over CMOVE for retrieval of query results - /// false by default - void setPreferCGET ( bool preferCGET ); - bool preferCGET()const; - - /// Query a remote DICOM Image Store SCP - /// You must at least set the host and port before calling query() - Q_INVOKABLE bool query(ctkDICOMDatabase& database); - - /// Access the list of study and series instance UIDs from the last query - QList< QPair > studyAndSeriesInstanceUIDQueried()const; + void setPort(int port); + int port() const; + /// connection timeout, default 10 sec. + void setConnectionTimeout(const int timeout); + int connectionTimeout() const; + /// maximum number of responses allowed in one query + /// when query is at Patient level. Default is 25. + void setMaximumPatientsQuery(const int maximumPatientsQuery); + int maximumPatientsQuery(); /// /// Filters are keyword/value pairs as generated by @@ -87,7 +90,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject /// StartDate and EndDate /// Key DICOM Tag Type Example /// ----------------------------------------------------------- - /// Name DCM_PatientName QString JOHNDOE + /// Name DCM_PatientName QString JOHNDOE /// Study DCM_StudyDescription QString /// Series DCM_SeriesDescription QString /// ID DCM_PatientID QString @@ -95,8 +98,54 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject /// StartDate DCM_StudyDate QString 20090101 /// EndDate DCM_StudyDate QString 20091231 /// No filter (empty) by default. - void setFilters(const QMap&); - QMap filters()const; + Q_INVOKABLE void setFilters(const QMap&); + Q_INVOKABLE QMap filters()const; + + /// operation is canceled? + Q_INVOKABLE bool wasCanceled(); + + /// Log level for dcmtk. Default: Error. + Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); + Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; + + /// Query a remote DICOM Image Store SCP. + /// You must at least set the host and port before calling query() + Q_INVOKABLE bool query(ctkDICOMDatabase& database); + + /// Access the list of study and series instance UIDs from the last query method. + QList> studyAndSeriesInstanceUIDQueried()const; + + /// Utility method to query a remote DICOM Image Store SCP only at patient level. + /// You must at least set the host and port before calling query(). + /// Results will be stored in the ctkDICOMJobResponseSet list (jobResponseSets). + Q_INVOKABLE bool queryPatients(); + + /// Utility method to query a remote DICOM Image Store SCP only at study level. + /// You must at least set the host and port before calling query(). + /// Results will be stored in the ctkDICOMJobResponseSet list (jobResponseSets). + Q_INVOKABLE bool queryStudies(const QString& patientID); + + /// Utility method to query a remote DICOM Image Store SCP only at series level + /// given a studyInstanceUID. + /// You must at least set the host and port before calling query(). + /// Results will be stored in the ctkDICOMJobResponseSet list (jobResponseSets). + Q_INVOKABLE bool querySeries(const QString& patientID, + const QString& studyInstanceUID); + + /// Utility method to query a remote DICOM Image Store SCP only at instance level + /// given a studyInstanceUID and seriesInstanceUID. + /// You must at least set the host and port before calling query(). + /// Results will be stored in the ctkDICOMJobResponseSet list (jobResponseSets). + Q_INVOKABLE bool queryInstances(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID); + + /// Access the list of datasets from the last queryPatients, queryStudies, + /// querySeries and queryInstances methods. + Q_INVOKABLE QList jobResponseSets() const; + QList> jobResponseSetsShared() const; + void setJobUID(const QString& jobUID); + QString jobUID() const; Q_SIGNALS: /// Signal is emitted inside the query() function. It ranges from 0 to 100. @@ -118,6 +167,9 @@ public Q_SLOTS: void cancel(); protected: + QString applyFilters(); + bool initializeSCU(); + QScopedPointer d_ptr; private: diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp new file mode 100644 index 0000000000..b233a2db96 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -0,0 +1,210 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMQueryJob_p.h" +#include "ctkDICOMQueryWorker.h" +#include "ctkDICOMServer.h" +#include "ctkLogger.h" + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMQueryJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMQueryJobPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMQueryJobPrivate::ctkDICOMQueryJobPrivate(ctkDICOMQueryJob* object) + : q_ptr(object) +{ + this->Server = nullptr; + this->MaximumPatientsQuery = 25; +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryJobPrivate::~ctkDICOMQueryJobPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMQueryJob methods + +//------------------------------------------------------------------------------ +ctkDICOMQueryJob::ctkDICOMQueryJob() + : d_ptr(new ctkDICOMQueryJobPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryJob::ctkDICOMQueryJob(ctkDICOMQueryJobPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryJob::~ctkDICOMQueryJob() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryJob::setFilters(const QMap &filters) +{ + Q_D(ctkDICOMQueryJob); + d->Filters = filters; +} + +//---------------------------------------------------------------------------- +QMap ctkDICOMQueryJob::filters() const +{ + Q_D(const ctkDICOMQueryJob); + return d->Filters; +} + +//------------------------------------------------------------------------------ +void ctkDICOMQueryJob::setMaximumPatientsQuery(const int maximumPatientsQuery) +{ + Q_D(ctkDICOMQueryJob); + d->MaximumPatientsQuery = maximumPatientsQuery; +} + +//------------------------------------------------------------------------------ +int ctkDICOMQueryJob::maximumPatientsQuery() +{ + Q_D(const ctkDICOMQueryJob); + return d->MaximumPatientsQuery; +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMQueryJob::server() const +{ + Q_D(const ctkDICOMQueryJob); + return d->Server.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMQueryJob::serverShared() const +{ + Q_D(const ctkDICOMQueryJob); + return d->Server; +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryJob::setServer(ctkDICOMServer &server) +{ + Q_D(ctkDICOMQueryJob); + d->Server = QSharedPointer(&server, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryJob::setServer(QSharedPointer server) +{ + Q_D(ctkDICOMQueryJob); + d->Server = server; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMQueryJob::loggerReport(const QString &status) const +{ + switch (this->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + return QString("ctkDICOMQueryJob: query job at patients level %1.\n" + "JobUID: %2\n" + "Server: %3") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()); + case ctkDICOMJob::DICOMLevels::Studies: + return QString("ctkDICOMQueryJob: query job at studies level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()); + case ctkDICOMJob::DICOMLevels::Series: + return QString("ctkDICOMQueryJob: query job at series level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4\n" + "StudyInstanceUID: %5") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()); + case ctkDICOMJob::DICOMLevels::Instances: + return QString("ctkDICOMQueryJob: query job at instances level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4\n" + "StudyInstanceUID: %5\n" + "SeriesInstanceUID: %6") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()) + .arg(this->seriesInstanceUID()); + default: + return QString(""); + } +} +//------------------------------------------------------------------------------ +ctkDICOMJob* ctkDICOMQueryJob::generateCopy() const +{ + ctkDICOMQueryJob* newQueryJob = new ctkDICOMQueryJob; + newQueryJob->setMaximumPatientsQuery(this->maximumConcurrentJobsPerType()); + newQueryJob->setServer(this->serverShared()); + newQueryJob->setFilters(this->filters()); + newQueryJob->setDICOMLevel(this->dicomLevel()); + newQueryJob->setPatientID(this->patientID()); + newQueryJob->setStudyInstanceUID(this->studyInstanceUID()); + newQueryJob->setSeriesInstanceUID(this->seriesInstanceUID()); + newQueryJob->setSOPInstanceUID(this->sopInstanceUID()); + newQueryJob->setMaximumNumberOfRetry(this->maximumNumberOfRetry()); + newQueryJob->setRetryDelay(this->retryDelay()); + newQueryJob->setRetryCounter(this->retryCounter()); + newQueryJob->setIsPersistent(this->isPersistent()); + newQueryJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); + newQueryJob->setPriority(this->priority()); + + return newQueryJob; +} + +//------------------------------------------------------------------------------ +ctkDICOMWorker *ctkDICOMQueryJob::createWorker() +{ + ctkDICOMQueryWorker* worker = + new ctkDICOMQueryWorker; + worker->setJob(*this); + return worker; +} diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h new file mode 100644 index 0000000000..95c3720b5e --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -0,0 +1,106 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMQueryJob_h +#define __ctkDICOMQueryJob_h + +// Qt includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMJob.h" + +class ctkDICOMQueryJobPrivate; +class ctkDICOMServer; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob +{ + Q_OBJECT + Q_PROPERTY(int maximumPatientsQuery READ maximumPatientsQuery WRITE setMaximumPatientsQuery); + +public: + typedef ctkDICOMJob Superclass; + explicit ctkDICOMQueryJob(); + virtual ~ctkDICOMQueryJob(); + + /// + /// Filters are keyword/value pairs as generated by + /// the ctkDICOMWidgets in a human readable (and editable) + /// format. The Query is responsible for converting these + /// into the appropriate dicom syntax for the C-Find + /// Currently supports the keys: Name, Study, Series, ID, Modalities, + /// StartDate and EndDate + /// Key DICOM Tag Type Example + /// ----------------------------------------------------------- + /// Name DCM_PatientName QString JOHNDOE + /// Study DCM_StudyDescription QString + /// Series DCM_SeriesDescription QString + /// ID DCM_PatientID QString + /// Modalities DCM_ModalitiesInStudy QStringList CT, MR, MN + /// StartDate DCM_StudyDate QString 20090101 + /// EndDate DCM_StudyDate QString 20091231 + /// No filter (empty) by default. + Q_INVOKABLE void setFilters(const QMap &filters); + Q_INVOKABLE QMap filters()const; + + /// maximum number of responses allowed in one query + /// when query is at Patient level. Default is 25. + void setMaximumPatientsQuery(const int maximumPatientsQuery); + int maximumPatientsQuery(); + + /// Server + Q_INVOKABLE ctkDICOMServer* server() const; + QSharedPointer serverShared() const; + Q_INVOKABLE void setServer(ctkDICOMServer& server); + void setServer(QSharedPointer server); + + /// Logger report string formatting for specific task + Q_INVOKABLE QString loggerReport(const QString& status) const; + + /// Create a copy of the object + Q_INVOKABLE ctkDICOMJob* generateCopy() const; + + /// Generate worker for job + Q_INVOKABLE ctkDICOMWorker* createWorker(); + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMQueryJob(ctkDICOMQueryJobPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMQueryJob); + Q_DISABLE_COPY(ctkDICOMQueryJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob_p.h b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h new file mode 100644 index 0000000000..60178c0ebd --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h @@ -0,0 +1,48 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMQueryJobPrivate_h +#define __ctkDICOMQueryJobPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMQueryJob.h" + +//------------------------------------------------------------------------------ +class ctkDICOMQueryJobPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMQueryJob) + +protected: + ctkDICOMQueryJob* const q_ptr; + +public: + ctkDICOMQueryJobPrivate(ctkDICOMQueryJob* object); + ~ctkDICOMQueryJobPrivate(); + + QSharedPointer Server; + QMap Filters; + int MaximumPatientsQuery; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp new file mode 100644 index 0000000000..d5ef21df36 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -0,0 +1,232 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMQueryWorker_p.h" +#include "ctkDICOMQueryJob.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMServer.h" +#include "ctkLogger.h" + +static ctkLogger logger ("org.commontk.dicom.ctkDICOMQueryWorker"); + +//------------------------------------------------------------------------------ +// ctkDICOMQueryWorkerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMQueryWorkerPrivate::ctkDICOMQueryWorkerPrivate(ctkDICOMQueryWorker* object) + : q_ptr(object) +{ + this->Query = QSharedPointer(new ctkDICOMQuery); +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryWorkerPrivate::~ctkDICOMQueryWorkerPrivate() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMQueryWorkerPrivate::setQueryParameters() +{ + Q_Q(ctkDICOMQueryWorker); + + QSharedPointer queryJob = + qobject_cast>(q->Job); + if (!queryJob) + { + return; + } + + ctkDICOMServer* server = queryJob->server(); + if (!server) + { + return; + } + + this->Query->setConnectionName(server->connectionName()); + this->Query->setCallingAETitle(server->callingAETitle()); + this->Query->setCalledAETitle(server->calledAETitle()); + this->Query->setHost(server->host()); + this->Query->setPort(server->port()); + this->Query->setConnectionTimeout(server->connectionTimeout()); + this->Query->setJobUID(queryJob->jobUID()); + this->Query->setFilters(queryJob->filters()); +} + +//------------------------------------------------------------------------------ +// ctkDICOMQueryWorker methods + +//------------------------------------------------------------------------------ +ctkDICOMQueryWorker::ctkDICOMQueryWorker() + : d_ptr(new ctkDICOMQueryWorkerPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryWorker::ctkDICOMQueryWorker(ctkDICOMQueryWorkerPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMQueryWorker::~ctkDICOMQueryWorker() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryWorker::cancel() +{ + Q_D(const ctkDICOMQueryWorker); + d->Query->cancel(); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryWorker::setJob(ctkAbstractJob &job) +{ + this->setJob(QSharedPointer(&job, skipDelete)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryWorker::run() +{ + Q_D(const ctkDICOMQueryWorker); + QSharedPointer queryJob = + qobject_cast>(this->Job); + if (!queryJob) + { + return; + } + + QSharedPointer scheduler = + qobject_cast>(this->Scheduler); + if (!scheduler) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + if (queryJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + queryJob->setStatus(ctkAbstractJob::JobStatus::Running); + emit queryJob->started(); + + logger.debug("ctkDICOMQueryWorker : running job on thread id " + + QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + switch(queryJob->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + if (!d->Query->queryPatients()) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Studies: + if (!d->Query->queryStudies(queryJob->patientID())) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Series: + if (!d->Query->querySeries(queryJob->patientID(), + queryJob->studyInstanceUID())) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Instances: + if (!d->Query->queryInstances(queryJob->patientID(), + queryJob->studyInstanceUID(), + queryJob->seriesInstanceUID())) + { + emit queryJob->canceled(); + this->onJobCanceled(); + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + } + + if (d->Query->jobResponseSetsShared().count() > 0 && + queryJob->status() != ctkAbstractJob::JobStatus::Stopped) + { + scheduler->insertJobResponseSets(d->Query->jobResponseSetsShared()); + } + + queryJob->setStatus(ctkAbstractJob::JobStatus::Finished); + emit queryJob->finished(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMQueryWorker::setJob(QSharedPointer job) +{ + Q_D(ctkDICOMQueryWorker); + + QSharedPointer queryJob = + qobject_cast>(job); + if (!queryJob) + { + return; + } + + Superclass::setJob(job); + d->setQueryParameters(); +} + +//------------------------------------------------------------------------------ +QSharedPointer ctkDICOMQueryWorker::querierShared() const +{ + Q_D(const ctkDICOMQueryWorker); + return d->Query; +} + +//------------------------------------------------------------------------------ +ctkDICOMQuery* ctkDICOMQueryWorker::querier() const +{ + Q_D(const ctkDICOMQueryWorker); + return d->Query.data(); +} diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h new file mode 100644 index 0000000000..394562bfa7 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -0,0 +1,79 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMQueryWorker_h +#define __ctkDICOMQueryWorker_h + +// Qt includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMWorker.h" + +class ctkDICOMQuery; +class ctkDICOMQueryWorkerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkDICOMWorker +{ + Q_OBJECT + +public: + typedef ctkDICOMWorker Superclass; + explicit ctkDICOMQueryWorker(); + virtual ~ctkDICOMQueryWorker(); + + /// Execute worker + void run(); + + /// Cancel worker + void cancel(); + + /// Job + Q_INVOKABLE void setJob(ctkAbstractJob& job); + void setJob(QSharedPointer job); + + /// Querier + QSharedPointer querierShared() const; + Q_INVOKABLE ctkDICOMQuery* querier() const; + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMQueryWorker(ctkDICOMQueryWorkerPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMQueryWorker); + Q_DISABLE_COPY(ctkDICOMQueryWorker); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h new file mode 100644 index 0000000000..146a7c3a8b --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h @@ -0,0 +1,49 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMQueryWorkerPrivate_h +#define __ctkDICOMQueryWorkerPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMQuery.h" +#include "ctkDICOMQueryWorker.h" + +//------------------------------------------------------------------------------ +class ctkDICOMQueryWorkerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMQueryWorker) + +protected: + ctkDICOMQueryWorker* const q_ptr; + +public: + ctkDICOMQueryWorkerPrivate(ctkDICOMQueryWorker* object); + ~ctkDICOMQueryWorkerPrivate(); + + void setQueryParameters(); + + QSharedPointer Query; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index f2a0ebc497..137a5adb56 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -1,4 +1,4 @@ -/*========================================================================= +/*========================================================================= Library: CTK @@ -24,29 +24,27 @@ // ctkDICOMCore includes #include "ctkDICOMRetrieve.h" +#include "ctkErrorLogLevel.h" #include "ctkLogger.h" +#include "ctkDICOMJobResponseSet.h" // DCMTK includes -#include "dcmtk/dcmnet/dimse.h" -#include "dcmtk/dcmnet/diutil.h" -#include "dcmtk/dcmnet/scu.h" - -#include +#include +#include +#include #include #include #include +#include #include #include #include /* for class OFStandard */ #include /* for class DicomDirInterface */ - #include /* for dcmjpeg decoders */ #include /* for dcmjpeg encoders */ #include /* for DcmRLEDecoderRegistration */ #include /* for DcmRLEEncoderRegistration */ -#include "dcmtk/oflog/oflog.h" - static ctkLogger logger("org.commontk.dicom.DICOMRetrieve"); //------------------------------------------------------------------------------ @@ -68,14 +66,13 @@ class ctkDICOMRetrieveSCUPrivate : public DcmSCU RetrieveResponse *response, OFBool &waitForNextResponse) { - if (this->retrieve) + if (this->retrieve && !this->retrieve->wasCanceled()) { emit this->retrieve->progress(ctkDICOMRetrieve::tr("Got move request")); emit this->retrieve->progress(0); return this->DcmSCU::handleMOVEResponse( - presID, response, waitForNextResponse); + presID, response, waitForNextResponse); } - //return false; return EC_IllegalCall; }; @@ -86,30 +83,65 @@ class ctkDICOMRetrieveSCUPrivate : public DcmSCU OFBool& continueCGETSession, Uint16& cStoreReturnStatus) { - if (this->retrieve) + if (!this->retrieve || this->retrieve->wasCanceled()) { - OFString instanceUID; - incomingObject->findAndGetOFString(DCM_SOPInstanceUID, instanceUID); - QString qInstanceUID(instanceUID.c_str()); - emit this->retrieve->progress( - //: %1 is an instance UID - ctkDICOMRetrieve::tr("Got STORE request for %1").arg(qInstanceUID) - ); - emit this->retrieve->progress(0); - continueCGETSession = !this->retrieve->wasCanceled(); - if (this->retrieve && this->retrieve->database()) + return EC_IllegalCall; + } + + OFString instanceUID; + incomingObject->findAndGetOFString(DCM_SOPInstanceUID, instanceUID); + QString qInstanceUID(instanceUID.c_str()); + emit this->retrieve->progress( + //: %1 is an instance UID + ctkDICOMRetrieve::tr("Got STORE request for %1").arg(qInstanceUID) + ); + emit this->retrieve->progress(0); + if (!this->retrieve->jobUID().isEmpty()) + { + QSharedPointer jobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSOPInstance) + { + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); + } + else if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) { - this->retrieve->database()->insert(incomingObject); - return EC_Normal; + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSeries); } - else + else if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveStudy) { - return this->DcmSCU::handleSTORERequest( - presID, incomingObject, continueCGETSession, cStoreReturnStatus); + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); } + jobResponseSet->setPatientID(this->retrieve->patientID()); + jobResponseSet->setStudyInstanceUID(this->retrieve->studyInstanceUID()); + jobResponseSet->setSeriesInstanceUID(this->retrieve->seriesInstanceUID()); + jobResponseSet->setSOPInstanceUID(qInstanceUID); + jobResponseSet->setConnectionName(this->retrieve->connectionName()); + jobResponseSet->setDataset(incomingObject); + jobResponseSet->setJobUID(this->retrieve->jobUID()); + jobResponseSet->setCopyFile(true); + + // To Do: this should be emitted for all the RetrieveTypes, but we should change the insert in the + // ctkDICOMRetrieveWorker to happen every 10 frames (configurable). + // i.e. a slot in ctkDICOMRetrieveWorker with a counter. When the counter > batchLimit -> insert + if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) + { + emit this->retrieve->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + } + + this->retrieve->addJobResponseSet(jobResponseSet); + return EC_Normal; + } + else if (this->retrieve->dicomDatabase()) + { + this->retrieve->dicomDatabase()->insert(incomingObject, true, false); + return EC_Normal; + } + else + { + return this->DcmSCU::handleSTORERequest( + presID, incomingObject, continueCGETSession, cStoreReturnStatus); } - //return false; - return EC_IllegalCall; }; // called when status information from remote server @@ -118,14 +150,12 @@ class ctkDICOMRetrieveSCUPrivate : public DcmSCU RetrieveResponse* response, OFBool& continueCGETSession) { - if (this->retrieve) + if (this->retrieve && !this->retrieve->wasCanceled()) { emit this->retrieve->progress(ctkDICOMRetrieve::tr("Got CGET response")); emit this->retrieve->progress(0); - continueCGETSession = !this->retrieve->wasCanceled(); return this->DcmSCU::handleCGETResponse(presID, response, continueCGETSession); } - //return false; return EC_IllegalCall; }; }; @@ -142,27 +172,43 @@ class ctkDICOMRetrievePrivate: public QObject public: ctkDICOMRetrievePrivate(ctkDICOMRetrieve& obj); ~ctkDICOMRetrievePrivate(); + /// Keep the currently negotiated connection to the /// peer host open unless the connection parameters change - bool WasCanceled; - bool KeepAssociationOpen; - bool ConnectionParamsChanged; - bool LastRetrieveType; + bool Canceled; + bool KeepAssociationOpen; + bool ConnectionParamsChanged; + ctkDICOMRetrieve::RetrieveType LastRetrieveType; + + QString PatientID; + QString StudyInstanceUID; + QString SeriesInstanceUID; + QString SOPInstanceUID; + QString ConnectionName; + QString JobUID; + QSharedPointer Database; - ctkDICOMRetrieveSCUPrivate SCU; + ctkDICOMRetrieveSCUPrivate SCU; + T_ASC_PresentationContextID PresentationContext; QString MoveDestinationAETitle; - // do the retrieve, handling both series and study retrieves - enum RetrieveType { RetrieveNone, RetrieveSeries, RetrieveStudy }; - bool initializeSCU(const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType, - DcmDataset *retrieveParameters); - bool move ( const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType ); - bool get ( const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType ); + QList> JobResponseSets; + + bool initializeSCU(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType, + DcmDataset *retrieveParameters); + bool move(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType); + bool get(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType); }; //------------------------------------------------------------------------------ @@ -173,10 +219,16 @@ ctkDICOMRetrievePrivate::ctkDICOMRetrievePrivate(ctkDICOMRetrieve& obj) : q_ptr(&obj) { this->Database = QSharedPointer (0); - this->WasCanceled = false; + this->Canceled = false; this->KeepAssociationOpen = true; this->ConnectionParamsChanged = false; - this->LastRetrieveType = RetrieveNone; + this->LastRetrieveType = ctkDICOMRetrieve::RetrieveNone; + + this->PatientID = ""; + this->StudyInstanceUID = ""; + this->SeriesInstanceUID = ""; + this->ConnectionName = ""; + this->JobUID = ""; // Register the JPEG libraries in case we need them // (registration only happens once, so it's okay to call repeatedly) @@ -189,21 +241,25 @@ ctkDICOMRetrievePrivate::ctkDICOMRetrievePrivate(ctkDICOMRetrieve& obj) // register RLE decompression codec DcmRLEDecoderRegistration::registerCodecs(); - logger.info ( "Setting Transfer Syntaxes" ); + logger.debug("Setting Transfer Syntaxes"); OFList transferSyntaxes; - transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax ); - transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax ); - transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax ); - this->SCU.addPresentationContext ( - UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes ); - this->SCU.addPresentationContext ( - UID_GETStudyRootQueryRetrieveInformationModel, transferSyntaxes ); - - for (Uint16 i = 0; i < numberOfDcmLongSCUStorageSOPClassUIDs; i++) + transferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax); + transferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax); + this->SCU.addPresentationContext( + UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes); + this->SCU.addPresentationContext( + UID_GETStudyRootQueryRetrieveInformationModel, transferSyntaxes); + + for (Uint16 index = 0; index < numberOfDcmLongSCUStorageSOPClassUIDs; index++) { - this->SCU.addPresentationContext(dcmLongSCUStorageSOPClassUIDs[i], - transferSyntaxes, ASC_SC_ROLE_SCP); + this->SCU.addPresentationContext(dcmLongSCUStorageSOPClassUIDs[index], + transferSyntaxes, ASC_SC_ROLE_SCP); } + + this->SCU.setACSETimeout(3); + this->SCU.setConnectionTimeout(3); + this->SCU.setStorageDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString().c_str()); } //------------------------------------------------------------------------------ @@ -212,21 +268,24 @@ ctkDICOMRetrievePrivate::~ctkDICOMRetrievePrivate() // At least now be kind to the server and release association if (this->SCU.isConnected()) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } + + this->JobResponseSets.clear(); } //------------------------------------------------------------------------------ -bool ctkDICOMRetrievePrivate::initializeSCU( const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType, - DcmDataset *retrieveParameters) +bool ctkDICOMRetrievePrivate::initializeSCU(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType, + DcmDataset *retrieveParameters) { - // If we like to query another server than before, be sure to disconnect first if (this->SCU.isConnected() && this->ConnectionParamsChanged) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } // Connect to server if not already connected if (!this->SCU.isConnected()) @@ -250,64 +309,116 @@ bool ctkDICOMRetrievePrivate::initializeSCU( const QString& studyInstanceUID, this->ConnectionParamsChanged = false; // Setup query about what to be received from the PACS logger.debug ( "Setting Retrieve Parameters" ); - if ( retrieveType == RetrieveSeries ) + if (retrieveType == ctkDICOMRetrieve::RetrieveSOPInstance) { - retrieveParameters->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" ); - retrieveParameters->putAndInsertString ( DCM_SeriesInstanceUID, - seriesInstanceUID.toStdString().c_str() ); + retrieveParameters->putAndInsertString(DCM_QueryRetrieveLevel, "IMAGE"); + retrieveParameters->putAndInsertString(DCM_SOPInstanceUID, + sopInstanceUID.toStdString().c_str()); + retrieveParameters->putAndInsertString(DCM_SeriesInstanceUID, + seriesInstanceUID.toStdString().c_str()); // Always required to send all highler level unique keys, so add study here (we are in Study Root) - retrieveParameters->putAndInsertString ( DCM_StudyInstanceUID, - studyInstanceUID.toStdString().c_str() ); //TODO + retrieveParameters->putAndInsertString(DCM_StudyInstanceUID, + studyInstanceUID.toStdString().c_str()); + if (!patientID.isEmpty()) + { + retrieveParameters->putAndInsertString(DCM_PatientID, + patientID.toStdString().c_str()); + } + } + else if (retrieveType == ctkDICOMRetrieve::RetrieveSeries) + { + retrieveParameters->putAndInsertString(DCM_QueryRetrieveLevel, "SERIES"); + retrieveParameters->putAndInsertString(DCM_SeriesInstanceUID, + seriesInstanceUID.toStdString().c_str()); + // Always required to send all highler level unique keys, so add study here (we are in Study Root) + retrieveParameters->putAndInsertString(DCM_StudyInstanceUID, + studyInstanceUID.toStdString().c_str()); + if (!patientID.isEmpty()) + { + retrieveParameters->putAndInsertString(DCM_PatientID, + patientID.toStdString().c_str()); + } } else { retrieveParameters->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" ); retrieveParameters->putAndInsertString ( DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str() ); + if (!patientID.isEmpty()) + { + retrieveParameters->putAndInsertString(DCM_PatientID, + patientID.toStdString().c_str()); + } } + + this->LastRetrieveType = retrieveType; return true; } //------------------------------------------------------------------------------ -bool ctkDICOMRetrievePrivate::move ( const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType ) +bool ctkDICOMRetrievePrivate::move(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType) { + Q_Q(ctkDICOMRetrieve); + + this->JobResponseSets.clear(); + this->PatientID = patientID; + this->StudyInstanceUID = studyInstanceUID; + this->SeriesInstanceUID = seriesInstanceUID; + this->SOPInstanceUID = sopInstanceUID; + + if (this->Canceled) + { + return false; + } DcmDataset *retrieveParameters = new DcmDataset(); - if (! this->initializeSCU(studyInstanceUID, seriesInstanceUID, retrieveType, retrieveParameters) ) + if (!this->initializeSCU(patientID, + studyInstanceUID, + seriesInstanceUID, + sopInstanceUID, + retrieveType, + retrieveParameters)) { delete retrieveParameters; + logger.error("MOVE Request failed: SCU initialization failed"); return false; } - // Issue request logger.debug ( "Sending Move Request" ); OFList responses; - T_ASC_PresentationContextID presID = this->SCU.findPresentationContextID( - UID_MOVEStudyRootQueryRetrieveInformationModel, - "" /* don't care about transfer syntax */ ); - if (presID == 0) + this->PresentationContext = this->SCU.findPresentationContextID( + UID_MOVEStudyRootQueryRetrieveInformationModel, + "" /* don't care about transfer syntax */); + if (this->PresentationContext == 0) { logger.error ( "MOVE Request failed: No valid Study Root MOVE Presentation Context available" ); if (!this->KeepAssociationOpen) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } delete retrieveParameters; return false; } + if (this->Canceled) + { + return false; + } + // do the actual move request - OFCondition status = this->SCU.sendMOVERequest ( - presID, this->MoveDestinationAETitle.toStdString().c_str(), - retrieveParameters, &responses ); + OFCondition status = this->SCU.sendMOVERequest( + this->PresentationContext, this->MoveDestinationAETitle.toStdString().c_str(), + retrieveParameters, &responses); // Close association if we do not want to explicitly keep it open if (!this->KeepAssociationOpen) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } // Free some (little) memory delete retrieveParameters; @@ -320,6 +431,11 @@ bool ctkDICOMRetrievePrivate::move ( const QString& studyInstanceUID, return false; } + if (this->Canceled) + { + return false; + } + /* The server is permitted to acknowledge every image that was received, or * to send a single move response. * If there is only a single response, this can mean the following: @@ -340,13 +456,12 @@ bool ctkDICOMRetrievePrivate::move ( const QString& studyInstanceUID, if (rsp->m_numberOfCompletedSubops == 0) { logger.error ( "No images transferred by PACS!" ); - //throw std::runtime_error( std::string("No images transferred by PACS!") ); return false; } } else { - logger.error("MOVE request failed, server does report error"); + logger.debug("MOVE request failed, server does report error"); QString statusDetail("No details"); if (rsp->m_statusDetail != NULL) { @@ -355,8 +470,7 @@ bool ctkDICOMRetrievePrivate::move ( const QString& studyInstanceUID, statusDetail = "Status Detail: " + statusDetail.fromStdString(out.str()); } statusDetail.prepend("MOVE request failed: "); - logger.error(statusDetail); - //throw std::runtime_error( statusDetail.toStdString() ); + logger.debug(statusDetail); return false; } } @@ -377,20 +491,71 @@ bool ctkDICOMRetrievePrivate::move ( const QString& studyInstanceUID, .arg(QString::number(static_cast((*it)->m_numberOfWarningSubops))) .arg(QString::number(static_cast((*it)->m_numberOfFailedSubops))) ); + + if (this->Canceled) + { + return false; + } + + // if move was successful, add a taskResults to report it + QSharedPointer jobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSOPInstance) + { + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); + } + else if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) + { + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSeries); + } + else if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveStudy) + { + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); + } + jobResponseSet->setStudyInstanceUID(q->studyInstanceUID()); + jobResponseSet->setSeriesInstanceUID(q->seriesInstanceUID()); + jobResponseSet->setConnectionName(q->connectionName()); + jobResponseSet->setJobUID(q->jobUID()); + q->addJobResponseSet(jobResponseSet); + return true; } //------------------------------------------------------------------------------ -bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, - const QString& seriesInstanceUID, - const RetrieveType retrieveType ) +bool ctkDICOMRetrievePrivate::get(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& sopInstanceUID, + const ctkDICOMRetrieve::RetrieveType retrieveType) { Q_Q(ctkDICOMRetrieve); + this->JobResponseSets.clear(); + this->PatientID = patientID; + this->StudyInstanceUID = studyInstanceUID; + this->SeriesInstanceUID = seriesInstanceUID; + this->SOPInstanceUID = sopInstanceUID; + + if (this->Canceled) + { + return false; + } + DcmDataset *retrieveParameters = new DcmDataset(); - if (! this->initializeSCU(studyInstanceUID, seriesInstanceUID, retrieveType, retrieveParameters) ) + if (!this->initializeSCU(patientID, + studyInstanceUID, + seriesInstanceUID, + sopInstanceUID, + retrieveType, + retrieveParameters)) { delete retrieveParameters; + logger.error("MOVE Request failed: SCU initialization failed"); + return false; + } + + if (this->Canceled) + { return false; } @@ -399,26 +564,30 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, emit q->progress(ctkDICOMRetrieve::tr("Sending Get Request")); emit q->progress(0); OFList responses; - T_ASC_PresentationContextID presID = this->SCU.findPresentationContextID( + this->PresentationContext = this->SCU.findPresentationContextID( UID_GETStudyRootQueryRetrieveInformationModel, "" /* don't care about transfer syntax */ ); - if (presID == 0) + if (this->PresentationContext == 0) { logger.error ( "GET Request failed: No valid Study Root GET Presentation Context available" ); if (!this->KeepAssociationOpen) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } delete retrieveParameters; return false; } + if (this->Canceled) + { + return false; + } + emit q->progress(ctkDICOMRetrieve::tr("Found Presentation Context")); emit q->progress(1); // do the actual move request - OFCondition status = this->SCU.sendCGETRequest ( - presID, retrieveParameters, &responses ); + OFCondition status = this->SCU.sendCGETRequest(this->PresentationContext, retrieveParameters, &responses); emit q->progress(ctkDICOMRetrieve::tr("Sent Get Request")); emit q->progress(2); @@ -426,7 +595,7 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, // Close association if we do not want to explicitly keep it open if (!this->KeepAssociationOpen) { - this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); + this->SCU.releaseAssociation(); } // Free some (little) memory delete retrieveParameters; @@ -440,6 +609,11 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, return false; } + if (this->Canceled) + { + return false; + } + emit q->progress(ctkDICOMRetrieve::tr("Got Responses")); emit q->progress(3); @@ -463,13 +637,12 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, if (rsp->m_numberOfCompletedSubops == 0) { logger.error ( "No images transferred by PACS!" ); - //throw std::runtime_error( std::string("No images transferred by PACS!") ); return false; } } else { - logger.error("GET request failed, server does report error"); + logger.debug("GET request failed, server does report error"); QString statusDetail("No details"); if (rsp->m_statusDetail != NULL) { @@ -478,8 +651,7 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, statusDetail = "Status Detail: " + statusDetail.fromStdString(out.str()); } statusDetail.prepend("GET request failed: "); - logger.error(statusDetail); - //throw std::runtime_error( statusDetail.toStdString() ); + logger.debug(statusDetail); return false; } } @@ -490,6 +662,7 @@ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, { it++; } + logger.debug ( QString("GET responses report for study: %1\n" "%2 images transferred, and\n" @@ -516,7 +689,11 @@ ctkDICOMRetrieve::ctkDICOMRetrieve(QObject* parent) d_ptr(new ctkDICOMRetrievePrivate(*this)) { Q_D(ctkDICOMRetrieve); + + d->SCU.setVerbosePCMode(false); d->SCU.retrieve = this; // give the dcmtk level access to this for emitting signals + + this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -524,9 +701,23 @@ ctkDICOMRetrieve::~ctkDICOMRetrieve() { } -//------------------------------------------------------------------------------ /// Set methods for connectivity -void ctkDICOMRetrieve::setCallingAETitle( const QString& callingAETitle ) +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::setConnectionName(const QString &connectionName) +{ + Q_D(ctkDICOMRetrieve); + d->ConnectionName = connectionName; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::connectionName() const +{ + Q_D(const ctkDICOMRetrieve); + return d->ConnectionName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::setCallingAETitle(const QString& callingAETitle) { Q_D(ctkDICOMRetrieve); if (strcmp(callingAETitle.toStdString().c_str(), d->SCU.getAETitle().c_str())) @@ -614,6 +805,7 @@ QString ctkDICOMRetrieve::moveDestinationAETitle()const return d->MoveDestinationAETitle; } +//------------------------------------------------------------------------------ static void skipDelete(QObject* obj) { Q_UNUSED(obj); @@ -636,12 +828,108 @@ void ctkDICOMRetrieve::setDatabase(QSharedPointer dicomDatabas } //------------------------------------------------------------------------------ -QSharedPointer ctkDICOMRetrieve::database()const +ctkDICOMDatabase* ctkDICOMRetrieve::dicomDatabase()const +{ + Q_D(const ctkDICOMRetrieve); + return d->Database.data(); +} + +//------------------------------------------------------------------------------ +QSharedPointer ctkDICOMRetrieve::dicomDatabaseShared()const { Q_D(const ctkDICOMRetrieve); return d->Database; } +//------------------------------------------------------------------------------ +QList ctkDICOMRetrieve::jobResponseSets() const +{ + Q_D(const ctkDICOMRetrieve); + QList jobResponseSets; + foreach(QSharedPointer jobResponseSet, d->JobResponseSets) + { + jobResponseSets.append(jobResponseSet.data()); + } + + return jobResponseSets; +} + +//------------------------------------------------------------------------------ +QList> ctkDICOMRetrieve::jobResponseSetsShared() const +{ + Q_D(const ctkDICOMRetrieve); + return d->JobResponseSets; +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::addJobResponseSet(ctkDICOMJobResponseSet &jobResponseSet) +{ + this->addJobResponseSet(QSharedPointer(&jobResponseSet, skipDelete)); +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::addJobResponseSet(QSharedPointer jobResponseSet) +{ + Q_D(ctkDICOMRetrieve); + d->JobResponseSets.append(jobResponseSet); +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::removeJobResponseSet(QSharedPointer jobResponseSet) +{ + Q_D(ctkDICOMRetrieve); + d->JobResponseSets.removeOne(jobResponseSet); +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieve::setJobUID(const QString &jobUID) +{ + Q_D(ctkDICOMRetrieve); + d->JobUID = jobUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::jobUID() const +{ + Q_D(const ctkDICOMRetrieve); + return d->JobUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::patientID() const +{ + Q_D(const ctkDICOMRetrieve); + return d->PatientID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::studyInstanceUID() const +{ + Q_D(const ctkDICOMRetrieve); + return d->StudyInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::seriesInstanceUID() const +{ + Q_D(const ctkDICOMRetrieve); + return d->SeriesInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMRetrieve::sopInstanceUID() const +{ + Q_D(const ctkDICOMRetrieve); + return d->SOPInstanceUID; +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieve::RetrieveType ctkDICOMRetrieve::getLastRetrieveType() const +{ + Q_D(const ctkDICOMRetrieve); + return d->LastRetrieveType; +} + //------------------------------------------------------------------------------ void ctkDICOMRetrieve::setKeepAssociationOpen(const bool keepOpen) { @@ -650,27 +938,77 @@ void ctkDICOMRetrieve::setKeepAssociationOpen(const bool keepOpen) } //------------------------------------------------------------------------------ -bool ctkDICOMRetrieve::keepAssociationOpen() +bool ctkDICOMRetrieve::keepAssociationOpen() const { Q_D(const ctkDICOMRetrieve); return d->KeepAssociationOpen; } -void ctkDICOMRetrieve::setWasCanceled(const bool wasCanceled) +//----------------------------------------------------------------------------- +void ctkDICOMRetrieve::setConnectionTimeout(const int timeout) { Q_D(ctkDICOMRetrieve); - d->WasCanceled = wasCanceled; + d->SCU.setACSETimeout(timeout); + d->SCU.setConnectionTimeout(timeout); } -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- +int ctkDICOMRetrieve::connectionTimeout() const +{ + Q_D(const ctkDICOMRetrieve); + return d->SCU.getConnectionTimeout(); +} + +//----------------------------------------------------------------------------- bool ctkDICOMRetrieve::wasCanceled() { Q_D(const ctkDICOMRetrieve); - return d->WasCanceled; + return d->Canceled; +} + +//----------------------------------------------------------------------------- +void ctkDICOMRetrieve::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) +{ + OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; + if (level == ctkErrorLogLevel::LogLevel::Fatal) + { + dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Critical || + level == ctkErrorLogLevel::LogLevel::Error) + { + dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Warning) + { + dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Info) + { + dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Debug) + { + dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Trace || + level == ctkErrorLogLevel::LogLevel::Status) + { + dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; + } + + OFLog::configure(dcmtkLogLevel); +} + +//----------------------------------------------------------------------------- +ctkErrorLogLevel::LogLevel ctkDICOMRetrieve::DCMTKLogLevel() const +{ + return logger.logLevel(); } //------------------------------------------------------------------------------ -bool ctkDICOMRetrieve::moveStudy(const QString& studyInstanceUID) +bool ctkDICOMRetrieve::moveStudy(const QString& studyInstanceUID, + const QString& patientID) { if (studyInstanceUID.isEmpty()) { @@ -678,54 +1016,105 @@ bool ctkDICOMRetrieve::moveStudy(const QString& studyInstanceUID) return false; } Q_D(ctkDICOMRetrieve); - logger.info ( "Starting moveStudy" ); - return d->move ( studyInstanceUID, "", ctkDICOMRetrievePrivate::RetrieveStudy ); + logger.debug("Starting moveStudy"); + return d->move(patientID, studyInstanceUID, "", "", ctkDICOMRetrieve::RetrieveStudy); } //------------------------------------------------------------------------------ -bool ctkDICOMRetrieve::getStudy(const QString& studyInstanceUID) +bool ctkDICOMRetrieve::getStudy(const QString& studyInstanceUID, + const QString& patientID) { if (studyInstanceUID.isEmpty()) { - logger.error("Cannot receive series: Study Instance UID empty."); + logger.error("Cannot receive study: Study Instance UID empty."); return false; } Q_D(ctkDICOMRetrieve); - logger.info ( "Starting getStudy" ); - return d->get ( studyInstanceUID, "", ctkDICOMRetrievePrivate::RetrieveStudy ); + logger.debug("Starting getStudy"); + return d->get(patientID, studyInstanceUID, "", "", ctkDICOMRetrieve::RetrieveStudy); } //------------------------------------------------------------------------------ bool ctkDICOMRetrieve::moveSeries(const QString& studyInstanceUID, - const QString& seriesInstanceUID) + const QString& seriesInstanceUID, + const QString& patientID) { - if (studyInstanceUID.isEmpty() || seriesInstanceUID.isEmpty()) + if (studyInstanceUID.isEmpty() || + seriesInstanceUID.isEmpty()) { - logger.error("Cannot receive series: Either Study or Series Instance UID empty."); + logger.error("Cannot receive series: Study or Series Instance UID empty."); return false; } Q_D(ctkDICOMRetrieve); - logger.info ( "Starting moveSeries" ); - return d->move ( studyInstanceUID, seriesInstanceUID, ctkDICOMRetrievePrivate::RetrieveSeries ); + logger.debug("Starting moveSeries"); + return d->move(patientID, studyInstanceUID, seriesInstanceUID, "", ctkDICOMRetrieve::RetrieveSeries); } //------------------------------------------------------------------------------ bool ctkDICOMRetrieve::getSeries(const QString& studyInstanceUID, - const QString& seriesInstanceUID) + const QString& seriesInstanceUID, + const QString& patientID) { - if (studyInstanceUID.isEmpty() || seriesInstanceUID.isEmpty()) + if (studyInstanceUID.isEmpty() || + seriesInstanceUID.isEmpty()) + { + logger.error("Cannot receive series: Study or Series Instance UID empty."); + return false; + } + Q_D(ctkDICOMRetrieve); + logger.debug("Starting getSeries"); + return d->get(patientID, studyInstanceUID, seriesInstanceUID, "", ctkDICOMRetrieve::RetrieveSeries); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMRetrieve::moveSOPInstance(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& SOPInstanceUID, + const QString& patientID) +{ + Q_D(ctkDICOMRetrieve); + + if (studyInstanceUID.isEmpty() || + seriesInstanceUID.isEmpty() || + SOPInstanceUID.isEmpty()) { - logger.error("Cannot receive series: Either Study or Series Instance UID empty."); + logger.error("Cannot receive SOPInstance: Study, Series or SOP Instance UID empty."); return false; } + + logger.debug("Starting moveSOPInstance"); + return d->move(patientID, studyInstanceUID, seriesInstanceUID, SOPInstanceUID, ctkDICOMRetrieve::RetrieveSOPInstance); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMRetrieve::getSOPInstance(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& SOPInstanceUID, + const QString& patientID) +{ Q_D(ctkDICOMRetrieve); - logger.info ( "Starting getSeries" ); - return d->get ( studyInstanceUID, seriesInstanceUID, ctkDICOMRetrievePrivate::RetrieveSeries ); + + if (studyInstanceUID.isEmpty() || + seriesInstanceUID.isEmpty() || + SOPInstanceUID.isEmpty()) + { + logger.error("Cannot receive SOPInstance: Study, Series or SOP Instance UID empty."); + return false; + } + + logger.debug("Starting getSOPInstance"); + return d->get(patientID, studyInstanceUID, seriesInstanceUID, SOPInstanceUID, ctkDICOMRetrieve::RetrieveSOPInstance); } //------------------------------------------------------------------------------ void ctkDICOMRetrieve::cancel() { Q_D(ctkDICOMRetrieve); - d->WasCanceled = true; + d->Canceled = true; + + if (d->PresentationContext != 0) + { + d->SCU.sendCANCELRequest(d->PresentationContext); + d->PresentationContext = 0; + } } diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.h b/Libs/DICOM/Core/ctkDICOMRetrieve.h index a2723b249f..efadabaaa0 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.h @@ -31,70 +31,130 @@ // CTK Core includes #include "ctkDICOMDatabase.h" +#include "ctkErrorLogLevel.h" class ctkDICOMRetrievePrivate; +class ctkDICOMJobResponse; /// \ingroup DICOM_Core class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieve : public QObject { Q_OBJECT + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName); Q_PROPERTY(QString callingAETitle READ callingAETitle WRITE setCallingAETitle); Q_PROPERTY(QString calledAETitle READ calledAETitle WRITE setCalledAETitle); Q_PROPERTY(QString host READ host WRITE setHost); Q_PROPERTY(int port READ port WRITE setPort); Q_PROPERTY(QString moveDestinationAETitle READ moveDestinationAETitle WRITE setMoveDestinationAETitle); Q_PROPERTY(bool keepAssociationOpen READ keepAssociationOpen WRITE setKeepAssociationOpen); - Q_PROPERTY(bool wasCanceled READ wasCanceled WRITE setWasCanceled); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + Q_PROPERTY(QString seriesInstanceUID READ seriesInstanceUID); + Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID); + Q_PROPERTY(QString jobUID READ jobUID WRITE setJobUID); public: explicit ctkDICOMRetrieve(QObject* parent = 0); virtual ~ctkDICOMRetrieve(); /// Set methods for connectivity + /// name identifying the server + void setConnectionName(const QString& connectionName); + QString connectionName() const; /// CTK_AE - the AE string by which the peer host might /// recognize your request - Q_INVOKABLE void setCallingAETitle( const QString& callingAETitle ); - Q_INVOKABLE QString callingAETitle() const; + void setCallingAETitle(const QString& callingAETitle); + QString callingAETitle() const; /// CTK_AE - the AE of the service of peer host that you are calling /// which tells the host what you are requesting - Q_INVOKABLE void setCalledAETitle( const QString& calledAETitle ); - Q_INVOKABLE QString calledAETitle() const; + void setCalledAETitle(const QString& calledAETitle); + QString calledAETitle() const; /// peer hostname being connected to - Q_INVOKABLE void setHost( const QString& host ); - Q_INVOKABLE QString host() const; + void setHost(const QString& host); + QString host() const; /// [0, 65365] port on peer host - e.g. 11112 - Q_INVOKABLE void setPort( int port ); - Q_INVOKABLE int port() const; + void setPort(int port); + int port() const; /// Typically CTK_STORE or similar - needs to be something that the /// peer host knows about and is able to move data into /// Only used when calling moveSeries or moveStudy - Q_INVOKABLE void setMoveDestinationAETitle( const QString& moveDestinationAETitle ); - Q_INVOKABLE QString moveDestinationAETitle() const; + void setMoveDestinationAETitle(const QString& moveDestinationAETitle); + QString moveDestinationAETitle() const; /// prefer to keep using the existing association to peer host when doing /// multiple requests (default true) - Q_INVOKABLE void setKeepAssociationOpen(const bool keepOpen); - Q_INVOKABLE bool keepAssociationOpen(); - /// did someone cancel us during operation? - /// (default false) - Q_INVOKABLE void setWasCanceled(const bool wasCanceled); + void setKeepAssociationOpen(const bool keepOpen); + bool keepAssociationOpen() const; + /// connection timeout, default 3 sec. + void setConnectionTimeout(const int timeout); + int connectionTimeout() const; + + /// operation is canceled? Q_INVOKABLE bool wasCanceled(); + + /// Log level for dcmtk. Default: Error. + Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); + Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; + /// where to insert new data sets obtained via get (must be set for /// get to succeed) Q_INVOKABLE void setDatabase(ctkDICOMDatabase& dicomDatabase); void setDatabase(QSharedPointer dicomDatabase); - Q_INVOKABLE QSharedPointer database()const; + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + QSharedPointer dicomDatabaseShared() const; + + /// Access the list of datasets from the last operation. + Q_INVOKABLE QList jobResponseSets() const; + QList> jobResponseSetsShared() const; + Q_INVOKABLE void addJobResponseSet(ctkDICOMJobResponseSet& jobResponseSet); + void addJobResponseSet(QSharedPointer jobResponseSet); + void removeJobResponseSet(QSharedPointer jobResponseSet); + Q_INVOKABLE void setJobUID(const QString& jobUID); + Q_INVOKABLE QString jobUID() const; + + /// Patient ID from from the last operation. + QString patientID() const; + /// Study instance UID from from the last operation. + QString studyInstanceUID() const; + /// Series instance UID from from the last operation. + QString seriesInstanceUID() const; + /// SOP instance UID from from the last operation. + QString sopInstanceUID() const; + + enum RetrieveType + { + RetrieveNone, + RetrieveSOPInstance, + RetrieveSeries, + RetrieveStudy + }; + + /// last retrieve type + RetrieveType getLastRetrieveType() const; public Q_SLOTS: /// Use CMOVE to ask peer host to store data to move destination - Q_INVOKABLE bool moveSeries( const QString& studyInstanceUID, - const QString& seriesInstanceUID ); + Q_INVOKABLE bool moveSOPInstance(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& SOPInstanceUID, + const QString& patientID = ""); /// Use CMOVE to ask peer host to store data to move destination - Q_INVOKABLE bool moveStudy( const QString& studyInstanceUID ); + Q_INVOKABLE bool moveSeries(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& patientID = ""); + /// Use CMOVE to ask peer host to store data to move destination + Q_INVOKABLE bool moveStudy(const QString& studyInstanceUID, + const QString& patientID = ""); + /// Use CGET to ask peer host to store data to us + Q_INVOKABLE bool getSOPInstance(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& SOPInstanceUID, + const QString& patientID = ""); /// Use CGET to ask peer host to store data to us - Q_INVOKABLE bool getSeries( const QString& studyInstanceUID, - const QString& seriesInstanceUID ); + Q_INVOKABLE bool getSeries(const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& patientID = ""); /// Use CGET to ask peer host to store data to us - Q_INVOKABLE bool getStudy( const QString& studyInstanceUID ); + Q_INVOKABLE bool getStudy(const QString& studyInstanceUID, + const QString& patientID = ""); /// Cancel the current operation Q_INVOKABLE void cancel(); @@ -113,6 +173,8 @@ public Q_SLOTS: /// Signal is emitted inside the retrieve() function when finished with value /// true for success or false for error void done(const bool& error); + /// Signal is emitted inside the retrieve() function when a frame has been fetched + void progressJobDetail(QVariant data); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp new file mode 100644 index 0000000000..299a97b107 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -0,0 +1,187 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMRetrieveJob_p.h" +#include "ctkDICOMRetrieveWorker.h" +#include "ctkDICOMServer.h" +#include "ctkLogger.h" + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMRetrieveJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMRetrieveJobPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveJobPrivate::ctkDICOMRetrieveJobPrivate(ctkDICOMRetrieveJob* object) + : q_ptr(object) +{ + this->Server = nullptr; +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveJobPrivate::~ctkDICOMRetrieveJobPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMRetrieveJob methods + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveJob::ctkDICOMRetrieveJob() + : d_ptr(new ctkDICOMRetrieveJobPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveJob::ctkDICOMRetrieveJob(ctkDICOMRetrieveJobPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveJob::~ctkDICOMRetrieveJob() +{ +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMRetrieveJob::server() const +{ + Q_D(const ctkDICOMRetrieveJob); + return d->Server.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMRetrieveJob::serverShared() const +{ + Q_D(const ctkDICOMRetrieveJob); + return d->Server; +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveJob::setServer(ctkDICOMServer &server) +{ + Q_D(ctkDICOMRetrieveJob); + d->Server = QSharedPointer(&server, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveJob::setServer(QSharedPointer server) +{ + Q_D(ctkDICOMRetrieveJob); + d->Server = server; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const +{ + switch (this->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + return QString("ctkDICOMRetrieveJob: retrieve task at patients level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()); + case ctkDICOMJob::DICOMLevels::Studies: + return QString("ctkDICOMRetrieveJob: retrieve task at studies level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4\n" + "StudyInstanceUID: %5") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()); + case ctkDICOMJob::DICOMLevels::Series: + return QString("ctkDICOMRetrieveJob: retrieve task at series level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4\n" + "StudyInstanceUID: %5\n" + "SeriesInstanceUID: %6") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()) + .arg(this->seriesInstanceUID()); + case ctkDICOMJob::DICOMLevels::Instances: + return QString("ctkDICOMRetrieveJob: retrieve task at instances level %1.\n" + "JobUID: %2\n" + "Server: %3\n" + "PatientID: %4\n" + "StudyInstanceUID: %5\n" + "SeriesInstanceUID: %6\n" + "SOPInstanceUID: %7") + .arg(status) + .arg(this->jobUID()) + .arg(this->server()->connectionName()) + .arg(this->patientID()) + .arg(this->studyInstanceUID()) + .arg(this->seriesInstanceUID()) + .arg(this->sopInstanceUID()); + default: + return QString(""); + } +} +//------------------------------------------------------------------------------ +ctkDICOMJob* ctkDICOMRetrieveJob::generateCopy() const +{ + ctkDICOMRetrieveJob* newRetrieveJob = new ctkDICOMRetrieveJob; + newRetrieveJob->setServer(this->serverShared()); + newRetrieveJob->setDICOMLevel(this->dicomLevel()); + newRetrieveJob->setPatientID(this->patientID()); + newRetrieveJob->setStudyInstanceUID(this->studyInstanceUID()); + newRetrieveJob->setSeriesInstanceUID(this->seriesInstanceUID()); + newRetrieveJob->setSOPInstanceUID(this->sopInstanceUID()); + newRetrieveJob->setMaximumNumberOfRetry(this->maximumNumberOfRetry()); + newRetrieveJob->setRetryDelay(this->retryDelay()); + newRetrieveJob->setRetryCounter(this->retryCounter()); + newRetrieveJob->setIsPersistent(this->isPersistent()); + newRetrieveJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); + newRetrieveJob->setPriority(this->priority()); + + return newRetrieveJob; +} + +//------------------------------------------------------------------------------ +ctkDICOMWorker *ctkDICOMRetrieveJob::createWorker() +{ + ctkDICOMRetrieveWorker* worker = + new ctkDICOMRetrieveWorker; + worker->setJob(*this); + return worker; +} diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h new file mode 100644 index 0000000000..94ceeba7f0 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -0,0 +1,78 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMRetrieveJob_h +#define __ctkDICOMRetrieveJob_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMJob.h" + +class ctkDICOMRetrieveJobPrivate; +class ctkDICOMServer; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob +{ + Q_OBJECT + +public: + typedef ctkDICOMJob Superclass; + explicit ctkDICOMRetrieveJob(); + virtual ~ctkDICOMRetrieveJob(); + + /// Server + Q_INVOKABLE ctkDICOMServer* server() const; + QSharedPointer serverShared() const; + Q_INVOKABLE void setServer(ctkDICOMServer& server); + void setServer(QSharedPointer server); + + /// Logger report string formatting for specific task + Q_INVOKABLE QString loggerReport(const QString& status) const; + + /// Create a copy of the object + Q_INVOKABLE ctkDICOMJob* generateCopy() const; + + /// Generate worker for job + Q_INVOKABLE ctkDICOMWorker* createWorker(); + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMRetrieveJob(ctkDICOMRetrieveJobPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMRetrieveJob); + Q_DISABLE_COPY(ctkDICOMRetrieveJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h new file mode 100644 index 0000000000..58351b5a2e --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h @@ -0,0 +1,46 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMRetrieveJobPrivate_h +#define __ctkDICOMRetrieveJobPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMRetrieveJob.h" + +//------------------------------------------------------------------------------ +class ctkDICOMRetrieveJobPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMRetrieveJob) + +protected: + ctkDICOMRetrieveJob* const q_ptr; + +public: + ctkDICOMRetrieveJobPrivate(ctkDICOMRetrieveJob* object); + ~ctkDICOMRetrieveJobPrivate(); + + QSharedPointer Server; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp new file mode 100644 index 0000000000..c391dd49be --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -0,0 +1,327 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" +#include "ctkDICOMRetrieveWorker_p.h" +#include "ctkDICOMRetrieveJob.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMServer.h" +#include "ctkLogger.h" + +static ctkLogger logger ("org.commontk.dicom.ctkDICOMRetrieveWorker"); + +//------------------------------------------------------------------------------ +// ctkDICOMRetrieveWorkerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveWorkerPrivate::ctkDICOMRetrieveWorkerPrivate(ctkDICOMRetrieveWorker* object) + : q_ptr(object) +{ + this->Retrieve = QSharedPointer(new ctkDICOMRetrieve); +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveWorkerPrivate::~ctkDICOMRetrieveWorkerPrivate() +{ + Q_Q(ctkDICOMRetrieveWorker); + + QSharedPointer retrieveJob = + qobject_cast>(q->Job); + if (!retrieveJob) + { + return; + } + + QObject::disconnect(this->Retrieve.data(), SIGNAL(progressJobDetail(QVariant)), + retrieveJob.data(), SIGNAL(progressJobDetail(QVariant))); +} + +//------------------------------------------------------------------------------ +void ctkDICOMRetrieveWorkerPrivate::setRetrieveParameters() +{ + Q_Q(ctkDICOMRetrieveWorker); + + QSharedPointer retrieveJob = + qobject_cast>(q->Job); + if (!retrieveJob) + { + return; + } + + ctkDICOMServer* server = retrieveJob->server(); + if (!server) + { + return; + } + + this->Retrieve->setConnectionName(server->connectionName()); + this->Retrieve->setCallingAETitle(server->callingAETitle()); + this->Retrieve->setCalledAETitle(server->calledAETitle()); + this->Retrieve->setHost(server->host()); + this->Retrieve->setPort(server->port()); + this->Retrieve->setConnectionTimeout(server->connectionTimeout()); + this->Retrieve->setMoveDestinationAETitle(server->moveDestinationAETitle()); + this->Retrieve->setKeepAssociationOpen(server->keepAssociationOpen()); + this->Retrieve->setJobUID(retrieveJob->jobUID()); + + QObject::connect(this->Retrieve.data(), SIGNAL(progressJobDetail(QVariant)), + retrieveJob.data(), SIGNAL(progressJobDetail(QVariant)), Qt::DirectConnection); + +} + +//------------------------------------------------------------------------------ +// ctkDICOMRetrieveWorker methods + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveWorker::ctkDICOMRetrieveWorker() + : d_ptr(new ctkDICOMRetrieveWorkerPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveWorker::ctkDICOMRetrieveWorker(ctkDICOMRetrieveWorkerPrivate* pimpl) + : d_ptr(pimpl) +{ + Q_D(ctkDICOMRetrieveWorker); +} + +//------------------------------------------------------------------------------ +ctkDICOMRetrieveWorker::~ctkDICOMRetrieveWorker() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveWorker::cancel() +{ + Q_D(const ctkDICOMRetrieveWorker); + d->Retrieve->cancel(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveWorker::run() +{ + Q_D(const ctkDICOMRetrieveWorker); + QSharedPointer retrieveJob = + qobject_cast>(this->Job); + if (!retrieveJob) + { + return; + } + + QSharedPointer scheduler = + qobject_cast>(this->Scheduler); + if (!scheduler) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + ctkDICOMServer* server = retrieveJob->server(); + if (!server) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + if (retrieveJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Running); + emit retrieveJob->started(); + + logger.debug("ctkDICOMRetrieveWorker : running job on thread id " + + QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + switch (server->retrieveProtocol()) + { + case ctkDICOMServer::CGET: + switch(retrieveJob->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + logger.info("ctkDICOMRetrieveTask : get operation for a full patient is not implemented."); + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + case ctkDICOMJob::DICOMLevels::Studies: + if (!d->Retrieve->getStudy(retrieveJob->studyInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Series: + if (!d->Retrieve->getSeries(retrieveJob->studyInstanceUID(), + retrieveJob->seriesInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Instances: + if (!d->Retrieve->getSOPInstance(retrieveJob->studyInstanceUID(), + retrieveJob->seriesInstanceUID(), + retrieveJob->sopInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + } + break; + case ctkDICOMServer::CMOVE: + switch(retrieveJob->dicomLevel()) + { + case ctkDICOMJob::DICOMLevels::Patients: + logger.info("ctkDICOMRetrieveTask : move operation for a full patient is not implemented."); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + emit retrieveJob->failed(); + return; + case ctkDICOMJob::DICOMLevels::Studies: + if (!d->Retrieve->moveStudy(retrieveJob->studyInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Series: + if (!d->Retrieve->moveSeries(retrieveJob->studyInstanceUID(), + retrieveJob->seriesInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + case ctkDICOMJob::DICOMLevels::Instances: + if (!d->Retrieve->moveSOPInstance(retrieveJob->studyInstanceUID(), + retrieveJob->seriesInstanceUID(), + retrieveJob->sopInstanceUID(), + retrieveJob->patientID())) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + break; + } + break; + //case ctkDICOMServer::WADO: // To Do + } + + if (retrieveJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit retrieveJob->canceled(); + this->onJobCanceled(); + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + ctkDICOMServer* proxyServer = server->proxyServer(); + if (proxyServer && proxyServer->queryRetrieveEnabled()) + { + ctkDICOMRetrieveJob* newJob = qobject_cast(retrieveJob->generateCopy()); + newJob->setRetryCounter(0); + newJob->setServer(*proxyServer); + scheduler->addJob(newJob); + } + else if (d->Retrieve->jobResponseSetsShared().count() > 0) + { + // To Do: this insert should happen in batch of 10 frames (configurable), + // instead of at the end of operation (all frames requested)). + // This would avoid memory usage spikes when requesting a series or study with a lot of frames. + // NOTE: the memory release should happen as soon as we insert the response. + scheduler->insertJobResponseSets(d->Retrieve->jobResponseSetsShared()); + } + + retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); + emit retrieveJob->finished(); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveWorker::setJob(ctkAbstractJob &job) +{ + this->setJob(QSharedPointer(&job, skipDelete)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMRetrieveWorker::setJob(QSharedPointer job) +{ + Q_D(ctkDICOMRetrieveWorker); + + QSharedPointer retrieveJob = + qobject_cast>(job); + if (!retrieveJob) + { + return; + } + + Superclass::setJob(job); + d->setRetrieveParameters(); +} + +//---------------------------------------------------------------------------- +ctkDICOMRetrieve *ctkDICOMRetrieveWorker::retriever() const +{ + Q_D(const ctkDICOMRetrieveWorker); + return d->Retrieve.data(); +} + +//------------------------------------------------------------------------------ +QSharedPointer ctkDICOMRetrieveWorker::retrieverShared() const +{ + Q_D(const ctkDICOMRetrieveWorker); + return d->Retrieve; +} diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h new file mode 100644 index 0000000000..15a8ea60c9 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -0,0 +1,77 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMRetrieveWorker_h +#define __ctkDICOMRetrieveWorker_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMWorker.h" + +class ctkDICOMRetrieve; +class ctkDICOMRetrieveWorkerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkDICOMWorker +{ + Q_OBJECT + +public: + typedef ctkDICOMWorker Superclass; + explicit ctkDICOMRetrieveWorker(); + virtual ~ctkDICOMRetrieveWorker(); + + /// Execute worker + void run(); + + /// Cancel worker + void cancel(); + + /// Job + Q_INVOKABLE void setJob(ctkAbstractJob& job); + void setJob(QSharedPointer job); + + /// Retriever + QSharedPointer retrieverShared() const; + Q_INVOKABLE ctkDICOMRetrieve* retriever() const; + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMRetrieveWorker(ctkDICOMRetrieveWorkerPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMRetrieveWorker); + Q_DISABLE_COPY(ctkDICOMRetrieveWorker); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h new file mode 100644 index 0000000000..a124dc5967 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h @@ -0,0 +1,49 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMRetrieveWorkerPrivate_h +#define __ctkDICOMRetrieveWorkerPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMRetrieve.h" +#include "ctkDICOMRetrieveWorker.h" + +//------------------------------------------------------------------------------ +class ctkDICOMRetrieveWorkerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMRetrieveWorker) + +protected: + ctkDICOMRetrieveWorker* const q_ptr; + +public: + ctkDICOMRetrieveWorkerPrivate(ctkDICOMRetrieveWorker* object); + ~ctkDICOMRetrieveWorkerPrivate(); + + void setRetrieveParameters(); + + QSharedPointer Retrieve; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp new file mode 100644 index 0000000000..52143e061e --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -0,0 +1,1129 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMInserterJob.h" +#include "ctkDICOMQueryJob.h" +#include "ctkDICOMRetrieveJob.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMScheduler_p.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMStorageListenerJob.h" +#include "ctkDICOMUtil.h" +#include "ctkDICOMWorker.h" +#include "ctkLogger.h" + +// dcmtk includes +#include + + +static ctkLogger logger ( "org.commontk.dicom.DICOMJobPool" ); + +//------------------------------------------------------------------------------ +// ctkDICOMSchedulerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMSchedulerPrivate::ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj) + : q_ptr(&obj) +{ + ctk::setDICOMLogLevel(ctkErrorLogLevel::Info); + + this->DicomDatabase = nullptr; + this->ThreadPool = QSharedPointer (new QThreadPool()); + this->ThreadPool->setMaxThreadCount(20); + this->RetryDelay = 100; + this->MaximumNumberOfRetry = 3; + this->MaximumPatientsQuery = 25; +} + +//------------------------------------------------------------------------------ +ctkDICOMSchedulerPrivate::~ctkDICOMSchedulerPrivate() +{ + Q_Q(ctkDICOMScheduler); + q->removeAllServers(); +} + +//------------------------------------------------------------------------------ +int ctkDICOMSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) +{ + int count = 0; + foreach (QSharedPointer queuedJob, this->JobsQueue) + { + if (queuedJob->jobUID() == job->jobUID()) + { + continue; + } + + if ((queuedJob->status() == ctkAbstractJob::JobStatus::Queued || + queuedJob->status() == ctkAbstractJob::JobStatus::Running) && + queuedJob->className() == job->className()) + { + count++; + } + } + + return count; +} + +//------------------------------------------------------------------------------ +void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) +{ + Q_Q(ctkDICOMScheduler); + + if (!job) + { + return; + } + + logger.debug(QString("ctkDICOMScheduler: creating job object %1 of type %2 in thread %3") + .arg(job->jobUID()) + .arg(job->className()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + + QObject::connect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); + QObject::connect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); + QObject::connect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); + QObject::connect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); + QObject::connect(job.data(), SIGNAL(progressJobDetail(QVariant)), + q, SIGNAL(progressJobDetail(QVariant))); + + QMutexLocker ml(&this->mMutex); + this->JobsQueue.insert(job->jobUID(), job); + emit q->queueJobs(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMSchedulerPrivate::removeJob(QString jobUID) +{ + Q_Q(ctkDICOMScheduler); + + logger.debug(QString("ctkDICOMScheduler: deleting job object %1 in thread %2") + .arg(jobUID) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); + + QSharedPointer job = this->JobsQueue.value(jobUID); + if (!job) + { + return; + } + + QObject::disconnect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); + QObject::disconnect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); + QObject::disconnect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); + QObject::disconnect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); + QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), q, SIGNAL(progressJobDetail(QVariant))); + + this->JobsQueue.remove(jobUID); + emit q->queueJobs(); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSchedulerPrivate::generateUniqueJobUID() +{ + return QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); +} + +//------------------------------------------------------------------------------ +ctkDICOMServer *ctkDICOMSchedulerPrivate::getServerFromProxyServersByConnectionName(const QString &connectionName) +{ + foreach (QSharedPointer server, this->Servers) + { + ctkDICOMServer* proxyServer = server->proxyServer(); + if (proxyServer && proxyServer->connectionName() == connectionName) + { + return proxyServer; + } + } + + return nullptr; +} + +//------------------------------------------------------------------------------ +// ctkDICOMScheduler methods + +//------------------------------------------------------------------------------ +ctkDICOMScheduler::ctkDICOMScheduler(QObject* parentObject) + : Superclass(parentObject) + , d_ptr(new ctkDICOMSchedulerPrivate(*this)) +{ + QObject::connect(this, SIGNAL(queueJobs()), + this, SLOT(onQueueJobsInThreadPool()), + Qt::QueuedConnection); +} + +//------------------------------------------------------------------------------ +ctkDICOMScheduler::~ctkDICOMScheduler() +{ + this->stopAllJobs(true); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::queryPatients(QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMQueryJob); + job->setServer(server); + job->setMaximumPatientsQuery(d->MaximumPatientsQuery); + job->setFilters(d->Filters); + job->setDICOMLevel(ctkDICOMQueryJob::DICOMLevels::Patients); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::queryStudies(const QString& patientID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMQueryJob); + job->setServer(server); + job->setFilters(d->Filters); + job->setDICOMLevel(ctkDICOMQueryJob::DICOMLevels::Studies); + job->setPatientID(patientID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::querySeries(const QString& patientID, + const QString& studyInstanceUID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMQueryJob); + job->setServer(server); + job->setFilters(d->Filters); + job->setDICOMLevel(ctkDICOMQueryJob::DICOMLevels::Series); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::queryInstances(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMQueryJob); + job->setServer(server); + job->setFilters(d->Filters); + job->setDICOMLevel(ctkDICOMQueryJob::DICOMLevels::Instances); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setSeriesInstanceUID(seriesInstanceUID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::retrieveStudy(const QString &patientID, + const QString &studyInstanceUID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMRetrieveJob); + job->setServer(server); + job->setDICOMLevel(ctkDICOMRetrieveJob::DICOMLevels::Studies); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::retrieveSeries(const QString &patientID, + const QString &studyInstanceUID, + const QString &seriesInstanceUID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMRetrieveJob); + job->setServer(server); + job->setDICOMLevel(ctkDICOMRetrieveJob::DICOMLevels::Series); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setSeriesInstanceUID(seriesInstanceUID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::retrieveSOPInstance(const QString &patientID, + const QString &studyInstanceUID, + const QString &seriesInstanceUID, + const QString &SOPInstanceUID, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + foreach(QSharedPointer server, d->Servers) + { + if (!server->queryRetrieveEnabled()) + { + continue; + } + + QSharedPointer job = + QSharedPointer(new ctkDICOMRetrieveJob); + job->setServer(server); + job->setDICOMLevel(ctkDICOMRetrieveJob::DICOMLevels::Instances); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setSeriesInstanceUID(seriesInstanceUID); + job->setSOPInstanceUID(SOPInstanceUID); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::startListener(const int port, + const QString &AETitle, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + QSharedPointer job = + QSharedPointer(new ctkDICOMStorageListenerJob); + job->setPort(port); + job->setAETitle(AETitle); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setPriority(priority); + + d->insertJob(job); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::insertJobResponseSet(QSharedPointer jobResponseSet, + QThread::Priority priority) +{ + QList> jobResponseSets; + jobResponseSets.append(jobResponseSet); + this->insertJobResponseSets(jobResponseSets, priority); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::insertJobResponseSets(QList> jobResponseSets, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + QSharedPointer job = + QSharedPointer(new ctkDICOMInserterJob); + job->copyJobResponseSets(jobResponseSets); + job->setMaximumNumberOfRetry(d->MaximumNumberOfRetry); + job->setRetryDelay(d->RetryDelay); + job->setDatabaseFilename(d->DicomDatabase->databaseFilename()); + job->setTagsToPrecache(d->DicomDatabase->tagsToPrecache()); + job->setTagsToExcludeFromStorage(d->DicomDatabase->tagsToExcludeFromStorage()); + job->setPriority(priority); + + d->insertJob(job); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMDatabase* ctkDICOMScheduler::dicomDatabase()const +{ + Q_D(const ctkDICOMScheduler); + return d->DicomDatabase.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMScheduler::dicomDatabaseShared()const +{ + Q_D(const ctkDICOMScheduler); + return d->DicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setDicomDatabase(ctkDICOMDatabase& dicomDatabase) +{ + Q_D(ctkDICOMScheduler); + d->DicomDatabase = QSharedPointer(&dicomDatabase, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setDicomDatabase(QSharedPointer dicomDatabase) +{ + Q_D(ctkDICOMScheduler); + d->DicomDatabase = dicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setFilters(const QMap &filters) +{ + Q_D(ctkDICOMScheduler); + d->Filters = filters; +} + +//---------------------------------------------------------------------------- +QMap ctkDICOMScheduler::filters() const +{ + Q_D(const ctkDICOMScheduler); + return d->Filters; +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::getNumberOfServers() +{ + Q_D(ctkDICOMScheduler); + return d->Servers.size(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::getNumberOfQueryRetrieveServers() +{ + Q_D(ctkDICOMScheduler); + int numberOfServers = 0; + foreach (QSharedPointer server, d->Servers) + { + if (server && server->queryRetrieveEnabled()) + { + numberOfServers++; + } + } + return numberOfServers; +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::getNumberOfStorageServers() +{ + Q_D(ctkDICOMScheduler); + int numberOfServers = 0; + foreach (QSharedPointer server, d->Servers) + { + if (server && server->storageEnabled()) + { + numberOfServers++; + } + } + return numberOfServers; +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMScheduler::getNthServer(int id) +{ + Q_D(ctkDICOMScheduler); + if (id < 0 || id > d->Servers.size() - 1) + { + return nullptr; + } + return d->Servers.at(id).data(); +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMScheduler::getServer(const char *connectionName) +{ + Q_D(ctkDICOMScheduler); + ctkDICOMServer* server = this->getNthServer(this->getServerIndexFromName(connectionName)); + if (!server) + { + server = d->getServerFromProxyServersByConnectionName(connectionName); + } + return server; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::addServer(ctkDICOMServer& server) +{ + Q_D(ctkDICOMScheduler); + QSharedPointer QSharedServer = QSharedPointer(&server, skipDelete); + d->Servers.append(QSharedServer); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::addServer(QSharedPointer server) +{ + Q_D(ctkDICOMScheduler); + d->Servers.append(server); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::removeServer(const char *connectionName) +{ + this->removeNthServer(this->getServerIndexFromName(connectionName)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::removeNthServer(int id) +{ + Q_D(ctkDICOMScheduler); + if (id < 0 || id > d->Servers.size() - 1) + { + return; + } + + d->Servers.removeAt(id); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::removeAllServers() +{ + Q_D(ctkDICOMScheduler); + d->Servers.clear(); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMScheduler::getServerNameFromIndex(int id) +{ + Q_D(ctkDICOMScheduler); + if (id < 0 || id > d->Servers.size() - 1) + { + return ""; + } + + QSharedPointer server = d->Servers.at(id); + if (!server) + { + return ""; + } + + return server->connectionName(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::getServerIndexFromName(const char *connectionName) +{ + Q_D(ctkDICOMScheduler); + if (!connectionName) + { + return -1; + } + for(int serverIndex = 0; serverIndex < d->Servers.size(); ++serverIndex) + { + QSharedPointer server = d->Servers.at(serverIndex); + if (server && server->connectionName() == connectionName) + { + // found + return serverIndex; + } + } + return -1; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::waitForFinish() +{ + Q_D(ctkDICOMScheduler); + + int numberOfPersistentJobs = this->numberOfPersistentJobs(); + while(this->numberOfJobs() > numberOfPersistentJobs) + { + QCoreApplication::processEvents(); + d->ThreadPool->waitForDone(300); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::waitForDone(int msec) +{ + Q_D(ctkDICOMScheduler); + + QCoreApplication::processEvents(); + d->ThreadPool->waitForDone(msec); +} + +Q_INVOKABLE void waitForFinish(); +void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, + const QStringList &studyInstanceUIDs, + const QStringList &seriesInstanceUIDs, + const QStringList &sopInstanceUIDs) +{ + Q_D(ctkDICOMScheduler); + + if (patientIDs.count() == 0 && + studyInstanceUIDs.count() == 0 && + seriesInstanceUIDs.count() == 0 && + sopInstanceUIDs.count() == 0) + { + return; + } + + bool wait = true; + while(wait) + { + QCoreApplication::processEvents(); + d->ThreadPool->waitForDone(300); + + wait = false; + foreach (QSharedPointer job, d->JobsQueue) + { + if (!job) + { + continue; + } + + if (job->isPersistent()) + { + continue; + } + + if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || + (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || + (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || + (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + { + if (job->status() != ctkAbstractJob::JobStatus::Finished) + { + wait = true; + break; + } + } + } + } +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::numberOfJobs() +{ + Q_D(ctkDICOMScheduler); + QMutexLocker ml(&d->mMutex); + return d->JobsQueue.count(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::numberOfPersistentJobs() +{ + Q_D(ctkDICOMScheduler); + int cont = 0; + QMutexLocker ml(&d->mMutex); + foreach (QSharedPointer job, d->JobsQueue) + { + if (job->isPersistent()) + { + cont++; + } + } + + return cont; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::addJob(ctkDICOMJob *job) +{ + Q_D(ctkDICOMScheduler); + + QSharedPointer jobShared = QSharedPointer(job); + d->insertJob(jobShared); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::deleteJob(QString jobUID) +{ + Q_D(ctkDICOMScheduler); + d->removeJob(jobUID); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::deleteWorker(QString jobUID) +{ + Q_D(ctkDICOMScheduler); + + QMap>::iterator it = d->Workers.find(jobUID); + if (it == d->Workers.end()) + { + return; + } + + d->Workers.remove(jobUID); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMScheduler::getJobSharedByUID(QString JobUID) +{ + Q_D(ctkDICOMScheduler); + + QMutexLocker ml(&d->mMutex); + QMap>::iterator it = d->JobsQueue.find(JobUID); + if (it == d->JobsQueue.end()) + { + return nullptr; + } + + return d->JobsQueue.value(JobUID); +} + +//---------------------------------------------------------------------------- +ctkDICOMJob *ctkDICOMScheduler::getJobByUID(QString jobUID) +{ + QSharedPointer job = this->getJobSharedByUID(jobUID); + if (!job) + { + return nullptr; + } + + return job.data(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::stopAllJobs(bool stopPersistentJobs) +{ + Q_D(ctkDICOMScheduler); + + QMutexLocker ml(&d->mMutex); + + // Stops jobs without a worker (in waiting) + foreach (QSharedPointer job, d->JobsQueue) + { + if (job->isPersistent() && !stopPersistentJobs) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Initialized) + { + continue; + } + + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + this->deleteJob(job->jobUID()); + } + + // Stops queued and running jobs + foreach (QSharedPointer worker, d->Workers) + { + QSharedPointer job = worker->jobShared(); + if (job->isPersistent() && !stopPersistentJobs) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Running && + job->status() != ctkAbstractJob::JobStatus::Queued) + { + continue; + } + + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + worker->cancel(); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, + const QStringList& studyInstanceUIDs, + const QStringList& seriesInstanceUIDs, + const QStringList& sopInstanceUIDs) +{ + Q_D(ctkDICOMScheduler); + + if (patientIDs.count() == 0 && + studyInstanceUIDs.count() == 0 && + seriesInstanceUIDs.count() == 0 && + sopInstanceUIDs.count() == 0) + { + return; + } + + QMutexLocker ml(&d->mMutex); + + // Stops jobs without a worker (in waiting) + foreach (QSharedPointer job, d->JobsQueue) + { + if (!job) + { + continue; + } + + if (job->isPersistent()) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Initialized) + { + continue; + } + + if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || + (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || + (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || + (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + { + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + this->deleteJob(job->jobUID()); + } + } + + // Stops queued and running jobs + foreach (QSharedPointer worker, d->Workers) + { + QSharedPointer job = + qobject_cast>(worker->jobShared()); + if (!job) + { + continue; + } + + if (job->isPersistent()) + { + continue; + } + + if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || + (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || + (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || + (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + { + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + worker->cancel(); + } + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::raiseJobsPriorityForSeries(const QStringList& selectedSeriesInstanceUIDs, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + if (selectedSeriesInstanceUIDs.count() == 0) + { + return; + } + + QMutexLocker ml(&d->mMutex); + foreach (QSharedPointer job, d->JobsQueue) + { + if (job->isPersistent()) + { + continue; + } + + if (!selectedSeriesInstanceUIDs.contains(job->seriesInstanceUID())) + { + priority = QThread::Priority::LowPriority; + } + + job->setPriority(priority); + } +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::maximumThreadCount() const +{ + Q_D(const ctkDICOMScheduler); + return d->ThreadPool->maxThreadCount(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setMaximumThreadCount(const int &maximumThreadCount) +{ + Q_D(ctkDICOMScheduler); + + d->ThreadPool->setMaxThreadCount(maximumThreadCount); +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::maximumNumberOfRetry() const +{ + Q_D(const ctkDICOMScheduler); + return d->MaximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) +{ + Q_D(ctkDICOMScheduler); + d->MaximumNumberOfRetry = maximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +int ctkDICOMScheduler::retryDelay() const +{ + Q_D(const ctkDICOMScheduler); + return d->RetryDelay; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::setRetryDelay(const int &retryDelay) +{ + Q_D(ctkDICOMScheduler); + d->RetryDelay = retryDelay; +} + +//------------------------------------------------------------------------------ +void ctkDICOMScheduler::setMaximumPatientsQuery(const int maximumPatientsQuery) +{ + Q_D(ctkDICOMScheduler); + d->MaximumPatientsQuery = maximumPatientsQuery; +} + +//------------------------------------------------------------------------------ +int ctkDICOMScheduler::maximumPatientsQuery() +{ + Q_D(const ctkDICOMScheduler); + return d->MaximumPatientsQuery; +} + +//---------------------------------------------------------------------------- +ctkDICOMStorageListenerJob *ctkDICOMScheduler::listenerJob() +{ + Q_D(ctkDICOMScheduler); + QMutexLocker ml(&d->mMutex); + foreach(QSharedPointer job, d->JobsQueue) + { + QSharedPointer listenerJob = + qobject_cast>(job); + if (listenerJob) + { + return listenerJob.data(); + } + } + + return nullptr; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMScheduler::isStorageListenerActive() +{ + ctkDICOMStorageListenerJob* listenerJob = this->listenerJob(); + if (listenerJob && + listenerJob->status() == ctkAbstractJob::JobStatus::Running) + { + return true; + } + return false; +} + +//---------------------------------------------------------------------------- +QThreadPool *ctkDICOMScheduler::threadPool() const +{ + Q_D(const ctkDICOMScheduler); + return d->ThreadPool.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMScheduler::threadPoolShared() const +{ + Q_D(const ctkDICOMScheduler); + return d->ThreadPool; +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::onJobStarted() +{ + ctkDICOMJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("started")); + QString jobType = job->className(); + emit this->jobStarted(job->jobUID(), jobType); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::onJobCanceled() +{ + ctkDICOMJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("canceled")); + QString jobType = job->className(); + emit this->jobCanceled(job->jobUID(), jobType); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::onJobFailed() +{ + ctkDICOMJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("failed")); + + QString jobUID = job->jobUID(); + QString jobType = job->className(); + this->deleteWorker(jobUID); + this->deleteJob(jobUID); + + emit this->jobFailed(jobUID, jobType); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::onJobFinished() +{ + ctkDICOMJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("finished")); + + QString jobUID = job->jobUID(); + QString jobType = job->className(); + this->deleteWorker(jobUID); + this->deleteJob(jobUID); + + emit this->jobFinished(jobUID, jobType); +} + +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::onQueueJobsInThreadPool() +{ + Q_D(ctkDICOMScheduler); + + QList threadPriorityEnums; + threadPriorityEnums.append(QThread::Priority::HighestPriority); + threadPriorityEnums.append(QThread::Priority::HighPriority); + threadPriorityEnums.append(QThread::Priority::NormalPriority); + threadPriorityEnums.append(QThread::Priority::LowPriority); + threadPriorityEnums.append(QThread::Priority::LowestPriority); + + QMutexLocker ml(&d->mMutex); + foreach (QThread::Priority priority, threadPriorityEnums) + { + foreach(QSharedPointer job, d->JobsQueue) + { + if (job->priority() != priority) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Initialized) + { + continue; + } + + int numberOfRunningJobsWithSameType = d->getSameTypeJobsInThreadPoolQueueOrRunning(job); + if (numberOfRunningJobsWithSameType >= job->maximumConcurrentJobsPerType()) + { + continue; + } + + logger.debug(QString("ctkDICOMScheduler: creating worker for job %1 in thread %2") + .arg(job->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + + job->setStatus(ctkAbstractJob::JobStatus::Queued); + + QSharedPointer worker = + QSharedPointer(job->createWorker()); + worker->setScheduler(*this); + + d->Workers.insert(job->jobUID(), worker); + d->ThreadPool->start(worker.data(), job->priority()); + } + } +} diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h new file mode 100644 index 0000000000..8235730732 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -0,0 +1,227 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMScheduler_h +#define __ctkDICOMScheduler_h + +// Qt includes +#include +#include +#include + +// ctkCore includes +#include "ctkAbstractScheduler.h" + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMDatabase.h" + +class ctkDICOMJob; +class ctkDICOMIndexer; +class ctkDICOMSchedulerPrivate; +class ctkDICOMServer; +class ctkDICOMStorageListenerJob; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler +{ + Q_OBJECT + Q_PROPERTY(int maximumThreadCount READ maximumThreadCount WRITE setMaximumThreadCount); + Q_PROPERTY(int maximumNumberOfRetry READ maximumNumberOfRetry WRITE setMaximumNumberOfRetry); + Q_PROPERTY(int retryDelay READ retryDelay WRITE setRetryDelay); + Q_PROPERTY(int maximumPatientsQuery READ maximumPatientsQuery WRITE setMaximumPatientsQuery); + +public: + typedef ctkAbstractScheduler Superclass; + explicit ctkDICOMScheduler(QObject* parent = 0); + virtual ~ctkDICOMScheduler(); + + /// Query Patients applying filters on all servers. + /// The method spans a ctkDICOMQueryJob for each server. + Q_INVOKABLE void queryPatients(QThread::Priority priority = QThread::LowPriority); + + /// Query Studies applying filters on all servers. + /// The method spans a ctkDICOMQueryJob for each server. + Q_INVOKABLE void queryStudies(const QString& patientID, + QThread::Priority priority = QThread::LowPriority); + + /// Query Series applying filters on all servers. + /// The method spans a ctkDICOMQueryJob for each server. + Q_INVOKABLE void querySeries(const QString& patientID, + const QString& studyInstanceUID, + QThread::Priority priority = QThread::LowPriority); + + /// Query Instances applying filters on all servers. + /// The method spans a ctkDICOMQueryJob for each server. + Q_INVOKABLE void queryInstances(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + QThread::Priority priority = QThread::LowPriority); + + /// Retrieve Study. + /// The method spans a ctkDICOMRetrieveJob for each server. + Q_INVOKABLE void retrieveStudy(const QString& patientID, + const QString& studyInstanceUID, + QThread::Priority priority = QThread::LowPriority); + + /// Retrieve Series. + /// The method spans a ctkDICOMRetrieveJob for each server. + Q_INVOKABLE void retrieveSeries(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + QThread::Priority priority = QThread::LowPriority); + + /// Retrieve SOPInstance. + /// The method spans a ctkDICOMRetrieveJob for each server. + Q_INVOKABLE void retrieveSOPInstance(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString &SOPInstanceUID, + QThread::Priority priority = QThread::LowPriority); + + /// Start a storage listener + Q_INVOKABLE void startListener(const int port, + const QString &AETitle, + QThread::Priority priority = QThread::LowPriority); + + /// Insert results from a job + void insertJobResponseSet(QSharedPointer jobResponseSet, + QThread::Priority priority = QThread::HighPriority); + void insertJobResponseSets(QList> jobResponseSets, + QThread::Priority priority = QThread::HighPriority); + + + /// Return the Dicom Database. + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + /// Return Dicom Database as a shared pointer + /// (not Python-wrappable). + QSharedPointer dicomDatabaseShared() const; + /// Set the Dicom Database. + Q_INVOKABLE void setDicomDatabase(ctkDICOMDatabase& dicomDatabase); + /// Set the Dicom Database as a shared pointer + /// (not Python-wrappable). + void setDicomDatabase(QSharedPointer dicomDatabase); + + /// + /// Filters are keyword/value pairs as generated by + /// the ctkDICOMWidgets in a human readable (and editable) + /// format. The Query is responsible for converting these + /// into the appropriate dicom syntax for the C-Find + /// Currently supports the keys: Name, Study, Series, ID, Modalities, + /// StartDate and EndDate + /// Key DICOM Tag Type Example + /// ----------------------------------------------------------- + /// Name DCM_PatientName QString JOHNDOE + /// Study DCM_StudyDescription QString + /// Series DCM_SeriesDescription QString + /// ID DCM_PatientID QString + /// Modalities DCM_ModalitiesInStudy QStringList CT, MR, MN + /// StartDate DCM_StudyDate QString 20090101 + /// EndDate DCM_StudyDate QString 20091231 + /// No filter (empty) by default. + Q_INVOKABLE void setFilters(const QMap &filters); + Q_INVOKABLE QMap filters()const; + + /// Servers + Q_INVOKABLE int getNumberOfServers(); + Q_INVOKABLE int getNumberOfQueryRetrieveServers(); + Q_INVOKABLE int getNumberOfStorageServers(); + Q_INVOKABLE ctkDICOMServer* getNthServer(int id); + Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE void addServer(ctkDICOMServer& server); + void addServer(QSharedPointer server); + Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeNthServer(int id); + Q_INVOKABLE void removeAllServers(); + Q_INVOKABLE QString getServerNameFromIndex(int id); + Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + + /// Jobs managment + Q_INVOKABLE int numberOfJobs(); + Q_INVOKABLE int numberOfPersistentJobs(); + Q_INVOKABLE void addJob(ctkDICOMJob* job); + Q_INVOKABLE void deleteJob(QString jobUID); + Q_INVOKABLE void deleteWorker(QString jobUID); + QSharedPointer getJobSharedByUID(QString jobUID); + Q_INVOKABLE ctkDICOMJob* getJobByUID(QString jobUID); + Q_INVOKABLE void waitForFinish(); + Q_INVOKABLE void waitForDone(int msec); + Q_INVOKABLE void waitForFinishByUIDs(const QStringList& patientIDs = {}, + const QStringList& studyInstanceUIDs = {}, + const QStringList& seriesInstanceUIDs = {}, + const QStringList& sopInstanceUIDs = {}); + Q_INVOKABLE void stopAllJobs(bool stopPersistentJobs = false); + Q_INVOKABLE void stopJobsByUIDs(const QStringList& patientIDs = {}, + const QStringList& studyInstanceUIDs = {}, + const QStringList& seriesInstanceUIDs = {}, + const QStringList& sopInstanceUIDs = {}); + Q_INVOKABLE void raiseJobsPriorityForSeries(const QStringList& selectedSeriesInstanceUIDs, + QThread::Priority priority = QThread::HighestPriority); + + /// Maximum number of concurrent QThreads spawned by the threadPool in the Job pool + /// default: 20 + int maximumThreadCount() const; + void setMaximumThreadCount(const int& maximumThreadCount); + /// Maximum number of retries that the Job pool will try on each failed Job + /// default: 3 + int maximumNumberOfRetry() const; + void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + /// Retry delay in millisec + /// default: 100 msec + int retryDelay() const; + void setRetryDelay(const int& retryDelay); + /// maximum number of responses allowed in one query + /// when query is at Patient level. Default is 25. + void setMaximumPatientsQuery(const int maximumPatientsQuery); + int maximumPatientsQuery(); + + /// Return the listener Job. + Q_INVOKABLE ctkDICOMStorageListenerJob* listenerJob(); + Q_INVOKABLE bool isStorageListenerActive(); + + /// Return the threadPool. + Q_INVOKABLE QThreadPool* threadPool() const; + /// Return threadPool as a shared pointer + /// (not Python-wrappable). + QSharedPointer threadPoolShared() const; + +Q_SIGNALS: + void queueJobs(); + void progressJobDetail(QVariant data); + +public Q_SLOTS: + void onJobStarted(); + void onJobCanceled(); + void onJobFailed(); + void onJobFinished(); + void onQueueJobsInThreadPool(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMScheduler); + Q_DISABLE_COPY(ctkDICOMScheduler); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h new file mode 100644 index 0000000000..bcdcfc9af7 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -0,0 +1,71 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMQueryJobPrivate_h +#define __ctkDICOMQueryJobPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMScheduler.h" + +class ctkDICOMWorker; +class ctkDICOMJob; + +struct ThumbnailUID +{ + QString studyInstanceUID; + QString seriesInstanceUID; + QString SOPInstanceUID; +} ; + +//------------------------------------------------------------------------------ +class ctkDICOMSchedulerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMScheduler); + +protected: + ctkDICOMScheduler* const q_ptr; + +public: + ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj); + ~ctkDICOMSchedulerPrivate(); + + int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); + void insertJob(QSharedPointer job); + void removeJob(QString jobUID); + QString generateUniqueJobUID(); + ctkDICOMServer* getServerFromProxyServersByConnectionName(const QString&); + + QSharedPointer DicomDatabase; + QSharedPointer ThreadPool; + QList> Servers; + QMap> JobsQueue; + QMap> Workers; + QMap Filters; + QMutex mMutex; + int RetryDelay; + int MaximumNumberOfRetry; + int MaximumPatientsQuery; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp new file mode 100644 index 0000000000..3bff55dab9 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -0,0 +1,333 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMServer.h" +#include "ctkLogger.h" + +// Qt includes +#include + +static ctkLogger logger("org.commontk.dicom.DICOMServer"); + +//------------------------------------------------------------------------------ +class ctkDICOMServerPrivate: public QObject +{ + Q_DECLARE_PUBLIC(ctkDICOMServer); + +protected: + ctkDICOMServer* const q_ptr; + +public: + ctkDICOMServerPrivate(ctkDICOMServer& obj); + ~ctkDICOMServerPrivate(); + + QString ConnectionName; + bool QueryRetrieveEnabled; + bool StorageEnabled; + QString CallingAETitle; + QString CalledAETitle; + QString Host; + int Port; + ctkDICOMServer::RetrieveProtocol RetrieveProtocol; + bool KeepAssociationOpen; + QString MoveDestinationAETitle; + int ConnectionTimeout; + QSharedPointer ProxyServer; +}; + +//------------------------------------------------------------------------------ +// ctkDICOMServerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMServerPrivate::ctkDICOMServerPrivate(ctkDICOMServer& obj) + : q_ptr(&obj) +{ + this->ConnectionName = ""; + this->CallingAETitle = ""; + this->CalledAETitle = ""; + this->Host = ""; + this->MoveDestinationAETitle = ""; + this->QueryRetrieveEnabled = true; + this->StorageEnabled = true; + this->KeepAssociationOpen = false; + this->ConnectionTimeout = 10; + this->Port = 80; + this->RetrieveProtocol = ctkDICOMServer::RetrieveProtocol::CGET; + this->ProxyServer = nullptr; +} + +//------------------------------------------------------------------------------ +ctkDICOMServerPrivate::~ctkDICOMServerPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMServer methods + +//------------------------------------------------------------------------------ +ctkDICOMServer::ctkDICOMServer(QObject* parent) + : QObject(parent), + d_ptr(new ctkDICOMServerPrivate(*this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMServer::~ctkDICOMServer() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setConnectionName(const QString& connectionName) +{ + Q_D(ctkDICOMServer); + d->ConnectionName = connectionName; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMServer::connectionName() const +{ + Q_D(const ctkDICOMServer); + return d->ConnectionName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setQueryRetrieveEnabled(bool queryRetrieveEnabled) +{ + Q_D(ctkDICOMServer); + d->QueryRetrieveEnabled = queryRetrieveEnabled; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMServer::queryRetrieveEnabled() const +{ + Q_D(const ctkDICOMServer); + return d->QueryRetrieveEnabled; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setStorageEnabled(bool storageEnabled) +{ + Q_D(ctkDICOMServer); + d->StorageEnabled = storageEnabled; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMServer::storageEnabled() const +{ + Q_D(const ctkDICOMServer); + return d->StorageEnabled; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setCallingAETitle( const QString& callingAETitle ) +{ + Q_D(ctkDICOMServer); + d->CallingAETitle = callingAETitle; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMServer::callingAETitle() const +{ + Q_D(const ctkDICOMServer); + return d->CallingAETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setCalledAETitle( const QString& calledAETitle ) +{ + Q_D(ctkDICOMServer); + d->CalledAETitle = calledAETitle; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMServer::calledAETitle()const +{ + Q_D(const ctkDICOMServer); + return d->CalledAETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setHost(const QString& host) +{ + Q_D(ctkDICOMServer); + d->Host = host; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMServer::host() const +{ + Q_D(const ctkDICOMServer); + return d->Host; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setPort(int port) +{ + Q_D(ctkDICOMServer); + d->Port = port; +} + +//------------------------------------------------------------------------------ +int ctkDICOMServer::port() const +{ + Q_D(const ctkDICOMServer); + return d->Port; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setRetrieveProtocol(RetrieveProtocol protocol) +{ + Q_D(ctkDICOMServer); + d->RetrieveProtocol = protocol; +} + +//------------------------------------------------------------------------------ +ctkDICOMServer::RetrieveProtocol ctkDICOMServer::retrieveProtocol() const +{ + Q_D(const ctkDICOMServer); + return d->RetrieveProtocol; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setRetrieveProtocolAsString(QString protocolString) +{ + Q_D(ctkDICOMServer); + + if (protocolString == "CGET") + { + d->RetrieveProtocol = RetrieveProtocol::CGET; + } + else if (protocolString == "CMOVE") + { + d->RetrieveProtocol = RetrieveProtocol::CMOVE; + } + /*else if (protocolString == "WADO") To Do + { + d->RetrieveProtocol = RetrieveProtocol::WADO; + }*/ +} + +//------------------------------------------------------------------------------ +QString ctkDICOMServer::retrieveProtocolAsString() const +{ + Q_D(const ctkDICOMServer); + + QString protocolString = ""; + switch (d->RetrieveProtocol) + { + case RetrieveProtocol::CGET: + protocolString = "CGET"; + break; + case RetrieveProtocol::CMOVE: + protocolString = "CMOVE"; + break; + /*case RetrieveProtocol::WADO: To Do + protocolString = "WADO"; + break; */ + default: + break; + } + + return protocolString; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setMoveDestinationAETitle(const QString& moveDestinationAETitle) +{ + Q_D(ctkDICOMServer); + if (moveDestinationAETitle != d->MoveDestinationAETitle) + { + d->MoveDestinationAETitle = moveDestinationAETitle; + } +} +//------------------------------------------------------------------------------ +QString ctkDICOMServer::moveDestinationAETitle()const +{ + Q_D(const ctkDICOMServer); + return d->MoveDestinationAETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMServer::setKeepAssociationOpen(const bool keepOpen) +{ + Q_D(ctkDICOMServer); + d->KeepAssociationOpen = keepOpen; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMServer::keepAssociationOpen() +{ + Q_D(const ctkDICOMServer); + return d->KeepAssociationOpen; +} + +//----------------------------------------------------------------------------- +void ctkDICOMServer::setConnectionTimeout(const int timeout) +{ + Q_D(ctkDICOMServer); + d->ConnectionTimeout = timeout; +} + +//----------------------------------------------------------------------------- +int ctkDICOMServer::connectionTimeout() +{ + Q_D(const ctkDICOMServer); + return d->ConnectionTimeout; +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMServer::proxyServer() const +{ + Q_D(const ctkDICOMServer); + return d->ProxyServer.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMServer::proxyServerShared() const +{ + Q_D(const ctkDICOMServer); + return d->ProxyServer; +} + +//---------------------------------------------------------------------------- +void ctkDICOMServer::setProxyServer(ctkDICOMServer &proxyServer) +{ + Q_D(ctkDICOMServer); + d->ProxyServer = QSharedPointer(&proxyServer, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServer::setProxyServer(QSharedPointer proxyServer) +{ + Q_D(ctkDICOMServer); + d->ProxyServer = proxyServer; +} diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h new file mode 100644 index 0000000000..19fa552a65 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -0,0 +1,121 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMServer_h +#define __ctkDICOMServer_h + +// Qt includes +#include + +#include "ctkDICOMCoreExport.h" + +class ctkDICOMServerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject +{ + Q_OBJECT + Q_ENUMS(RetrieveProtocol) + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName); + Q_PROPERTY(bool queryRetrieveEnabled READ queryRetrieveEnabled WRITE setQueryRetrieveEnabled); + Q_PROPERTY(bool storageEnabled READ storageEnabled WRITE setStorageEnabled); + Q_PROPERTY(QString callingAETitle READ callingAETitle WRITE setCallingAETitle); + Q_PROPERTY(QString calledAETitle READ calledAETitle WRITE setCalledAETitle); + Q_PROPERTY(QString host READ host WRITE setHost); + Q_PROPERTY(int port READ port WRITE setPort); + Q_PROPERTY(RetrieveProtocol retrieveProtocol READ retrieveProtocol WRITE setRetrieveProtocol); + Q_PROPERTY(QString moveDestinationAETitle READ moveDestinationAETitle WRITE setMoveDestinationAETitle); + Q_PROPERTY(bool keepAssociationOpen READ keepAssociationOpen WRITE setKeepAssociationOpen); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + +public: + explicit ctkDICOMServer(QObject* parent = 0); + virtual ~ctkDICOMServer(); + + /// Set methods for connectivity + void setConnectionName(const QString& connectionName); + QString connectionName() const; + /// Query/Retrieve operations + /// true as default + void setQueryRetrieveEnabled(bool queryRetrieveEnabled); + bool queryRetrieveEnabled() const; + /// Storage operations + /// true as default + void setStorageEnabled(bool storageEnabled); + bool storageEnabled() const; + /// CTK_AE - the AE string by which the peer host might + /// recognize your request + void setCallingAETitle(const QString& callingAETitle); + QString callingAETitle() const; + /// CTK_AE - the AE of the service of peer host that you are calling + /// which tells the host what you are requesting + void setCalledAETitle(const QString& calledAETitle); + QString calledAETitle() const; + /// peer hostname being connected to + void setHost(const QString& host); + QString host() const; + /// [0, 65365] port on peer host + /// 80 as default + void setPort(int port); + int port() const; + + /// Protocol for retrieval of query results. + /// CGET by default + enum RetrieveProtocol + { + CGET = 0, + CMOVE + // WADO // To Do + }; + void setRetrieveProtocol(RetrieveProtocol protocol); + RetrieveProtocol retrieveProtocol() const; + Q_INVOKABLE void setRetrieveProtocolAsString(QString protocolString); + Q_INVOKABLE QString retrieveProtocolAsString() const; + /// Typically CTK_STORE or similar - needs to be something that the + /// peer host knows about and is able to move data into + /// Only used when calling moveSeries or moveStudy + void setMoveDestinationAETitle(const QString& moveDestinationAETitle); + QString moveDestinationAETitle() const; + /// prefer to keep using the existing association to peer host when doing + /// multiple requests (default true) + void setKeepAssociationOpen(const bool keepOpen); + bool keepAssociationOpen(); + /// connection timeout in seconds, default 10 s. + void setConnectionTimeout(const int timeout); + int connectionTimeout(); + /// proxy server + Q_INVOKABLE ctkDICOMServer* proxyServer() const; + QSharedPointer proxyServerShared() const; + Q_INVOKABLE void setProxyServer(ctkDICOMServer& proxyServer); + void setProxyServer(QSharedPointer proxyServer); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMServer); + Q_DISABLE_COPY(ctkDICOMServer); +}; + + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp new file mode 100644 index 0000000000..e5c8eedb13 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -0,0 +1,451 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMStorageListener.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkLogger.h" + +// DCMTK includes +#include "dcmtk/dcmnet/dstorscp.h" /* for DcmStorageSCP */ + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListener" ); + +//------------------------------------------------------------------------------ +// A customized implementation so that Qt signals can be emitted +// when query results are obtained +class ctkDICOMStorageListenerSCUPrivate : public DcmStorageSCP +{ +public: + ctkDICOMStorageListener *listener; + ctkDICOMStorageListenerSCUPrivate() + { + this->listener = 0; + }; + ~ctkDICOMStorageListenerSCUPrivate() {}; + + virtual OFCondition acceptAssociations() + { + return DcmSCP::acceptAssociations(); + } + + virtual OFBool stopAfterCurrentAssociation() + { + if (!this->listener || this->listener->wasCanceled()) + { + return OFTrue; + } + + return OFFalse; + } + + virtual OFBool stopAfterConnectionTimeout() + { + if (!this->listener || this->listener->wasCanceled()) + { + return OFTrue; + } + + return OFFalse; + } + + virtual OFCondition handleIncomingCommand(T_DIMSE_Message *incomingMsg, + const DcmPresentationContextInfo &presInfo) + { + OFCondition status = EC_IllegalParameter; + if (incomingMsg != NULL && !this->listener->wasCanceled()) + { + // check whether we've received a supported command + if (incomingMsg->CommandField == DIMSE_C_ECHO_RQ) + { + // handle incoming C-ECHO request + status = handleECHORequest(incomingMsg->msg.CEchoRQ, presInfo.presentationContextID); + } + else if (incomingMsg->CommandField == DIMSE_C_STORE_RQ) + { + // handle incoming C-STORE request + T_DIMSE_C_StoreRQ &storeReq = incomingMsg->msg.CStoreRQ; + Uint16 rspStatusCode = STATUS_STORE_Error_CannotUnderstand; + DcmDataset *reqDataset = new DcmDataset; + // receive dataset in memory + status = receiveSTORERequest(storeReq, presInfo.presentationContextID, reqDataset); + if (status.good()) + { + rspStatusCode = STATUS_Success; + } + + OFString instanceUID; + reqDataset->findAndGetOFString(DCM_SOPInstanceUID, instanceUID); + OFString seriesUID; + reqDataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUID); + OFString studyUID; + reqDataset->findAndGetOFString(DCM_StudyInstanceUID, studyUID); + emit this->listener->progress( + ctkDICOMStorageListener::tr("Got STORE request for %1").arg(instanceUID.c_str()) + ); + emit this->listener->progress(0); + if (!this->listener->jobUID().isEmpty() && !this->listener->wasCanceled()) + { + QSharedPointer jobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); + jobResponseSet->setStudyInstanceUID(studyUID.c_str()); + jobResponseSet->setSeriesInstanceUID(seriesUID.c_str()); + jobResponseSet->setSOPInstanceUID(instanceUID.c_str()); + jobResponseSet->setConnectionName(this->listener->AETitle()); + jobResponseSet->setDataset(reqDataset); + jobResponseSet->setJobUID(this->listener->jobUID()); + jobResponseSet->setCopyFile(true); + + this->listener->addJobResponseSet(jobResponseSet); + + emit this->listener->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + } + // send C-STORE response (with DIMSE status code) + if (status.good()) + { + status = sendSTOREResponse(presInfo.presentationContextID, storeReq, rspStatusCode); + } + else if (status == DIMSE_OUTOFRESOURCES) + { + // do not overwrite the previous error status + sendSTOREResponse(presInfo.presentationContextID, storeReq, STATUS_STORE_Refused_OutOfResources); + } + } + else + { + // unsupported command + OFString tempStr; + DCMNET_ERROR("cannot handle this kind of DIMSE command (0x" + << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4) + << OFstatic_cast(unsigned int, incomingMsg->CommandField) + << "), we are a Storage SCP only"); + DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, *incomingMsg, DIMSE_INCOMING)); + status = DIMSE_BADCOMMANDTYPE; + } + } + return status; + } +}; + +//------------------------------------------------------------------------------ +class ctkDICOMStorageListenerPrivate +{ +public: + ctkDICOMStorageListenerPrivate(); + ~ctkDICOMStorageListenerPrivate(); + + QString findFile(const QStringList& nameFilters, const QString& subDir)const; + QString defaultConfigFile() const; + + QString ConnectionName; + QString AETitle; + int Port; + QString JobUID; + QList> JobResponseSets; + + ctkDICOMStorageListenerSCUPrivate SCU; + bool Canceled; +}; + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerPrivate::ctkDICOMStorageListenerPrivate() +{ + this->Port = 11112; + this->AETitle = "CTKSTORE"; + this->Canceled = false; + + this->SCU.setConnectionBlockingMode(DUL_NOBLOCK); + this->SCU.setACSETimeout(1); + this->SCU.setConnectionTimeout(1); + this->SCU.setRespondWithCalledAETitle(false); + this->SCU.setHostLookupEnabled(true); + this->SCU.setVerbosePCMode(false); +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerPrivate::~ctkDICOMStorageListenerPrivate() +{ +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStorageListenerPrivate::defaultConfigFile() const +{ + QString data; + QString fileName(":/dicom/storescp.cfg"); + + QFile readFile(fileName); + if(readFile.open(QIODevice::ReadOnly)) + { + data = readFile.readAll(); + } + else + { + logger.error("Failed to find listener configuration file"); + return ""; + } + + readFile.close(); + + QString tmpDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + QString configFile = tmpDir + "/storescp.cfg"; + QFile writeFile(configFile); + + if(writeFile.open(QFile::WriteOnly | QFile::Text)) + { + QTextStream stream(&writeFile); + stream << data; + } + else + { + logger.error("Failed to find listener configuration file"); + return ""; + } + writeFile.close(); + + return configFile; +} + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListener methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListener::ctkDICOMStorageListener(QObject* parentObject) + : QObject(parentObject) + , d_ptr(new ctkDICOMStorageListenerPrivate) +{ + Q_D(ctkDICOMStorageListener); + d->SCU.listener = this; // give the dcmtk level access to this for emitting signals + + this->setDCMTKLogLevel(logger.logLevel()); +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListener::~ctkDICOMStorageListener() +{ + Q_D(ctkDICOMStorageListener); + d->JobResponseSets.clear(); +} + +//----------------------------------------------------------------------------- +void ctkDICOMStorageListener::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) +{ + OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; + if (level == ctkErrorLogLevel::LogLevel::Fatal) + { + dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Critical || + level == ctkErrorLogLevel::LogLevel::Error) + { + dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Warning) + { + dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Info) + { + dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Debug) + { + dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Trace || + level == ctkErrorLogLevel::LogLevel::Status) + { + dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; + } + + OFLog::configure(dcmtkLogLevel); +} + +//----------------------------------------------------------------------------- +ctkErrorLogLevel::LogLevel ctkDICOMStorageListener::DCMTKLogLevel() const +{ + return logger.logLevel(); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStorageListener::listen() +{ + Q_D(ctkDICOMStorageListener); + if (!this->initializeSCU()) + { + return false; + } + + OFCondition status = d->SCU.listen(); + if (status.bad() || d->Canceled) + { + logger.error(QString("SCP stopped, it was listening on port %1 : %2 ") + .arg(QString::number(d->Port)).arg(status.text())); + return false; + } + return true; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStorageListener::wasCanceled() +{ + Q_D(const ctkDICOMStorageListener); + return d->Canceled; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::cancel() +{ + Q_D(ctkDICOMStorageListener); + d->Canceled = true; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStorageListener::initializeSCU() +{ + Q_D(ctkDICOMStorageListener); + d->SCU.setPort(this->port()); + d->SCU.setAETitle(OFString(this->AETitle().toStdString().c_str())); + + /* load association negotiation profile from configuration file (if specified) */ + OFCondition status = d->SCU.loadAssociationConfiguration( + OFString(d->defaultConfigFile().toStdString().c_str()), "alldicom"); + if (status.bad()) + { + logger.error(QString("Cannot load association configuration: %1").arg(status.text())); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::setAETitle(const QString& AETitle) +{ + Q_D(ctkDICOMStorageListener); + d->AETitle = AETitle; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStorageListener::AETitle() const +{ + Q_D(const ctkDICOMStorageListener); + return d->AETitle; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::setPort (int port) +{ + Q_D(ctkDICOMStorageListener); + d->Port = port; +} + +//------------------------------------------------------------------------------ +int ctkDICOMStorageListener::port()const +{ + Q_D(const ctkDICOMStorageListener); + return d->Port; +} + +//----------------------------------------------------------------------------- +void ctkDICOMStorageListener::setConnectionTimeout(const int timeout) +{ + Q_D(ctkDICOMStorageListener); + d->SCU.setACSETimeout(timeout); + d->SCU.setConnectionTimeout(timeout); +} + +//----------------------------------------------------------------------------- +int ctkDICOMStorageListener::connectionTimeout() +{ + Q_D(const ctkDICOMStorageListener); + return d->SCU.getConnectionTimeout(); +} + +//------------------------------------------------------------------------------ +QList ctkDICOMStorageListener::jobResponseSets() const +{ + Q_D(const ctkDICOMStorageListener); + QList jobResponseSets; + foreach(QSharedPointer jobResponseSet, d->JobResponseSets) + { + jobResponseSets.append(jobResponseSet.data()); + } + + return jobResponseSets; +} + +//------------------------------------------------------------------------------ +QList> ctkDICOMStorageListener::jobResponseSetsShared() const +{ + Q_D(const ctkDICOMStorageListener); + return d->JobResponseSets; +} + +//------------------------------------------------------------------------------ +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::addJobResponseSet(ctkDICOMJobResponseSet &jobResponseSet) +{ + this->addJobResponseSet(QSharedPointer(&jobResponseSet, skipDelete)); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::addJobResponseSet(QSharedPointer jobResponseSet) +{ + Q_D(ctkDICOMStorageListener); + d->JobResponseSets.append(jobResponseSet); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::removeJobResponseSet(QSharedPointer jobResponseSet) +{ + Q_D(ctkDICOMStorageListener); + d->JobResponseSets.removeOne(jobResponseSet); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListener::setJobUID(const QString &jobUID) +{ + Q_D(ctkDICOMStorageListener); + d->JobUID = jobUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStorageListener::jobUID() const +{ + Q_D(const ctkDICOMStorageListener); + return d->JobUID; +} diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h new file mode 100644 index 0000000000..e941fc8d15 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -0,0 +1,117 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkDICOMStorageListener_h +#define __ctkDICOMStorageListener_h + +// Qt includes +#include +#include +#include +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkErrorLogLevel.h" + +class ctkDICOMStorageListenerPrivate; +class ctkDICOMJobResponseSet; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString AETitle READ AETitle WRITE setAETitle); + Q_PROPERTY(int port READ port WRITE setPort); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + +public: + explicit ctkDICOMStorageListener(QObject* parent = 0); + virtual ~ctkDICOMStorageListener(); + + /// Storage AE title + /// "CTKSTORE" by default + void setAETitle(const QString& AETitle); + QString AETitle() const; + /// Storage port + /// 11112 by default + void setPort(int port); + int port() const; + /// connection timeout + /// 1 sec by default + void setConnectionTimeout(const int timeout); + int connectionTimeout(); + + /// Access the list of datasets from the last operation. + Q_INVOKABLE QList jobResponseSets() const; + QList> jobResponseSetsShared() const; + Q_INVOKABLE void addJobResponseSet(ctkDICOMJobResponseSet& jobResponseSet); + void addJobResponseSet(QSharedPointer jobResponseSet); + void removeJobResponseSet(QSharedPointer jobResponseSet); + Q_INVOKABLE void setJobUID(const QString& jobUID); + Q_INVOKABLE QString jobUID() const; + + /// Log level for dcmtk. Default: Error. + Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); + Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; + + /// Start listen connection. + bool listen(); + + /// operation is canceled? + Q_INVOKABLE bool wasCanceled(); + +Q_SIGNALS: + /// Signal is emitted inside the listener() function. It ranges from 0 to 100. + /// In case of an error, you are assured that the progress value 100 is fired + void progress(int progress); + /// Signal is emitted inside the listener() function. It sends the different step + /// the function is at. + void progress(const QString& message); + /// Signal is emitted inside the listener() function. It sends + /// detailed feedback for debugging + void debug(const QString& message); + /// Signal is emitted inside the listener() function. It send any error messages + void error(const QString& message); + /// Signal is emitted inside the listener() function when finished with value + /// true for success or false for error + void done(const bool& error); + /// Signal is emitted inside the listener() function when a frame has been fetched + void progressJobDetail(QVariant); + +public Q_SLOTS: + void cancel(); + +protected: + bool initializeSCU(); + + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMStorageListener); + Q_DISABLE_COPY(ctkDICOMStorageListener); + + friend class ctkDICOMStorageListenerSCUPrivate; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp new file mode 100644 index 0000000000..de3753e2a5 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -0,0 +1,145 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// ctkDICOMCore includes +#include "ctkDICOMStorageListenerJob_p.h" +#include "ctkDICOMStorageListenerWorker.h" +#include "ctkLogger.h" + +static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListenerJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerJobPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerJobPrivate::ctkDICOMStorageListenerJobPrivate(ctkDICOMStorageListenerJob* object) + : q_ptr(object) +{ + this->AETitle = "CTKSTORE"; + this->Port = 11112; + this->ConnectionTimeout = 1; +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerJobPrivate::~ctkDICOMStorageListenerJobPrivate() +{ +} + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerJob methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerJob::ctkDICOMStorageListenerJob() + : d_ptr(new ctkDICOMStorageListenerJobPrivate(this)) +{ + this->Persistent = true; +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerJob::ctkDICOMStorageListenerJob(ctkDICOMStorageListenerJobPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerJob::~ctkDICOMStorageListenerJob() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerJob::setPort(const int port) +{ + Q_D(ctkDICOMStorageListenerJob); + d->Port = port; +} + +//---------------------------------------------------------------------------- +int ctkDICOMStorageListenerJob::port() const +{ + Q_D(const ctkDICOMStorageListenerJob); + return d->Port; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerJob::setAETitle(const QString &AETitle) +{ + Q_D(ctkDICOMStorageListenerJob); + d->AETitle = AETitle; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMStorageListenerJob::AETitle() const +{ + Q_D(const ctkDICOMStorageListenerJob); + return d->AETitle; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerJob::setConnectionTimeout(const int timeout) +{ + Q_D(ctkDICOMStorageListenerJob); + d->ConnectionTimeout = timeout; +} + +//---------------------------------------------------------------------------- +int ctkDICOMStorageListenerJob::connectionTimeout() const +{ + Q_D(const ctkDICOMStorageListenerJob); + return d->ConnectionTimeout; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMStorageListenerJob::loggerReport(const QString &status) const +{ + return QString("ctkDICOMStorageListenerJob: listener job %1.\n" + "JobUID: %2") + .arg(status) + .arg(this->jobUID()); +} +//------------------------------------------------------------------------------ +ctkDICOMJob* ctkDICOMStorageListenerJob::generateCopy() const +{ + ctkDICOMStorageListenerJob* newListenerJob = + new ctkDICOMStorageListenerJob; + newListenerJob->setAETitle(this->AETitle()); + newListenerJob->setPort(this->port()); + newListenerJob->setConnectionTimeout(this->connectionTimeout()); + newListenerJob->setMaximumNumberOfRetry(this->maximumNumberOfRetry()); + newListenerJob->setRetryDelay(this->retryDelay()); + newListenerJob->setRetryCounter(this->retryCounter()); + newListenerJob->setIsPersistent(this->isPersistent()); + newListenerJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); + newListenerJob->setPriority(this->priority()); + + + return newListenerJob; +} + +//------------------------------------------------------------------------------ +ctkDICOMWorker *ctkDICOMStorageListenerJob::createWorker() +{ + ctkDICOMStorageListenerWorker* worker = + new ctkDICOMStorageListenerWorker; + worker->setJob(*this); + return worker; +} diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h new file mode 100644 index 0000000000..f572161219 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -0,0 +1,86 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMStorageListenerJob_h +#define __ctkDICOMStorageListenerJob_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMJob.h" + +class ctkDICOMStorageListenerJobPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob +{ + Q_OBJECT + Q_PROPERTY(int port READ port WRITE setPort); + Q_PROPERTY(QString AETitle READ AETitle WRITE setAETitle); + Q_PROPERTY(int connectionTimeout READ connectionTimeout WRITE setConnectionTimeout); + +public: + typedef ctkDICOMJob Superclass; + explicit ctkDICOMStorageListenerJob(); + virtual ~ctkDICOMStorageListenerJob(); + + /// Port, default: 11112 + void setPort(const int port); + int port() const; + + /// AETitle, default: CTKSTORE + void setAETitle(const QString& AETitle); + QString AETitle() const; + + /// Connection timeout, default 1 sec. + void setConnectionTimeout(const int timeout); + int connectionTimeout() const; + + /// Logger report string formatting for specific task + Q_INVOKABLE QString loggerReport(const QString& status) const; + + /// Create a copy of the object + Q_INVOKABLE ctkDICOMJob* generateCopy() const; + + /// Generate worker for job + Q_INVOKABLE ctkDICOMWorker* createWorker(); + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMStorageListenerJob(ctkDICOMStorageListenerJobPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMStorageListenerJob); + Q_DISABLE_COPY(ctkDICOMStorageListenerJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h new file mode 100644 index 0000000000..4e4513fa37 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h @@ -0,0 +1,48 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMStorageListenerJobPrivate_h +#define __ctkDICOMStorageListenerJobPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMStorageListenerJob.h" + +//------------------------------------------------------------------------------ +class ctkDICOMStorageListenerJobPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMStorageListenerJob) + +protected: + ctkDICOMStorageListenerJob* const q_ptr; + +public: + ctkDICOMStorageListenerJobPrivate(ctkDICOMStorageListenerJob* object); + ~ctkDICOMStorageListenerJobPrivate(); + + QString AETitle; + int Port; + int ConnectionTimeout; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp new file mode 100644 index 0000000000..3198b345dc --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -0,0 +1,227 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMStorageListenerWorker_p.h" +#include "ctkDICOMStorageListenerJob.h" +#include "ctkLogger.h" + +static ctkLogger logger ("org.commontk.dicom.ctkDICOMStorageListenerWorker"); + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerWorkerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerWorkerPrivate::ctkDICOMStorageListenerWorkerPrivate(ctkDICOMStorageListenerWorker* object) + : q_ptr(object) +{ + this->StorageListener = QSharedPointer(new ctkDICOMStorageListener); +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerWorkerPrivate::~ctkDICOMStorageListenerWorkerPrivate() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListenerWorkerPrivate::setStorageListenerParameters() +{ + Q_Q(ctkDICOMStorageListenerWorker); + + QSharedPointer storageListenerJob = + qobject_cast>(q->Job); + if (!storageListenerJob) + { + return; + } + + this->StorageListener->setAETitle(storageListenerJob->AETitle()); + this->StorageListener->setPort(storageListenerJob->port()); + this->StorageListener->setConnectionTimeout(storageListenerJob->connectionTimeout()); + this->StorageListener->setJobUID(storageListenerJob->jobUID()); + + QObject::connect(this->StorageListener.data(), SIGNAL(progressJobDetail(QVariant)), + storageListenerJob.data(), SIGNAL(progressJobDetail(QVariant)), + Qt::DirectConnection); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStorageListenerWorkerPrivate::init() +{ + Q_Q(ctkDICOMStorageListenerWorker); + // To Do: this insert should happen in batch of 10 frames (configurable), + // instead of every 1 sec. + // This would avoid memory usage spikes when requesting a series or study with a lot of frames. + // i.e. the slot should be connected to progressJobDetail from this->StorageListener. + // The slot should have a counter. When the counter > batchLimit -> insert + // NOTE: the memory release should happen as soon as we insert the response. + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), q, SLOT(onInsertJobDetail())); + timer->start(1000); +} + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerWorker methods + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerWorker::ctkDICOMStorageListenerWorker() + : d_ptr(new ctkDICOMStorageListenerWorkerPrivate(this)) +{ + Q_D(ctkDICOMStorageListenerWorker); + d->init(); +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerWorker::ctkDICOMStorageListenerWorker(ctkDICOMStorageListenerWorkerPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMStorageListenerWorker::~ctkDICOMStorageListenerWorker() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerWorker::cancel() +{ + Q_D(const ctkDICOMStorageListenerWorker); + d->StorageListener->cancel(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerWorker::run() +{ + Q_D(const ctkDICOMStorageListenerWorker); + QSharedPointer storageListenerJob = + qobject_cast>(this->Job); + if (!storageListenerJob) + { + return; + } + + QSharedPointer scheduler = + qobject_cast>(this->Scheduler); + if (!scheduler) + { + emit storageListenerJob->canceled(); + this->onJobCanceled(); + storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + if (storageListenerJob->status() == ctkAbstractJob::JobStatus::Stopped) + { + emit storageListenerJob->canceled(); + this->onJobCanceled(); + storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Running); + emit storageListenerJob->started(); + + logger.debug("ctkDICOMStorageListenerWorker : running job on thread id " + + QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + + if (!d->StorageListener->listen()) + { + emit storageListenerJob->canceled(); + this->onJobCanceled(); + storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Finished); + return; + } + + storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Finished); + emit storageListenerJob->finished(); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerWorker::setJob(ctkAbstractJob &job) +{ + this->setJob(QSharedPointer(&job, skipDelete)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerWorker::setJob(QSharedPointer job) +{ + Q_D(ctkDICOMStorageListenerWorker); + + QSharedPointer storageListenerJob = + qobject_cast>(job); + if (!storageListenerJob) + { + return; + } + + Superclass::setJob(job); + d->setStorageListenerParameters(); +} + +//---------------------------------------------------------------------------- +ctkDICOMStorageListener *ctkDICOMStorageListenerWorker::storageListener() const +{ + Q_D(const ctkDICOMStorageListenerWorker); + return d->StorageListener.data(); +} + +//------------------------------------------------------------------------------ +QSharedPointer ctkDICOMStorageListenerWorker::storageListenerShared() const +{ + Q_D(const ctkDICOMStorageListenerWorker); + return d->StorageListener; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStorageListenerWorker::onInsertJobDetail() +{ + Q_D(ctkDICOMStorageListenerWorker); + + QSharedPointer scheduler = + qobject_cast>(this->Scheduler); + if (!scheduler) + { + return; + } + + QList> jobResponseSets = + d->StorageListener->jobResponseSetsShared(); + scheduler->insertJobResponseSets(jobResponseSets); + foreach (QSharedPointer jobResponseSet, jobResponseSets) + { + d->StorageListener->removeJobResponseSet(jobResponseSet); + } +} diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h new file mode 100644 index 0000000000..f79d718136 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -0,0 +1,81 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMStorageListenerWorker_h +#define __ctkDICOMStorageListenerWorker_h + +// Qt includes +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMWorker.h" + +class ctkDICOMStorageListener; +class ctkDICOMStorageListenerWorkerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkDICOMWorker +{ + Q_OBJECT + +public: + typedef ctkDICOMWorker Superclass; + explicit ctkDICOMStorageListenerWorker(); + virtual ~ctkDICOMStorageListenerWorker(); + + /// Execute worker + void run(); + + /// Cancel worker + void cancel(); + + /// Job + Q_INVOKABLE void setJob(ctkAbstractJob& job); + void setJob(QSharedPointer job); + + /// Retriever + QSharedPointer storageListenerShared() const; + Q_INVOKABLE ctkDICOMStorageListener* storageListener() const; + +public slots: + void onInsertJobDetail(); + +protected: + QScopedPointer d_ptr; + + /// Constructor allowing derived class to specify a specialized pimpl. + /// + /// \note You are responsible to call init() in the constructor of + /// derived class. Doing so ensures the derived class is fully + /// instantiated in case virtual method are called within init() itself. + ctkDICOMStorageListenerWorker(ctkDICOMStorageListenerWorkerPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMStorageListenerWorker); + Q_DISABLE_COPY(ctkDICOMStorageListenerWorker); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h new file mode 100644 index 0000000000..6dea97c5e9 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h @@ -0,0 +1,50 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMStorageListenerWorkerPrivate_h +#define __ctkDICOMStorageListenerWorkerPrivate_h + +// ctkDICOMCore includes +#include "ctkDICOMStorageListener.h" +#include "ctkDICOMStorageListenerWorker.h" + +//------------------------------------------------------------------------------ +class ctkDICOMStorageListenerWorkerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMStorageListenerWorker) + +protected: + ctkDICOMStorageListenerWorker* const q_ptr; + +public: + ctkDICOMStorageListenerWorkerPrivate(ctkDICOMStorageListenerWorker* object); + ~ctkDICOMStorageListenerWorkerPrivate(); + + void init(); + void setStorageListenerParameters(); + + QSharedPointer StorageListener; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp new file mode 100644 index 0000000000..0272f94048 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMWorker.cpp @@ -0,0 +1,91 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#include "ctkDICOMJob.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMWorker.h" + +// -------------------------------------------------------------------------- +ctkDICOMWorker::ctkDICOMWorker() +{ +} + +//---------------------------------------------------------------------------- +ctkDICOMWorker::~ctkDICOMWorker() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMWorker::startNextJob() +{ + QSharedPointer scheduler = + qobject_cast>(this->Scheduler); + if (!scheduler) + { + return; + } + + QSharedPointer job = + qobject_cast>(this->Job); + if (!job) + { + return; + } + + ctkDICOMJob* newJob = job->generateCopy(); + newJob->setRetryCounter(newJob->retryCounter() + 1); + scheduler->addJob(newJob); +} + +//---------------------------------------------------------------------------- +void ctkDICOMWorker::onJobCanceled() +{ + QSharedPointer job = + qobject_cast>(this->Job); + if (!job) + { + return; + } + + if (job->retryCounter() < job->maximumNumberOfRetry() && + job->status() != ctkAbstractJob::JobStatus::Stopped) + { + QTimer timer; + timer.setSingleShot(true); + QEventLoop loop; + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(job->retryDelay()); + + this->startNextJob(); + + emit job->finished(); + } + else if (job->status() != ctkAbstractJob::JobStatus::Stopped) + { + emit job->failed(); + } + else + { + emit job->finished(); + } +} diff --git a/Libs/DICOM/Core/ctkDICOMWorker.h b/Libs/DICOM/Core/ctkDICOMWorker.h new file mode 100644 index 0000000000..ddc491cf27 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMWorker.h @@ -0,0 +1,63 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMWorker_h +#define __ctkDICOMWorker_h + +// Qt includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkAbstractWorker.h" + +class ctkDICOMJob; +class ctkDICOMScheduler; + +//------------------------------------------------------------------------------ +/// \ingroup Core +class CTK_DICOM_CORE_EXPORT ctkDICOMWorker : public ctkAbstractWorker +{ + Q_OBJECT + +public: + typedef ctkAbstractWorker Superclass; + explicit ctkDICOMWorker(); + virtual ~ctkDICOMWorker(); + + /// Execute worker + virtual void run() = 0; + + /// Cancel worker + virtual void cancel() = 0; + +public slots: + void startNextJob(); + void onJobCanceled(); + +private: + Q_DISABLE_COPY(ctkDICOMWorker) +}; + + +#endif // ctkDICOMWorker_h diff --git a/Libs/DICOM/Widgets/CMakeLists.txt b/Libs/DICOM/Widgets/CMakeLists.txt index 7c55927199..283b312d27 100644 --- a/Libs/DICOM/Widgets/CMakeLists.txt +++ b/Libs/DICOM/Widgets/CMakeLists.txt @@ -33,8 +33,16 @@ set(KIT_SRCS ctkDICOMQueryWidget.h ctkDICOMObjectListWidget.cpp ctkDICOMObjectListWidget.h + ctkDICOMPatientItemWidget.cpp + ctkDICOMPatientItemWidget.h + ctkDICOMSeriesItemWidget.cpp + ctkDICOMSeriesItemWidget.h ctkDICOMServerNodeWidget.cpp ctkDICOMServerNodeWidget.h + ctkDICOMServerNodeWidget2.cpp + ctkDICOMServerNodeWidget2.h + ctkDICOMStudyItemWidget.cpp + ctkDICOMStudyItemWidget.h ctkDICOMTableManager.h ctkDICOMTableManager.cpp ctkDICOMTableView.cpp @@ -43,6 +51,8 @@ set(KIT_SRCS ctkDICOMThumbnailGenerator.h ctkDICOMThumbnailListWidget.cpp ctkDICOMThumbnailListWidget.h + ctkDICOMVisualBrowserWidget.cpp + ctkDICOMVisualBrowserWidget.h ) # Headers that should run through moc @@ -57,11 +67,16 @@ set(KIT_MOC_SRCS ctkDICOMObjectModel.h ctkDICOMQueryRetrieveWidget.h ctkDICOMQueryWidget.h + ctkDICOMPatientItemWidget.h + ctkDICOMSeriesItemWidget.h ctkDICOMServerNodeWidget.h + ctkDICOMServerNodeWidget2.h + ctkDICOMStudyItemWidget.h ctkDICOMTableManager.h ctkDICOMTableView.h ctkDICOMThumbnailGenerator.h ctkDICOMThumbnailListWidget.h + ctkDICOMVisualBrowserWidget.h ) # UI files - includes new widgets @@ -74,19 +89,27 @@ set(KIT_UI_FORMS Resources/UI/ctkDICOMQueryRetrieveWidget.ui Resources/UI/ctkDICOMQueryWidget.ui Resources/UI/ctkDICOMObjectListWidget.ui + Resources/UI/ctkDICOMPatientItemWidget.ui + Resources/UI/ctkDICOMSeriesItemWidget.ui Resources/UI/ctkDICOMServerNodeWidget.ui + Resources/UI/ctkDICOMServerNodeWidget2.ui + Resources/UI/ctkDICOMStudyItemWidget.ui Resources/UI/ctkDICOMTableManager.ui Resources/UI/ctkDICOMTableView.ui + Resources/UI/ctkDICOMVisualBrowserWidget.ui ) # Resources set(KIT_resources + Resources/UI/ctkDICOMWidget.qrc ) # Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake # The following macro will read the target libraries from the file 'target_libraries.cmake' ctkFunctionGetTargetLibraries(KIT_target_libraries) +list(APPEND KIT_target_libraries Qt5::Svg) + ctkMacroBuildLib( NAME ${PROJECT_NAME} EXPORT_DIRECTIVE ${KIT_export_directive} diff --git a/Libs/DICOM/Widgets/Plugins/CMakeLists.txt b/Libs/DICOM/Widgets/Plugins/CMakeLists.txt index d3eadcde58..2ef9160efd 100644 --- a/Libs/DICOM/Widgets/Plugins/CMakeLists.txt +++ b/Libs/DICOM/Widgets/Plugins/CMakeLists.txt @@ -20,6 +20,9 @@ set(PLUGIN_SRCS ctkDICOMTableManagerPlugin.h ctkDICOMTableViewPlugin.cpp ctkDICOMTableViewPlugin.h + + ctkDICOMVisualBrowserWidgetPlugin.cpp + ctkDICOMVisualBrowserWidgetPlugin.h ) # Headers that should run through moc @@ -29,6 +32,7 @@ set(PLUGIN_MOC_SRCS ctkDICOMQueryRetrieveWidgetPlugin.h ctkDICOMTableManagerPlugin.h ctkDICOMTableViewPlugin.h + ctkDICOMVisualBrowserWidgetPlugin.h ) # Resources diff --git a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp new file mode 100644 index 0000000000..acc2b3574c --- /dev/null +++ b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp @@ -0,0 +1,71 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// CTK includes +#include "ctkDICOMVisualBrowserWidgetPlugin.h" +#include "ctkDICOMVisualBrowserWidget.h" + +//----------------------------------------------------------------------------- +ctkDICOMVisualBrowserWidgetPlugin::ctkDICOMVisualBrowserWidgetPlugin(QObject* pluginParent) + : QObject(pluginParent) +{ +} + +//----------------------------------------------------------------------------- +QWidget *ctkDICOMVisualBrowserWidgetPlugin::createWidget(QWidget *parentForWidget) +{ + ctkDICOMVisualBrowserWidget* newWidget = new ctkDICOMVisualBrowserWidget(parentForWidget); + return newWidget; +} + +//----------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidgetPlugin::domXml() const +{ + return "\n" + "\n"; +} + +// -------------------------------------------------------------------------- +QIcon ctkDICOMVisualBrowserWidgetPlugin::icon() const +{ + return QIcon(":/Icons/listview.png"); +} + +//----------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidgetPlugin::includeFile() const +{ + return "ctkDICOMVisualBrowserWidget.h"; +} + +//----------------------------------------------------------------------------- +bool ctkDICOMVisualBrowserWidgetPlugin::isContainer() const +{ + return false; +} + +//----------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidgetPlugin::name() const +{ + return "ctkDICOMVisualBrowserWidget"; +} diff --git a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h new file mode 100644 index 0000000000..95385270f5 --- /dev/null +++ b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h @@ -0,0 +1,48 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMVisualBrowserWidgetPlugin_h +#define __ctkDICOMVisualBrowserWidgetPlugin_h + +// CTK includes +#include "ctkDICOMWidgetsAbstractPlugin.h" + +class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMVisualBrowserWidgetPlugin + : public QObject + , public ctkDICOMWidgetsAbstractPlugin +{ + Q_OBJECT + +public: + ctkDICOMVisualBrowserWidgetPlugin(QObject *_parent = 0); + + QWidget *createWidget(QWidget *_parent); + QString domXml() const; + QIcon icon() const; + QString includeFile() const; + bool isContainer() const; + QString name() const; + +}; + +#endif diff --git a/Libs/DICOM/Widgets/Plugins/ctkDICOMWidgetsPlugins.h b/Libs/DICOM/Widgets/Plugins/ctkDICOMWidgetsPlugins.h index 60bc85014a..36866b5a9c 100644 --- a/Libs/DICOM/Widgets/Plugins/ctkDICOMWidgetsPlugins.h +++ b/Libs/DICOM/Widgets/Plugins/ctkDICOMWidgetsPlugins.h @@ -30,6 +30,7 @@ #include "ctkDICOMQueryRetrieveWidgetPlugin.h" #include "ctkDICOMTableManagerPlugin.h" #include "ctkDICOMTableViewPlugin.h" +#include "ctkDICOMVisualBrowserWidgetPlugin.h" /// \class Group the plugins in one library class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMWidgetsPlugins @@ -47,6 +48,7 @@ class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMWidgetsPlugins plugins << new ctkDICOMQueryRetrieveWidgetPlugin; plugins << new ctkDICOMTableManagerPlugin; plugins << new ctkDICOMTableViewPlugin; + plugins << new ctkDICOMVisualBrowserWidgetPlugin; return plugins; } }; diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/accept.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/accept.svg new file mode 100644 index 0000000000..bdeb9c44ba --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/accept.svg @@ -0,0 +1 @@ + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg new file mode 100644 index 0000000000..846383523a --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/cancel.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/cancel.svg new file mode 100644 index 0000000000..a22fdd0469 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/cancel.svg @@ -0,0 +1 @@ + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/cloud.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/cloud.svg new file mode 100644 index 0000000000..73b2b2e15d --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/cloud.svg @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg new file mode 100644 index 0000000000..560d174b9b --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg new file mode 100644 index 0000000000..c386420774 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/downloading.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/downloading.svg new file mode 100644 index 0000000000..54395ad12b --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/downloading.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/import.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/import.svg new file mode 100644 index 0000000000..31f5598fdb --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/import.svg @@ -0,0 +1 @@ + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg new file mode 100644 index 0000000000..4e5d743153 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/loaded.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/loaded.svg new file mode 100644 index 0000000000..9df0ec7bab --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/loaded.svg @@ -0,0 +1,50 @@ + + + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg new file mode 100644 index 0000000000..e172f878a6 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/patient.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/patient.svg new file mode 100644 index 0000000000..58f30a95c9 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/patient.svg @@ -0,0 +1 @@ + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg new file mode 100644 index 0000000000..c611282e40 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg new file mode 100644 index 0000000000..9cc945f49f --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg new file mode 100644 index 0000000000..8c2108c746 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/visible.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/visible.svg new file mode 100644 index 0000000000..bb86d8fa4a --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/visible.svg @@ -0,0 +1,50 @@ + + + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg new file mode 100644 index 0000000000..5b6554fd6a --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/warning.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/warning.svg new file mode 100644 index 0000000000..4d12f769cb --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/warning.svg @@ -0,0 +1 @@ + diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui new file mode 100644 index 0000000000..1dd5a16998 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui @@ -0,0 +1,365 @@ + + + ctkDICOMPatientItemWidget + + + + 0 + 0 + 1041 + 400 + + + + Patient Item + + + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 12 + + + + + + + Patient Information + + + false + + + false + + + 14 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + + + 100 + 0 + + + + + 11 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + 0 + + + ID + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 100 + 0 + + + + + 11 + + + + Sex + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 11 + + + + + + + Qt::AlignCenter + + + 10 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 100 + 0 + + + + + 11 + + + + Birth Date + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 11 + + + + + + + Qt::AlignCenter + + + 10 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 11 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + 0 + + + + + + Qt::AlignCenter + + + 10 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + 0 + 2 + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOff + + + true + + + + true + + + + 0 + 0 + 996 + 181 + + + + background-color: rgb(255, 255, 255); + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 1 + + + + + + + + + + + + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+
+ + + + + +
diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryWidget.ui index f38938066d..c5de6f31ef 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryWidget.ui @@ -7,7 +7,7 @@ 0 0 279 - 296 + 348 diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui new file mode 100644 index 0000000000..b0c5fd2f72 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui @@ -0,0 +1,101 @@ + + + ctkDICOMSeriesItemWidget + + + + 0 + 0 + 153 + 151 + + + + Series Item + + + + 7 + + + 7 + + + 7 + + + 7 + + + 7 + + + + + + 0 + 0 + + + + + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + Qt::AlignBottom|Qt::AlignHCenter + + + false + + + + 255 + 255 + 255 + + + + + + + + + + + + ctkThumbnailLabel + QWidget +
ctkThumbnailLabel.h
+
+
+ + + + +
diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui new file mode 100644 index 0000000000..82d75eaa8f --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui @@ -0,0 +1,407 @@ + + + ctkDICOMServerNodeWidget2 + + + + 0 + 0 + 1679 + 271 + + + + Server List + + + + 1 + + + 1 + + + 1 + + + 1 + + + 5 + + + + + Servers + + + Qt::AlignCenter + + + + + + + + 1 + 0 + + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideRight + + + 10 + + + true + + + false + + + 165 + + + 165 + + + true + + + false + + + true + + + false + + + false + + + + Name + + + + + Query/Retrieve + + + + + Storage + + + + + Calling AETitle + + + + + Called AETitle + + + + + Address + + + + + Port + + + + + Timeout + + + + + Protocol + + + + + Proxy + + + + + + + + + 1 + 0 + + + + Storage + + + false + + + false + + + + + + Port: + + + + + + + 11112 + + + + + + + false + + + + + + + Enable: + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + Status: + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + Inactive + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + AETitle: + + + + + + + CTKSTORE + + + true + + + + + + + + + + 3 + + + + + + 0 + 0 + + + + Add host + + + + :/Icons/add.svg:/Icons/add.svg + + + + + + + + 0 + 0 + + + + Verify host + + + + :/Icons/dns.svg:/Icons/dns.svg + + + + + + + + 0 + 0 + + + + Remove host + + + + :/Icons/delete.svg:/Icons/delete.svg + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 30 + + + + + + + + + 0 + 0 + + + + + 0 + 70 + + + + Qt::Vertical + + + QDialogButtonBox::Discard|QDialogButtonBox::Save + + + true + + + + + + + + + + ctkCheckBox + QCheckBox +
ctkCheckBox.h
+
+ + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+ + ctkPushButton + QPushButton +
ctkPushButton.h
+
+
+ + + + +
diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui new file mode 100644 index 0000000000..e48a467849 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui @@ -0,0 +1,235 @@ + + + ctkDICOMStudyItemWidget + + + + 0 + 0 + 378 + 534 + + + + Study Item + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 12 + + + + + + + Study ID 1234 --- Date + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + true + + + + 7 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:12pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + + + 0 + 1 + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + Qt::NoPen + + + 6 + + + false + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + ctkCheckBox + QCheckBox +
ctkCheckBox.h
+
+ + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+ + ctkFittedTextBrowser + QTextBrowser +
ctkFittedTextBrowser.h
+
+
+ + + + +
diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui new file mode 100644 index 0000000000..f405420237 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui @@ -0,0 +1,965 @@ + + + ctkDICOMVisualBrowserWidget + + + Qt::WindowModal + + + + 0 + 0 + 1201 + 678 + + + + Select data to load + + + + :/Icons/patient.svg:/Icons/patient.svg + + + + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + 11 + + + + + + + Patients Search + + + false + + + + 0 + + + 2 + + + 0 + + + 2 + + + 2 + + + + + 3 + + + + + 3 + + + + + 3 + + + + + + 0 + 0 + + + + + 11 + + + + ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Filter by patient ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + 3 + + + + + + 0 + 0 + + + + + 11 + + + + Name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Filter by patient name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + 3 + + + + + + 0 + 0 + + + + + 11 + + + + Study + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Filter by study description + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + 3 + + + + + + 0 + 0 + + + + + 11 + + + + Series + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Filter by series description + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + 3 + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + 11 + + + + Modality + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + Filter by modality + + + false + + + + + + Any + + + 0 + + + QComboBox::AdjustToContents + + + + + + true + + + 0 + + + + Any + + + + + CR + + + + + CT + + + + + MR + + + + + NM + + + + + US + + + + + PT + + + + + XA + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + 3 + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + 11 + + + + Date + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + Filter by date + + + + Any + + + + + Today + + + + + Yesterday + + + + + Last week + + + + + Last month + + + + + Last year + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Search the database. If servers has been provided, the widget will also query and retrieve the data. + + + + + + + :/Icons/query.svg:/Icons/query.svg + + + + 48 + 48 + + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 5 + + + + + + + + + 11 + + + + + + + Actions + + + false + + + + 0 + + + 2 + + + 0 + + + 2 + + + 2 + + + + + Close + + + + :/Icons/cancel.svg:/Icons/cancel.svg + + + + 24 + 24 + + + + + + + + Import + + + + :/Icons/import.svg:/Icons/import.svg + + + + 24 + 24 + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 70 + + + + Qt::StrongFocus + + + + + + No filters have been set and no patients have been found in the local database. +Please set at least one filter to query the servers + + + + :/Icons/warning.svg:/Icons/warning.svg + + + + 48 + 48 + + + + false + + + false + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 1 + + + + + + + QTabWidget::North + + + QTabWidget::Rounded + + + 1 + + + + 32 + 32 + + + + Qt::ElideNone + + + true + + + false + + + true + + + false + + + + + :/Icons/patient.svg:/Icons/patient.svg + + + Patient 1 + + + + + + :/Icons/patient.svg:/Icons/patient.svg + + + Patient 2 + + + + + + + + QFrame::Box + + + QFrame::Raised + + + + + + 0 + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + Progress + + + Qt::AlignCenter + + + + + + + false + + + 300 + + + false + + + true + + + + + + + + + + + 11 + + + + + + + Settings + + + false + + + true + + + + 0 + + + 2 + + + 0 + + + 2 + + + 2 + + + + + + + + + ctkCheckableComboBox + QComboBox +
ctkCheckableComboBox.h
+
+ + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+ + ctkComboBox + QComboBox +
ctkComboBox.h
+
+ + ctkPushButton + QPushButton +
ctkPushButton.h
+
+ + ctkSearchBox + QLineEdit +
ctkSearchBox.h
+
+
+ + + + +
diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc new file mode 100644 index 0000000000..5f282c68f9 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc @@ -0,0 +1,22 @@ + + + Icons/accept.svg + Icons/cancel.svg + Icons/cloud.svg + Icons/import.svg + Icons/loaded.svg + Icons/patient.svg + Icons/visible.svg + Icons/warning.svg + Icons/load.svg + Icons/text_document.svg + Icons/delete.svg + Icons/more_vert.svg + Icons/add.svg + Icons/dns.svg + Icons/save.svg + Icons/query.svg + Icons/wait.svg + Icons/downloading.svg + + diff --git a/Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt b/Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt index 1430d1a149..14b3b9046a 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt +++ b/Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt @@ -11,10 +11,15 @@ create_test_sourcelist(Tests ${KIT}CppTests.cpp ctkDICOMListenerWidgetTest1.cpp ctkDICOMModelTest2.cpp ctkDICOMObjectModelTest1.cpp + ctkDICOMPatientItemWidgetTest1.cpp ctkDICOMQueryResultsTabWidgetTest1.cpp ctkDICOMQueryRetrieveWidgetTest1.cpp + ctkDICOMSeriesItemWidgetTest1.cpp + ctkDICOMStudyItemWidgetTest1.cpp ctkDICOMServerNodeWidgetTest1.cpp + ctkDICOMServerNodeWidget2Test1.cpp ctkDICOMThumbnailListWidgetTest1.cpp + ctkDICOMVisualBrowserWidgetTest1.cpp ) set(Tests_MOC_CPPS @@ -54,8 +59,12 @@ SIMPLE_TEST(ctkDICOMModelTest2 ${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/ctkDICOMModelTest2-dicom.db ${CMAKE_CURRENT_SOURCE_DIR}/../../../Core/Resources/dicom-sample.sql ) +SIMPLE_TEST(ctkDICOMPatientItemWidgetTest1) SIMPLE_TEST(ctkDICOMQueryRetrieveWidgetTest1) SIMPLE_TEST(ctkDICOMQueryResultsTabWidgetTest1) +SIMPLE_TEST(ctkDICOMSeriesItemWidgetTest1) +SIMPLE_TEST(ctkDICOMStudyItemWidgetTest1) +SIMPLE_TEST(ctkDICOMServerNodeWidget2Test1) SIMPLE_TEST(ctkDICOMThumbnailListWidgetTest1 ${CMAKE_CURRENT_BINARY_DIR}/Testing/Temporary/ctkDICOMThumbnailListWidgetTest1-dicom.db ${CMAKE_CURRENT_SOURCE_DIR}/../../../Core/Resources/dicom-sample.sql @@ -71,4 +80,5 @@ if(EXISTS "${CTKData_DIR}") SIMPLE_TEST(ctkDICOMBrowserTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD) SIMPLE_TEST(ctkDICOMItemViewTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA) SIMPLE_TEST(ctkDICOMImageTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA) + SIMPLE_TEST(ctkDICOMVisualBrowserWidgetTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD) endif() diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest.cpp index 953a02ffbc..4cbe6e80c2 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest.cpp @@ -27,7 +27,6 @@ #include "ctkDICOMDatabase.h" #include "ctkDICOMBrowser.h" #include "ctkFileDialog.h" -#include "ctkScopedCurrentDir.h" #include "ctkTest.h" #include "ctkUtils.h" diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp new file mode 100644 index 0000000000..417c650a2a --- /dev/null +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp @@ -0,0 +1,75 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" +#include "ctkUtils.h" + +// ctkDICOMWidget includes +#include "ctkDICOMPatientItemWidget.h" + +// Test visual browser import functionality +int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) +{ + QApplication app(argc, argv); + + ctkDICOMPatientItemWidget widget; + // Test the default values + CHECK_QSTRING(widget.patientItem(), ""); + CHECK_QSTRING(widget.patientID(), ""); + CHECK_QSTRING(widget.filteringStudyDescription(), ""); + CHECK_QSTRING(widget.filteringSeriesDescription(), ""); + CHECK_INT(widget.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); + CHECK_INT(widget.numberOfStudiesPerPatient(), 2); + CHECK_INT(widget.numberOfSeriesPerRow(), 6); + CHECK_INT(widget.minimumThumbnailSize(), 300); + + // Test setting and getting + widget.setPatientItem("1"); + CHECK_QSTRING(widget.patientItem(), "1"); + widget.setPatientID("123456"); + CHECK_QSTRING(widget.patientID(), "123456"); + widget.setFilteringStudyDescription("study"); + CHECK_QSTRING(widget.filteringStudyDescription(), "study"); + widget.setFilteringSeriesDescription("series"); + CHECK_QSTRING(widget.filteringSeriesDescription(), "series"); + widget.setFilteringDate(ctkDICOMPatientItemWidget::DateType::LastYear); + CHECK_INT(widget.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); + widget.setNumberOfStudiesPerPatient(6); + CHECK_INT(widget.numberOfStudiesPerPatient(), 6); + widget.setNumberOfSeriesPerRow(12); + CHECK_INT(widget.numberOfSeriesPerRow(), 12); + widget.setMinimumThumbnailSize(150); + CHECK_INT(widget.minimumThumbnailSize(), 150); + + if (argc <= 2 || QString(argv[argc - 1]) != "-I") + { + QTimer::singleShot(200, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp new file mode 100644 index 0000000000..725487b1dc --- /dev/null +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp @@ -0,0 +1,84 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" +#include "ctkUtils.h" + +// ctkDICOMWidget includes +#include "ctkDICOMSeriesItemWidget.h" + +// Test visual browser import functionality +int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) +{ + QApplication app(argc, argv); + + ctkDICOMSeriesItemWidget widget; + // Test the default values + CHECK_QSTRING(widget.seriesItem(), ""); + CHECK_QSTRING(widget.patientID(), ""); + CHECK_QSTRING(widget.studyInstanceUID(), ""); + CHECK_QSTRING(widget.seriesInstanceUID(), ""); + CHECK_QSTRING(widget.seriesNumber(), ""); + CHECK_QSTRING(widget.modality(), ""); + CHECK_QSTRING(widget.seriesDescription(), ""); + CHECK_BOOL(widget.stopJobs(), false); + CHECK_BOOL(widget.raiseJobsPriority(), false); + CHECK_BOOL(widget.isCloud(), false); + CHECK_BOOL(widget.IsLoaded(), false); + CHECK_BOOL(widget.IsVisible(), false); + CHECK_INT(widget.thumbnailSize(), 300); + + // Test setting and getting + widget.setSeriesItem("1"); + CHECK_QSTRING(widget.seriesItem(), "1"); + widget.setPatientID("123456"); + CHECK_QSTRING(widget.patientID(), "123456"); + widget.setStudyInstanceUID("123456.123"); + CHECK_QSTRING(widget.studyInstanceUID(), "123456.123"); + widget.setSeriesInstanceUID("123456.456"); + CHECK_QSTRING(widget.seriesInstanceUID(), "123456.456"); + widget.setSeriesNumber("1"); + CHECK_QSTRING(widget.seriesNumber(), "1"); + widget.setModality("CT"); + CHECK_QSTRING(widget.modality(), "CT"); + widget.setSeriesDescription("description"); + CHECK_QSTRING(widget.seriesDescription(), "description"); + widget.setStopJobs(true); + CHECK_BOOL(widget.stopJobs(), true); + widget.setRaiseJobsPriority(true); + CHECK_BOOL(widget.raiseJobsPriority(), true); + widget.setThumbnailSize(150); + CHECK_INT(widget.thumbnailSize(), 150); + + if (argc <= 2 || QString(argv[argc - 1]) != "-I") + { + QTimer::singleShot(200, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp new file mode 100644 index 0000000000..8c82d7475a --- /dev/null +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp @@ -0,0 +1,97 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" + +// ctkDICOMCore includes +#include "ctkDICOMScheduler.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMServerNodeWidget2.h" + +int ctkDICOMServerNodeWidget2Test1( int argc, char * argv [] ) +{ + QApplication app(argc, argv); + + ctkDICOMServerNodeWidget2 widget; + + // Test the default values + // Check the default values of storage AE title and port + CHECK_QSTRING(widget.storageAETitle(), "CTKSTORE"); + CHECK_INT(widget.storagePort(), 11112); + + // Test setting and getting storage AE title + widget.setStorageAETitle("MyStorage"); + CHECK_QSTRING(widget.storageAETitle(), "MyStorage"); + + // Test setting and getting storage port + widget.setStoragePort(12345); + CHECK_INT(widget.storagePort(), 12345); + + // Test default servers + ctkDICOMScheduler scheduler; + widget.setScheduler(scheduler); + CHECK_QSTRING(widget.getServerNameFromIndex(0), "ExampleHost"); + CHECK_BOOL(widget.getServer("ExampleHost")->queryRetrieveEnabled(), false); + CHECK_BOOL(widget.getServer("ExampleHost")->storageEnabled(), false); + CHECK_QSTRING(widget.getServer("ExampleHost")->callingAETitle(), "CTK"); + CHECK_QSTRING(widget.getServer("ExampleHost")->calledAETitle(), "AETITLE"); + CHECK_QSTRING(widget.getServer("ExampleHost")->host(), "dicom.example.com"); + CHECK_INT(widget.getServer("ExampleHost")->port(), 11112); + CHECK_QSTRING(widget.getServer("ExampleHost")->retrieveProtocolAsString(), "CGET"); + CHECK_INT(widget.getServer("ExampleHost")->connectionTimeout(), 30); + + CHECK_QSTRING(widget.getServerNameFromIndex(1), "MedicalConnections"); + CHECK_BOOL(widget.getServer("MedicalConnections")->queryRetrieveEnabled(), false); + CHECK_BOOL(widget.getServer("MedicalConnections")->storageEnabled(), false); + CHECK_QSTRING(widget.getServer("MedicalConnections")->callingAETitle(), "CTK"); + CHECK_QSTRING(widget.getServer("MedicalConnections")->calledAETitle(), "ANYAE"); + CHECK_QSTRING(widget.getServer("MedicalConnections")->host(), "dicomserver.co.uk"); + CHECK_INT(widget.getServer("MedicalConnections")->port(), 104); + CHECK_QSTRING(widget.getServer("MedicalConnections")->retrieveProtocolAsString(), "CGET"); + CHECK_INT(widget.getServer("MedicalConnections")->connectionTimeout(), 30); + + // Test adding and removing servers + ctkDICOMServer* server = new ctkDICOMServer(); + server->setConnectionName("server"); + widget.addServer(server); + CHECK_INT(widget.getNumberOfServers(), 3); + CHECK_INT(widget.getServerIndexFromName("server"), 2); + CHECK_QSTRING(widget.getServerNameFromIndex(2), "server"); + CHECK_QSTRING(widget.getServer("server")->connectionName(), server->connectionName()); + widget.removeServer("server"); + CHECK_INT(widget.getNumberOfServers(), 2); + delete server; + + if (argc <= 1 || QString(argv[1]) != "-I") + { + QTimer::singleShot(200, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp new file mode 100644 index 0000000000..31f9b2c14b --- /dev/null +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp @@ -0,0 +1,81 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" +#include "ctkUtils.h" + +// ctkDICOMWidget includes +#include "ctkDICOMStudyItemWidget.h" + +// Test visual browser import functionality +int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) +{ + QApplication app(argc, argv); + + ctkDICOMStudyItemWidget widget; + // Test the default values + CHECK_QSTRING(widget.studyItem(), ""); + CHECK_QSTRING(widget.patientID(), ""); + CHECK_QSTRING(widget.studyInstanceUID(), ""); + CHECK_QSTRING(widget.title(), "Study ID 1234 --- Date"); + CHECK_QSTRING(widget.description(), ""); + CHECK_QSTRING(widget.filteringSeriesDescription(), ""); + CHECK_BOOL(widget.collapsed(), false) + CHECK_BOOL(widget.selection(), false) + CHECK_INT(widget.numberOfSeriesPerRow(), 6); + CHECK_INT(widget.thumbnailSize(), 300); + + // Test setting and getting + widget.setStudyItem("1"); + CHECK_QSTRING(widget.studyItem(), "1"); + widget.setPatientID("123456"); + CHECK_QSTRING(widget.patientID(), "123456"); + widget.setStudyInstanceUID("123456.123"); + CHECK_QSTRING(widget.studyInstanceUID(), "123456.123"); + widget.setTitle("title"); + CHECK_QSTRING(widget.title(), "title"); + widget.setDescription("description"); + CHECK_QSTRING(widget.description(), "description"); + widget.setFilteringSeriesDescription("seriesDescription"); + CHECK_QSTRING(widget.filteringSeriesDescription(), "seriesDescription"); + widget.setCollapsed(true); + CHECK_BOOL(widget.collapsed(), true); + widget.setSelection(true); + CHECK_BOOL(widget.selection(), true); + widget.setNumberOfSeriesPerRow(12); + CHECK_INT(widget.numberOfSeriesPerRow(), 12); + widget.setThumbnailSize(150); + CHECK_INT(widget.thumbnailSize(), 150); + + if (argc <= 2 || QString(argv[argc - 1]) != "-I") + { + QTimer::singleShot(200, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp new file mode 100644 index 0000000000..50c30fcf71 --- /dev/null +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -0,0 +1,151 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include + +// ctk includes +#include "ctkCoreTestingMacros.h" +#include "ctkUtils.h" + +// ctkDICOMWidget includes +#include "ctkDICOMVisualBrowserWidget.h" + +int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) +{ + QApplication app(argc, argv); + + qDebug() << "argc = " << argc; + for (int i = 0; i < argc; ++i) + { + qDebug() << "\t" << argv[i]; + } + + ctkDICOMVisualBrowserWidget browser; + // Test the default values + CHECK_QSTRING(browser.storageAETitle(), "CTKSTORE"); + CHECK_INT(browser.storagePort(), 11112); + CHECK_QSTRING(browser.filteringPatientID(), ""); + CHECK_QSTRING(browser.filteringPatientName(), ""); + CHECK_QSTRING(browser.filteringStudyDescription(), ""); + CHECK_QSTRING(browser.filteringSeriesDescription(), ""); + CHECK_QSTRING(browser.filteringModalities()[0], "Any"); + CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); + CHECK_INT(browser.numberOfStudiesPerPatient(), 2); + CHECK_INT(browser.numberOfSeriesPerRow(), 6); + CHECK_INT(browser.minimumThumbnailSize(), 300); + CHECK_BOOL(browser.isSendActionVisible(), false); + CHECK_BOOL(browser.isDeleteActionVisible(), true); + + // Test visual browser import functionality + QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMBrowserTest1-db")); + QString dbDir = tempFileInfo.absoluteFilePath(); + qDebug() << "\n\nUsing directory: " << dbDir; + if (tempFileInfo.exists()) + { + qDebug() << "\n\nRemoving directory: " << dbDir; + ctk::removeDirRecursively(dbDir); + } + qDebug() << "\n\nMaking directory: " << dbDir; + QDir dir(dbDir); + dir.mkdir(dbDir); + + browser.setDatabaseDirectory(dbDir); + browser.show(); + + qDebug() << "Importing directory " << argv[1]; + + // Test import of a few specific files + QDirIterator it(argv[1], QStringList() << "*.IMA", QDir::Files, QDirIterator::Subdirectories); + // Skip a few files + it.next(); + it.next(); + // Add 3 files + QStringList files; + files << it.next(); + files << it.next(); + files << it.next(); + browser.importFiles(files); + browser.waitForImportFinished(); + + qDebug() << browser.patientsAddedDuringImport() + << " " << browser.studiesAddedDuringImport() + << " " << browser.seriesAddedDuringImport() + << " " << browser.instancesAddedDuringImport(); + + CHECK_INT(browser.patientsAddedDuringImport(), 1); + CHECK_INT(browser.studiesAddedDuringImport(), 1); + CHECK_INT(browser.seriesAddedDuringImport(), 1); + CHECK_INT(browser.instancesAddedDuringImport(), 3); + + browser.importDirectories(QStringList() << argv[1]); + browser.waitForImportFinished(); + + qDebug() << "\n\nAdded to database directory: " << files; + + CHECK_INT(browser.patientsAddedDuringImport(), 1); + CHECK_INT(browser.studiesAddedDuringImport(), 1); + CHECK_INT(browser.seriesAddedDuringImport(), 1); + CHECK_INT(browser.instancesAddedDuringImport(), 100); + + qDebug() << "\n\nAdded to database directory: " << dbDir; + + // Test setting and getting + browser.setStorageAETitle("storage"); + CHECK_QSTRING(browser.storageAETitle(), "storage"); + browser.setStoragePort(2014); + CHECK_INT(browser.storagePort(), 2014); + browser.setFilteringPatientID("123456"); + CHECK_QSTRING(browser.filteringPatientID(), "123456"); + browser.setFilteringPatientName("Name"); + CHECK_QSTRING(browser.filteringPatientName(), "Name"); + browser.setFilteringStudyDescription("StudyDescription"); + CHECK_QSTRING(browser.filteringStudyDescription(), "StudyDescription"); + browser.setFilteringSeriesDescription("SeriesDescription"); + CHECK_QSTRING(browser.filteringSeriesDescription(), "SeriesDescription"); + QStringList filteringModalities = {"CT"}; + browser.setFilteringModalities(filteringModalities); + CHECK_QSTRING(browser.filteringModalities()[0], "CT"); + browser.setFilteringDate(ctkDICOMPatientItemWidget::DateType::LastYear); + CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); + browser.setNumberOfStudiesPerPatient(6); + CHECK_INT(browser.numberOfStudiesPerPatient(), 6); + browser.setNumberOfSeriesPerRow(12); + CHECK_INT(browser.numberOfSeriesPerRow(), 12); + browser.setMinimumThumbnailSize(150); + CHECK_INT(browser.minimumThumbnailSize(), 150); + browser.setSendActionVisible(true); + CHECK_BOOL(browser.isSendActionVisible(), true); + browser.setDeleteActionVisible(false); + CHECK_BOOL(browser.isDeleteActionVisible(), false); + + if (argc <= 2 || QString(argv[argc - 1]) != "-I") + { + QTimer::singleShot(200, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp b/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp index 22dda34897..ace876886f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp @@ -818,7 +818,6 @@ void ctkDICOMBrowser::onQueryRetrieveFinished() //---------------------------------------------------------------------------- void ctkDICOMBrowser::onRemoveAction() { - Q_D(ctkDICOMBrowser); this->removeSelectedItems(ctkDICOMModel::SeriesType); } @@ -1089,7 +1088,7 @@ bool ctkDICOMBrowser::confirmDeleteSelectedUIDs(QStringList uids) return false; } - ctkMessageBox confirmDeleteDialog; + ctkMessageBox confirmDeleteDialog(this); QString message = tr("Do you want to delete the following selected items?"); // calculate maximum number of rows that fit in the browser widget to have a reasonable limit @@ -1441,7 +1440,7 @@ void ctkDICOMBrowser::exportSeries(QString dirPath, QStringList uids) QString errorString = tr("Unable to create export destination directory:\n\n%1" "\n\nHalting export.") .arg(destinationDir); - ctkMessageBox createDirectoryErrorMessageBox; + ctkMessageBox createDirectoryErrorMessageBox(this); createDirectoryErrorMessageBox.setText(errorString); createDirectoryErrorMessageBox.setIcon(QMessageBox::Warning); createDirectoryErrorMessageBox.exec(); @@ -1491,7 +1490,7 @@ void ctkDICOMBrowser::exportSeries(QString dirPath, QStringList uids) QString errorString = tr("Export destination file already exists:\n\n%1" "\n\nHalting export.") .arg(destinationFileName); - ctkMessageBox copyErrorMessageBox; + ctkMessageBox copyErrorMessageBox(this); copyErrorMessageBox.setText(errorString); copyErrorMessageBox.setIcon(QMessageBox::Warning); copyErrorMessageBox.exec(); @@ -1507,7 +1506,7 @@ void ctkDICOMBrowser::exportSeries(QString dirPath, QStringList uids) "\n\nHalting export.") .arg(filePath) .arg(destinationFileName); - ctkMessageBox copyErrorMessageBox; + ctkMessageBox copyErrorMessageBox(this); copyErrorMessageBox.setText(errorString); copyErrorMessageBox.setIcon(QMessageBox::Warning); copyErrorMessageBox.exec(); diff --git a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp index c28dc4a6b0..7e1d5f32ff 100644 --- a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp @@ -321,10 +321,18 @@ void ctkDICOMObjectListWidget::updateWidget() // only update the thumbnail if visible for better update performance ctkDICOMThumbnailGenerator thumbnailGenerator; QImage thumbnailImage; - if (!thumbnailGenerator.generateThumbnail(d->currentFile, thumbnailImage)) + if (d->currentFile.isEmpty()) { thumbnailGenerator.generateBlankThumbnail(thumbnailImage); } + else + { + if (!thumbnailGenerator.generateThumbnail(d->currentFile, thumbnailImage)) + { + thumbnailGenerator.generateBlankThumbnail(thumbnailImage); + } + } + d->thumbnailLabel->setPixmap(QPixmap::fromImage(thumbnailImage)); } } diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp new file mode 100644 index 0000000000..b8b0137e74 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -0,0 +1,828 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +//Qt includes +#include +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMJobResponseSet.h" + +// ctkDICOMWidgets includes +#include "ctkDICOMSeriesItemWidget.h" +#include "ctkDICOMStudyItemWidget.h" +#include "ctkDICOMPatientItemWidget.h" +#include "ui_ctkDICOMPatientItemWidget.h" + +static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMPatientItemWidget"); + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +class ctkDICOMPatientItemWidgetPrivate: public Ui_ctkDICOMPatientItemWidget +{ + Q_DECLARE_PUBLIC( ctkDICOMPatientItemWidget ); + +protected: + ctkDICOMPatientItemWidget* const q_ptr; + +public: + ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatientItemWidget& obj); + ~ctkDICOMPatientItemWidgetPrivate(); + + void init(QWidget* parentWidget); + + QString getPatientItemFromPatientID(const QString& patientID); + QString formatDate(const QString&); + bool isStudyItemAlreadyAdded(const QString& studyItem); + void clearLayout(QLayout* layout, bool deleteWidgets = true); + void createStudies(); + + QSharedPointer DicomDatabase; + QSharedPointer Scheduler; + QSharedPointer VisualDICOMBrowser; + + int NumberOfStudiesPerPatient; + int NumberOfSeriesPerRow; + int MinimumThumbnailSize; + + QString PatientItem; + QString PatientID; + + QString FilteringStudyDescription; + ctkDICOMPatientItemWidget::DateType FilteringDate; + + QString FilteringSeriesDescription; + QStringList FilteringModalities; + + QList StudyItemWidgetsList; +}; + +//---------------------------------------------------------------------------- +// ctkDICOMPatientItemWidgetPrivate methods + +//---------------------------------------------------------------------------- +ctkDICOMPatientItemWidgetPrivate::ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatientItemWidget& obj) + : q_ptr(&obj) +{ + this->FilteringDate = ctkDICOMPatientItemWidget::DateType::Any; + this->NumberOfStudiesPerPatient = 2; + this->NumberOfSeriesPerRow = 6; + this->MinimumThumbnailSize = 300; + this->PatientItem = ""; + this->PatientID = ""; + this->FilteringStudyDescription = ""; + this->FilteringSeriesDescription = ""; + + this->DicomDatabase = nullptr; + this->Scheduler = nullptr; + this->VisualDICOMBrowser = nullptr; +} + +//---------------------------------------------------------------------------- +ctkDICOMPatientItemWidgetPrivate::~ctkDICOMPatientItemWidgetPrivate() +{ + QLayout *StudiesListWidgetLayout = this->StudiesListWidget->layout(); + this->clearLayout(StudiesListWidgetLayout); +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidgetPrivate::init(QWidget* parentWidget) +{ + Q_Q(ctkDICOMPatientItemWidget); + this->setupUi(q); + + this->VisualDICOMBrowser = QSharedPointer(parentWidget, skipDelete); + + this->PatientIDValueLabel->setWordWrap(true); + this->PatientBirthDateValueLabel->setWordWrap(true); + this->PatientSexValueLabel->setWordWrap(true); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMPatientItemWidgetPrivate::getPatientItemFromPatientID(const QString& patientID) +{ + if (!this->DicomDatabase) + { + return ""; + } + + QStringList patientList = this->DicomDatabase->patients(); + foreach (QString patientItem, patientList) + { + QString patientItemID = this->DicomDatabase->fieldForPatient("PatientID", patientItem); + + if (patientID == patientItemID) + { + return patientItem; + } + } + + return ""; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMPatientItemWidgetPrivate::formatDate(const QString& date) +{ + QString formattedDate = date; + formattedDate.replace(QString("-"), QString("")); + return QDate::fromString(formattedDate, "yyyyMMdd").toString(); +} + +//---------------------------------------------------------------------------- +bool ctkDICOMPatientItemWidgetPrivate::isStudyItemAlreadyAdded(const QString &studyItem) +{ + bool alreadyAdded = false; + foreach (ctkDICOMStudyItemWidget* studyItemWidget, this->StudyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + + if (studyItemWidget->studyItem() == studyItem) + { + alreadyAdded = true; + break; + } + } + + return alreadyAdded; +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidgetPrivate::clearLayout(QLayout *layout, bool deleteWidgets) +{ + Q_Q(ctkDICOMPatientItemWidget); + + if (!layout) + { + return; + } + + this->StudyItemWidgetsList.clear(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, this->StudyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + + q->disconnect(studyItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this->VisualDICOMBrowser.data(), SLOT(showStudyContextMenu(const QPoint&))); + + } + + while (QLayoutItem* item = layout->takeAt(0)) + { + if (deleteWidgets) + { + if (QWidget* widget = item->widget()) + { + widget->deleteLater(); + } + } + + if (QLayout* childLayout = item->layout()) + { + clearLayout(childLayout, deleteWidgets); + } + + delete item; + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidgetPrivate::createStudies() +{ + Q_Q(ctkDICOMPatientItemWidget); + + if (!this->DicomDatabase) + { + logger.error("createStudies failed, no DICOM Database has been set. \n"); + return; + } + + QLayout *studiesListWidgetLayout = this->StudiesListWidget->layout(); + if (this->PatientItem.isEmpty()) + { + this->PatientIDValueLabel->setText(""); + this->PatientSexValueLabel->setText(""); + this->PatientBirthDateValueLabel->setText(""); + return; + } + else + { + this->PatientIDValueLabel->setText(this->DicomDatabase->fieldForPatient("PatientID", this->PatientItem)); + this->PatientSexValueLabel->setText(this->DicomDatabase->fieldForPatient("PatientsSex", this->PatientItem)); + this->PatientBirthDateValueLabel->setText(this->formatDate(this->DicomDatabase->fieldForPatient("PatientsBirthDate", this->PatientItem))); + } + + QStringList studiesList = this->DicomDatabase->studiesForPatient(this->PatientItem); + + // Filter with studyDescription and studyDate and sort by Date + QMap studiesMap; + foreach (QString studyItem, studiesList) + { + if (this->isStudyItemAlreadyAdded(studyItem)) + { + continue; + } + + QString studyInstanceUID = this->DicomDatabase->fieldForStudy("StudyInstanceUID", studyItem); + if (studyInstanceUID.isEmpty()) + { + continue; + } + + QString studyDateString = this->DicomDatabase->fieldForStudy("StudyDate", studyItem); + studyDateString.replace(QString("-"), QString("")); + QString studyDescription = this->DicomDatabase->fieldForStudy("StudyDescription", studyItem); + + if (studyDateString.isEmpty()) + { + studyDateString = this->DicomDatabase->fieldForPatient("PatientsBirthDate", this->PatientItem); + if (studyDateString.isEmpty()) + { + studyDateString = "19000101"; + } + } + + if ((!this->FilteringStudyDescription.isEmpty() && + !studyDescription.contains(this->FilteringStudyDescription, Qt::CaseInsensitive))) + { + continue; + } + + int nDays = ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(this->FilteringDate); + QDate studyDate = QDate::fromString(studyDateString, "yyyyMMdd"); + if (nDays != -1) + { + QDate endDate = QDate::currentDate(); + QDate startDate = endDate.addDays(-nDays); + if (studyDate < startDate || studyDate > endDate) + { + continue; + } + } + long long date = studyDate.toJulianDay(); + while (studiesMap.contains(date)) + { + date++; + } + // QMap automatically sort in ascending with the key, + // but we want descending (latest study should be in the first row) + long long key = LLONG_MAX - date; + studiesMap[key] = studyItem; + } + + foreach (QString studyItem, studiesMap) + { + q->addStudyItemWidget(studyItem); + } + + QSpacerItem* verticalSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Expanding); + studiesListWidgetLayout->addItem(verticalSpacer); +} + +//---------------------------------------------------------------------------- +// ctkDICOMPatientItemWidget methods + +//---------------------------------------------------------------------------- +ctkDICOMPatientItemWidget::ctkDICOMPatientItemWidget(QWidget* parentWidget) + : Superclass(parentWidget) + , d_ptr(new ctkDICOMPatientItemWidgetPrivate(*this)) +{ + Q_D(ctkDICOMPatientItemWidget); + d->init(parentWidget); +} + +//---------------------------------------------------------------------------- +ctkDICOMPatientItemWidget::~ctkDICOMPatientItemWidget() +{ +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setPatientItem(const QString &patientItem) +{ + Q_D(ctkDICOMPatientItemWidget); + d->PatientItem = patientItem; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMPatientItemWidget::patientItem() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->PatientItem; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setPatientID(const QString &patientID) +{ + Q_D(ctkDICOMPatientItemWidget); + d->PatientID = patientID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMPatientItemWidget::patientID() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->PatientID; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setFilteringStudyDescription(const QString& filteringStudyDescription) +{ + Q_D(ctkDICOMPatientItemWidget); + d->FilteringStudyDescription = filteringStudyDescription; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMPatientItemWidget::filteringStudyDescription() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->FilteringStudyDescription; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType &filteringDate) +{ + Q_D(ctkDICOMPatientItemWidget); + d->FilteringDate = filteringDate; +} + +//------------------------------------------------------------------------------ +ctkDICOMPatientItemWidget::DateType ctkDICOMPatientItemWidget::filteringDate() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->FilteringDate; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setFilteringSeriesDescription(const QString& filteringSeriesDescription) +{ + Q_D(ctkDICOMPatientItemWidget); + d->FilteringSeriesDescription = filteringSeriesDescription; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMPatientItemWidget::filteringSeriesDescription() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->FilteringSeriesDescription; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setFilteringModalities(const QStringList &filteringModalities) +{ + Q_D(ctkDICOMPatientItemWidget); + d->FilteringModalities = filteringModalities; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMPatientItemWidget::filteringModalities() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->FilteringModalities; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient) +{ + Q_D(ctkDICOMPatientItemWidget); + d->NumberOfStudiesPerPatient = numberOfStudiesPerPatient; +} + +//------------------------------------------------------------------------------ +int ctkDICOMPatientItemWidget::numberOfStudiesPerPatient() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->NumberOfStudiesPerPatient; +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) +{ + Q_D(ctkDICOMPatientItemWidget); + d->NumberOfSeriesPerRow = numberOfSeriesPerRow; +} + +//------------------------------------------------------------------------------ +int ctkDICOMPatientItemWidget::numberOfSeriesPerRow() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->NumberOfSeriesPerRow; +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::setMinimumThumbnailSize(int minimumThumbnailSize) +{ + Q_D(ctkDICOMPatientItemWidget); + d->MinimumThumbnailSize = minimumThumbnailSize; +} + +//---------------------------------------------------------------------------- +int ctkDICOMPatientItemWidget::minimumThumbnailSize() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->MinimumThumbnailSize; +} + +//---------------------------------------------------------------------------- +ctkDICOMScheduler* ctkDICOMPatientItemWidget::scheduler()const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMPatientItemWidget::schedulerShared()const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::setScheduler(ctkDICOMScheduler& scheduler) +{ + Q_D(ctkDICOMPatientItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } + + d->Scheduler = QSharedPointer(&scheduler, skipDelete); + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::setScheduler(QSharedPointer scheduler) +{ + Q_D(ctkDICOMPatientItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } + + d->Scheduler = scheduler; + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } +} + +//---------------------------------------------------------------------------- +ctkDICOMDatabase* ctkDICOMPatientItemWidget::dicomDatabase()const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->DicomDatabase.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMPatientItemWidget::dicomDatabaseShared()const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->DicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::setDicomDatabase(ctkDICOMDatabase& dicomDatabase) +{ + Q_D(ctkDICOMPatientItemWidget); + d->DicomDatabase = QSharedPointer(&dicomDatabase, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::setDicomDatabase(QSharedPointer dicomDatabase) +{ + Q_D(ctkDICOMPatientItemWidget); + d->DicomDatabase = dicomDatabase; +} + +//------------------------------------------------------------------------------ +QList ctkDICOMPatientItemWidget::studyItemWidgetsList() const +{ + Q_D(const ctkDICOMPatientItemWidget); + return d->StudyItemWidgetsList; +} + +//------------------------------------------------------------------------------ +int ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(DateType FilteringDate) +{ + int nDays = 0; + switch (FilteringDate) + { + case ctkDICOMPatientItemWidget::DateType::Any: + nDays = -1; + break; + case ctkDICOMPatientItemWidget::DateType::Today: + nDays = 0; + break; + case ctkDICOMPatientItemWidget::DateType::Yesterday: + nDays = 1; + break; + case ctkDICOMPatientItemWidget::DateType::LastWeek: + nDays = 7; + break; + case ctkDICOMPatientItemWidget::DateType::LastMonth: + nDays = 30; + break; + case ctkDICOMPatientItemWidget::DateType::LastYear: + nDays = 365; + break; + } + + return nDays; +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString &studyItem) +{ + Q_D(ctkDICOMPatientItemWidget); + + if (!d->DicomDatabase) + { + logger.error("addStudyItemWidget failed, no DICOM Database has been set. \n"); + return; + } + + QString studyInstanceUID = d->DicomDatabase->fieldForStudy("StudyInstanceUID", studyItem); + QString studyID = d->DicomDatabase->fieldForStudy("StudyID", studyItem); + QString studyDate = d->DicomDatabase->fieldForStudy("StudyDate", studyItem); + QString formattedStudyDate = d->formatDate(studyDate); + QString studyDescription = d->DicomDatabase->fieldForStudy("StudyDescription", studyItem); + + ctkDICOMStudyItemWidget* studyItemWidget = new ctkDICOMStudyItemWidget(d->VisualDICOMBrowser.data()); + studyItemWidget->setStudyItem(studyItem); + studyItemWidget->setPatientID(d->PatientID); + studyItemWidget->setStudyInstanceUID(studyInstanceUID); + if (formattedStudyDate.isEmpty() && studyID.isEmpty()) + { + studyItemWidget->setTitle(tr("Study")); + } + else if (formattedStudyDate.isEmpty()) + { + studyItemWidget->setTitle(tr("Study ID %1").arg(studyID)); + } + else if (studyID.isEmpty()) + { + studyItemWidget->setTitle(tr("Study --- %1").arg(formattedStudyDate)); + } + else + { + studyItemWidget->setTitle(tr("Study ID %1 --- %2").arg(studyID).arg(formattedStudyDate)); + } + + studyItemWidget->setDescription(studyDescription); + studyItemWidget->setNumberOfSeriesPerRow(d->NumberOfSeriesPerRow); + if (this->parentWidget()) + { + studyItemWidget->setThumbnailSize(std::max(int(this->parentWidget()->width() / d->NumberOfSeriesPerRow), d->MinimumThumbnailSize) * 0.94); + } + studyItemWidget->setFilteringSeriesDescription(d->FilteringSeriesDescription); + studyItemWidget->setFilteringModalities(d->FilteringModalities); + studyItemWidget->setDicomDatabase(d->DicomDatabase); + studyItemWidget->setScheduler(d->Scheduler); + // Show in default (and start query/retrieve) only for the first 2 studies + // NOTE: in the layout for each studyItemWidget there is a QSpacerItem + if (d->StudiesListWidget->layout()->count() < d->NumberOfStudiesPerPatient * 2) + { + studyItemWidget->generateSeries(); + } + else + { + studyItemWidget->setCollapsed(true); + this->connect(studyItemWidget->collapsibleGroupBox(), SIGNAL(toggled(bool)), + studyItemWidget, SLOT(generateSeries(bool))); + } + studyItemWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + this->connect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemDoubleClicked(QTableWidgetItem *)), + d->VisualDICOMBrowser.data(), SLOT(onLoad())); + this->connect(studyItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + d->VisualDICOMBrowser.data(), SLOT(showStudyContextMenu(const QPoint&))); + this->connect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemClicked(QTableWidgetItem *)), + this, SLOT(onSeriesItemClicked())); + this->connect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemSelectionChanged()), + this, SLOT(raiseSelectedSeriesJobsPriority())); + + d->StudiesListWidget->layout()->addWidget(studyItemWidget); + d->StudyItemWidgetsList.append(studyItemWidget); +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidget::removeStudyItemWidget(const QString &studyItem) +{ + Q_D(ctkDICOMPatientItemWidget); + + for (int studyIndex = 0; studyIndex < d->StudyItemWidgetsList.size(); ++studyIndex) + { + ctkDICOMStudyItemWidget *studyItemWidget = + qobject_cast(d->StudyItemWidgetsList[studyIndex]); + if (!studyItemWidget || studyItemWidget->studyItem() != studyItem) + { + continue; + } + + this->disconnect(studyItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + d->VisualDICOMBrowser.data(), SLOT(showStudyContextMenu(const QPoint&))); + this->disconnect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemClicked(QTableWidgetItem *)), + this, SLOT(onSeriesItemClicked())); + this->disconnect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemSelectionChanged()), + this, SLOT(raiseSelectedSeriesJobsPriority())); + d->StudyItemWidgetsList.removeOne(studyItemWidget); + delete studyItemWidget; + break; + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::setSelection(bool selected) +{ + Q_D(ctkDICOMPatientItemWidget); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, d->StudyItemWidgetsList) + { + studyItemWidget->setSelection(selected); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::generateStudies() +{ + Q_D(ctkDICOMPatientItemWidget); + + d->createStudies(); + QStringList studiesList = d->DicomDatabase->studiesForPatient(d->PatientItem); + if (studiesList.count() && d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + { + d->Scheduler->queryStudies(d->PatientID, QThread::NormalPriority); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::updateGUIFromScheduler(QVariant data) +{ + Q_D(ctkDICOMPatientItemWidget); + + ctkJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + d->createStudies(); + } + + if (td.JobUID.isEmpty() || + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryStudies || + td.PatientID != d->PatientID) + { + return; + } + + d->createStudies(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::raiseSelectedSeriesJobsPriority() +{ + Q_D(ctkDICOMPatientItemWidget); + + if (!d->Scheduler || d->Scheduler->getNumberOfQueryRetrieveServers() == 0) + { + logger.error("raiseSelectedSeriesJobsPriority failed, no task pool has been set. \n"); + return; + } + + QList seriesWidgets; + QList selectedSeriesWidgets; + foreach (ctkDICOMStudyItemWidget *studyItemWidget, d->StudyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + + QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + for (int row = 0; row < seriesListTableWidget->rowCount(); row++) + { + for (int column = 0; column < seriesListTableWidget->columnCount(); column++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(seriesListTableWidget->cellWidget(row, column)); + seriesWidgets.append(seriesItemWidget); + } + } + + QList selectedItems = seriesListTableWidget->selectedItems(); + foreach (QTableWidgetItem *selectedItem, selectedItems) + { + if (!selectedItem) + { + continue; + } + + int row = selectedItem->row(); + int column = selectedItem->column(); + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(seriesListTableWidget->cellWidget(row, column)); + + selectedSeriesWidgets.append(seriesItemWidget); + } + } + + QStringList selectedSeriesInstanceUIDs; + foreach (ctkDICOMSeriesItemWidget* seriesWidget, seriesWidgets) + { + if (!seriesWidget) + { + continue; + } + + bool widgetIsSelected = selectedSeriesWidgets.contains(seriesWidget); + if (widgetIsSelected) + { + selectedSeriesInstanceUIDs.append(seriesWidget->seriesInstanceUID()); + } + + seriesWidget->setRaiseJobsPriority(widgetIsSelected); + } + + d->Scheduler->raiseJobsPriorityForSeries(selectedSeriesInstanceUIDs); +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::onSeriesItemClicked() +{ + Q_D(ctkDICOMPatientItemWidget); + + QTableWidget* seriesTable = qobject_cast(sender()); + if (!seriesTable) + { + return; + } + + if (QApplication::keyboardModifiers() && + (Qt::ControlModifier || Qt::ShiftModifier)) + { + return; + } + + if (seriesTable->selectedItems().count() != 1) + { + return; + } + + foreach (ctkDICOMStudyItemWidget *studyItemWidget, d->StudyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + + QTableWidget *studySeriesTable = studyItemWidget->seriesListTableWidget(); + if (studySeriesTable == seriesTable) + { + continue; + } + + studySeriesTable->clearSelection(); + } +} diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h new file mode 100644 index 0000000000..4083b8ec99 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -0,0 +1,159 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMPatientItemWidget_h +#define __ctkDICOMPatientItemWidget_h + +#include "ctkDICOMWidgetsExport.h" + +// Qt includes +#include +#include + +class ctkDICOMPatientItemWidgetPrivate; + +class ctkDICOMDatabase; +class ctkDICOMScheduler; +class ctkDICOMStudyItemWidget; + +/// \ingroup DICOM_Widgets +class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget +{ + Q_OBJECT; + Q_ENUMS(DateType) + Q_PROPERTY(QString patientItem READ patientItem WRITE setPatientItem); + Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); + Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); + Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); + Q_PROPERTY(int minimumThumbnailSize READ minimumThumbnailSize WRITE setMinimumThumbnailSize); + +public: + typedef QWidget Superclass; + explicit ctkDICOMPatientItemWidget(QWidget* parent = nullptr); + virtual ~ctkDICOMPatientItemWidget(); + + /// Patient item + void setPatientItem(const QString& patientItem); + QString patientItem() const; + + /// Patient ID + void setPatientID(const QString& patientID); + QString patientID() const; + + /// Query Filters + /// Empty by default + void setFilteringStudyDescription(const QString& filteringStudyDescription); + QString filteringStudyDescription() const; + /// Date filtering enum + enum DateType + { + Any = 0, + Today, + Yesterday, + LastWeek, + LastMonth, + LastYear + }; + + /// Available values: + /// Any, + /// Today, + /// Yesterday, + /// LastWeek, + /// LastMonth, + /// LastYear. + /// Any by default. + void setFilteringDate(const DateType& filteringDate); + DateType filteringDate() const; + /// Empty by default + void setFilteringSeriesDescription(const QString& filteringSeriesDescription); + QString filteringSeriesDescription() const; + /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default + void setFilteringModalities(const QStringList& filteringModalities); + QStringList filteringModalities() const; + + /// Number of non collapsed studies per patient + /// 2 by default + void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); + int numberOfStudiesPerPatient() const; + + /// Number of series displayed per row + /// 6 by default + void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); + int numberOfSeriesPerRow() const; + + /// Minimum thumbnail size in pixel + /// 300 by default + void setMinimumThumbnailSize(int minimumThumbnailSize); + int minimumThumbnailSize() const; + + /// Return the scheduler. + Q_INVOKABLE ctkDICOMScheduler* scheduler() const; + /// Return the scheduler as a shared pointer + /// (not Python-wrappable). + QSharedPointer schedulerShared() const; + /// Set the scheduler. + Q_INVOKABLE void setScheduler(ctkDICOMScheduler& scheduler); + /// Set the scheduler as a shared pointer + /// (not Python-wrappable). + void setScheduler(QSharedPointer scheduler); + + /// Return the Dicom Database. + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + /// Return Dicom Database as a shared pointer + /// (not Python-wrappable). + QSharedPointer dicomDatabaseShared() const; + /// Set the Dicom Database. + Q_INVOKABLE void setDicomDatabase(ctkDICOMDatabase& dicomDatabase); + /// Set the Dicom Database as a shared pointer + /// (not Python-wrappable). + void setDicomDatabase(QSharedPointer dicomDatabase); + + /// Return all the study item widgets for the patient + Q_INVOKABLE QList studyItemWidgetsList()const; + + /// Return number of days from filtering date attribute + Q_INVOKABLE static int getNDaysFromFilteringDate(ctkDICOMPatientItemWidget::DateType filteringDate); + + /// Add/Remove study item widgets + Q_INVOKABLE void addStudyItemWidget(const QString &studyItem); + Q_INVOKABLE void removeStudyItemWidget(const QString &studyItem); + + /// Set selection for all studies/series + Q_INVOKABLE void setSelection(bool selected); + +public Q_SLOTS: + void generateStudies(); + void updateGUIFromScheduler(QVariant data); + void onSeriesItemClicked(); + void raiseSelectedSeriesJobsPriority(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMPatientItemWidget); + Q_DISABLE_COPY(ctkDICOMPatientItemWidget); +}; + +#endif diff --git a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp index 3d9fc778ec..f83388d661 100644 --- a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp @@ -228,11 +228,11 @@ void ctkDICOMQueryRetrieveWidget::query() // create a query for the current server ctkDICOMQuery* query = new ctkDICOMQuery; d->CurrentQuery = query; + query->setConnectionName(parameters["Name"].toString()); query->setCallingAETitle(d->ServerNodeWidget->callingAETitle()); query->setCalledAETitle(parameters["AETitle"].toString()); query->setHost(parameters["Address"].toString()); query->setPort(parameters["Port"].toInt()); - query->setPreferCGET(parameters["CGET"].toBool()); // populate the query with the current search options query->setFilters( d->QueryWidget->parameters() ); @@ -351,21 +351,21 @@ void ctkDICOMQueryRetrieveWidget::retrieve() // Get information which server we want to get the study from and prepare request accordingly QMap::iterator queryIt = d->QueriesByStudyUID.find(studyUID); - ctkDICOMQuery* query = (queryIt == d->QueriesByStudyUID.end() ? nullptr : *queryIt); - if (!query) + ctkDICOMQuery* currentQuery = (queryIt == d->QueriesByStudyUID.end() ? nullptr : *queryIt); + if (!currentQuery) { logger.warn("Retrieve of series " + seriesUID + " failed. No query found for study " + studyUID + "."); continue; } retrieve->setDatabase( d->RetrieveDatabase ); - retrieve->setCallingAETitle( query->callingAETitle() ); - retrieve->setCalledAETitle( query->calledAETitle() ); - retrieve->setPort( query->port() ); - retrieve->setHost( query->host() ); + retrieve->setCallingAETitle( currentQuery->callingAETitle() ); + retrieve->setCalledAETitle( currentQuery->calledAETitle() ); + retrieve->setPort( currentQuery->port() ); + retrieve->setHost( currentQuery->host() ); // TODO: check the model item to see if it is checked // for now, assume all studies queried and shown to the user will be retrieved - logger.debug("About to retrieve " + seriesUID + " from " + query->host()); + logger.debug("About to retrieve " + seriesUID + " from " + currentQuery->host()); logger.info ( "Starting to retrieve" ); if(d->UseProgressDialog) @@ -379,7 +379,18 @@ void ctkDICOMQueryRetrieveWidget::retrieve() try { // perform the retrieve - if ( query->preferCGET() ) + QMap parameters; + foreach(QString server, d->QueriesByServer.keys()) + { + ctkDICOMQuery* query = d->QueriesByServer[server]; + if (query == currentQuery) + { + parameters = d->ServerNodeWidget->serverNodeParameters(server); + break; + } + } + + if ( parameters["CGET"].toBool() ) { retrieve->getSeries ( studyUID, seriesUID ); } @@ -418,9 +429,9 @@ void ctkDICOMQueryRetrieveWidget::retrieve() logger.info ( "Retrieve success" ); } - if (retrieve->database()) + if (retrieve->dicomDatabase()) { - retrieve->database()->updateDisplayedFields(); + retrieve->dicomDatabase()->updateDisplayedFields(); } if(d->UseProgressDialog) diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp new file mode 100644 index 0000000000..0ebef04176 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -0,0 +1,826 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +//Qt includes +#include +#include +#include +#include +#include +#include +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkDICOMThumbnailGenerator.h" + +// ctkDICOMWidgets includes +#include "ctkDICOMSeriesItemWidget.h" +#include "ui_ctkDICOMSeriesItemWidget.h" + +static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMSeriesItemWidget"); + +//---------------------------------------------------------------------------- +class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget +{ + Q_DECLARE_PUBLIC( ctkDICOMSeriesItemWidget ); + +protected: + ctkDICOMSeriesItemWidget* const q_ptr; + +public: + ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesItemWidget& obj); + ~ctkDICOMSeriesItemWidgetPrivate(); + + void init(); + QString getDICOMCenterFrameFromInstances(QStringList instancesList); + void createThumbnail(ctkJobDetail td); + void drawModalityThumbnail(); + void drawThumbnail(const QString& file, int numberOfFrames); + void drawTextWithShadow(QPainter *painter, + const QRect &r, + int flags, + const QString &text); + void updateThumbnailProgressBar(); + + QSharedPointer DicomDatabase; + QSharedPointer Scheduler; + + QString PatientID; + QString SeriesItem; + QString StudyInstanceUID; + QString SeriesInstanceUID; + QString CentralFrameSOPInstanceUID; + QString SeriesNumber; + QString Modality; + bool StopJobs; + bool RaiseJobsPriority; + bool IsCloud; + bool IsLoaded; + bool IsVisible; + int ThumbnailSize; + int NumberOfDownloads; + QImage ThumbnailImage; + bool isThumbnailDocument; +}; + +//---------------------------------------------------------------------------- +// ctkDICOMSeriesItemWidgetPrivate methods + +//---------------------------------------------------------------------------- +ctkDICOMSeriesItemWidgetPrivate::ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesItemWidget& obj) + : q_ptr(&obj) +{ + this->PatientID = ""; + this->SeriesItem = ""; + this->StudyInstanceUID = ""; + this->SeriesInstanceUID = ""; + this->CentralFrameSOPInstanceUID = ""; + this->SeriesNumber = ""; + this->Modality = ""; + + this->IsCloud = false; + this->IsLoaded = false; + this->IsVisible = false; + this->StopJobs = false; + this->RaiseJobsPriority = false; + this->isThumbnailDocument = false; + this->ThumbnailSize = 300; + this->NumberOfDownloads = 0; + + this->DicomDatabase = nullptr; + this->Scheduler = nullptr; +} + +//---------------------------------------------------------------------------- +ctkDICOMSeriesItemWidgetPrivate::~ctkDICOMSeriesItemWidgetPrivate() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::init() +{ + Q_Q(ctkDICOMSeriesItemWidget); + this->setupUi(q); + + this->SeriesThumbnail->setTransformationMode(Qt::TransformationMode::SmoothTransformation); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMSeriesItemWidgetPrivate::getDICOMCenterFrameFromInstances(QStringList instancesList) +{ + if (!this->DicomDatabase) + { + logger.error("getDICOMCenterFrameFromInstances failed, no DICOM Database has been set. \n"); + return ""; + } + + if (instancesList.count() == 0) + { + return ""; + } + + // NOTE: we sort by the instance number. + // We could sort for 3D spatial values (ImagePatientPosition and ImagePatientOrientation), + // plus time information (for 4D datasets). However, this would require additional metadata fetching and logic, which can slow down. + QMap DICOMInstances; + foreach (QString instanceItem, instancesList) + { + int instanceNumber = 0; + QString instanceNumberString = this->DicomDatabase->instanceValue(instanceItem, "0020,0013"); + + if (instanceNumberString != "") + { + instanceNumber = instanceNumberString.toInt(); + } + + DICOMInstances[instanceNumber] = instanceItem; + } + + if (DICOMInstances.count() == 1) + { + return instancesList[0]; + } + + QList keys = DICOMInstances.keys(); + std::sort(keys.begin(), keys.end()); + + int centerFrameIndex = floor(keys.count() / 2); + if (keys.count() <= centerFrameIndex) + { + return instancesList[0]; + } + + int centerInstanceNumber = keys[centerFrameIndex]; + + return DICOMInstances[centerInstanceNumber]; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) +{ + if (!this->DicomDatabase) + { + logger.error("importFiles failed, no DICOM Database has been set. \n"); + return; + } + + ctkDICOMJobResponseSet::JobType typeOfJob = ctkDICOMJobResponseSet::JobType::None; + QString jobSopInstanceUID; + if (!td.JobUID.isEmpty()) + { + jobSopInstanceUID = td.SOPInstanceUID; + typeOfJob = td.TypeOfJob; + } + + QStringList instancesList = this->DicomDatabase->instancesForSeries(this->SeriesInstanceUID); + int numberOfFrames = instancesList.count(); + if (numberOfFrames == 0) + { + this->drawModalityThumbnail(); + return; + } + + QStringList filesList = this->DicomDatabase->filesForSeries(this->SeriesInstanceUID); + filesList.removeAll(QString("")); + int numberOfFiles = filesList.count(); + QStringList urlsList = this->DicomDatabase->urlsForSeries(this->SeriesInstanceUID); + filesList.removeAll(QString("")); + int numberOfUrls = urlsList.count(); + if (!this->IsCloud && numberOfFrames > 0 && numberOfUrls > 0 && numberOfFiles < numberOfFrames) + { + this->IsCloud = true; + this->drawModalityThumbnail(); + } + else if (this->IsCloud && numberOfFrames > 0 && numberOfFiles == numberOfFrames) + { + this->IsCloud = false; + this->SeriesThumbnail->operationProgressBar()->hide(); + } + + if (!this->IsCloud) + { + if(this->DicomDatabase->visibleSeries().contains(this->SeriesInstanceUID)) + { + this->IsVisible = true; + } + else if (this->DicomDatabase->loadedSeries().contains(this->SeriesInstanceUID)) + { + this->IsLoaded = true; + } + else + { + this->IsVisible = false; + this->IsLoaded = false; + } + } + + QString file; + if (this->CentralFrameSOPInstanceUID.isEmpty()) + { + this->CentralFrameSOPInstanceUID = this->getDICOMCenterFrameFromInstances(instancesList); + file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); + + // Since getDICOMCenterFrameFromInstances is based on the sorting of the instance number, + // which is not always reliable, it could fail to get the right central frame. + // In these cases, we check if a frame has been already fetched and we use the first found one. + if (file.isEmpty() && numberOfFiles < numberOfFrames) + { + foreach(QString newFile, filesList) + { + if (file.isEmpty()) + { + continue; + } + + file = newFile; + this->CentralFrameSOPInstanceUID = this->DicomDatabase->instanceForFile(file); + break; + } + } + } + else + { + file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); + } + + if (!this->StopJobs && + this->Scheduler && + this->Scheduler->getNumberOfQueryRetrieveServers() > 0) + { + // Get file for thumbnail + if (file.isEmpty() && + this->IsCloud && + (typeOfJob == ctkDICOMJobResponseSet::JobType::None || + typeOfJob == ctkDICOMJobResponseSet::JobType::QueryInstances)) + { + this->Scheduler->retrieveSOPInstance(this->PatientID, + this->StudyInstanceUID, + this->SeriesInstanceUID, + this->CentralFrameSOPInstanceUID, + this->RaiseJobsPriority ? QThread::HighestPriority : QThread::HighPriority); + + return; + } + + // Get series + if (numberOfFrames > 1 && + this->IsCloud && + ((jobSopInstanceUID == this->CentralFrameSOPInstanceUID && + (typeOfJob == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + typeOfJob == ctkDICOMJobResponseSet::JobType::StoreSOPInstance)) || + (typeOfJob == ctkDICOMJobResponseSet::JobType::None || + typeOfJob == ctkDICOMJobResponseSet::JobType::QueryInstances))) + { + this->Scheduler->retrieveSeries(this->PatientID, + this->StudyInstanceUID, + this->SeriesInstanceUID, + this->RaiseJobsPriority ? QThread::HighestPriority : QThread::LowPriority); + } + } + + file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); + if ((jobSopInstanceUID.isEmpty() || + jobSopInstanceUID == this->CentralFrameSOPInstanceUID || + typeOfJob == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + typeOfJob == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) && + !file.isEmpty()) + { + this->drawThumbnail(file, numberOfFrames); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::drawModalityThumbnail() +{ + if (!this->DicomDatabase) + { + logger.error("drawThumbnail failed, no DICOM Database has been set. \n"); + return; + } + + int margin = 10; + int fontSize = 40; + + QPixmap resultPixmap(this->ThumbnailSize, this->ThumbnailSize); + resultPixmap.fill(Qt::transparent); + ctkDICOMThumbnailGenerator thumbnailGenerator; + thumbnailGenerator.setWidth(this->ThumbnailSize); + thumbnailGenerator.setHeight(this->ThumbnailSize); + + QImage thumbnailImage; + QPainter painter; + + thumbnailGenerator.generateBlankThumbnail(thumbnailImage, Qt::white); + resultPixmap = QPixmap::fromImage(thumbnailImage); + if (painter.begin(&resultPixmap)) + { + painter.setRenderHint(QPainter::Antialiasing); + QRect rect = resultPixmap.rect(); + painter.setFont(QFont("Arial", fontSize, QFont::Bold)); + this->drawTextWithShadow(&painter, rect.adjusted(margin, margin, margin, margin), Qt::AlignCenter, this->Modality); + painter.end(); + } + + this->SeriesThumbnail->setPixmap(resultPixmap); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int numberOfFrames) +{ + if (!this->DicomDatabase) + { + logger.error("drawThumbnail failed, no DICOM Database has been set. \n"); + return; + } + + int margin = 10; + int fontSize = 12; + if (!this->SeriesThumbnail->text().isEmpty()) + { + margin = 5; + fontSize = 14; + } + + QPixmap resultPixmap(this->ThumbnailSize, this->ThumbnailSize); + resultPixmap.fill(Qt::transparent); + ctkDICOMThumbnailGenerator thumbnailGenerator; + thumbnailGenerator.setWidth(this->ThumbnailSize); + thumbnailGenerator.setHeight(this->ThumbnailSize); + bool thumbnailGenerated = true; + bool emptyThumbnailGenerated = false; + QPainter painter; + if (this->ThumbnailImage.width() != this->ThumbnailSize) + { + if (!thumbnailGenerator.generateThumbnail(file, this->ThumbnailImage)) + { + thumbnailGenerated = false; + emptyThumbnailGenerated = true; + this->isThumbnailDocument = true; + thumbnailGenerator.generateBlankThumbnail(this->ThumbnailImage, Qt::white); + resultPixmap = QPixmap::fromImage(this->ThumbnailImage); + if (painter.begin(&resultPixmap)) + { + painter.setRenderHint(QPainter::Antialiasing); + QSvgRenderer renderer(QString(":Icons/text_document.svg")); + renderer.render(&painter); + painter.end(); + } + } + } + + if (thumbnailGenerated && !this->isThumbnailDocument) + { + if (painter.begin(&resultPixmap)) + { + painter.setRenderHint(QPainter::Antialiasing); + QRect rect = resultPixmap.rect(); + painter.setFont(QFont("Arial", fontSize, QFont::Bold)); + int x = int((rect.width() / 2) - (this->ThumbnailImage.rect().width() / 2)); + int y = int((rect.height() / 2) - (this->ThumbnailImage.rect().height() / 2)); + painter.drawPixmap(x, y, QPixmap::fromImage(this->ThumbnailImage)); + QString topLeft = ctkDICOMSeriesItemWidget::tr("Series: %1\n%2").arg(this->SeriesNumber).arg(this->Modality); + this->drawTextWithShadow(&painter, rect.adjusted(margin, margin, margin, margin), Qt::AlignTop | Qt::AlignLeft, topLeft); + QString bottomLeft = ctkDICOMSeriesItemWidget::tr("N.frames: %1").arg(numberOfFrames); + this->drawTextWithShadow(&painter, rect.adjusted(margin, -margin, margin, -margin), Qt::AlignBottom | Qt::AlignLeft, bottomLeft); + QString rows = this->DicomDatabase->instanceValue(this->CentralFrameSOPInstanceUID, "0028,0010"); + QString columns = this->DicomDatabase->instanceValue(this->CentralFrameSOPInstanceUID, "0028,0011"); + QString bottomRight = rows + "x" + columns; + this->drawTextWithShadow(&painter, rect.adjusted(-margin, -margin, -margin, -margin), Qt::AlignBottom | Qt::AlignRight, bottomRight); + QSvgRenderer renderer; + + if (this->IsCloud) + { + if (this->NumberOfDownloads > 0) + { + renderer.load(QString(":Icons/downloading.svg")); + } + else + { + renderer.load(QString(":Icons/cloud.svg")); + } + } + else if (this->IsVisible) + { + renderer.load(QString(":Icons/visible.svg")); + } + else if (this->IsLoaded) + { + renderer.load(QString(":Icons/loaded.svg")); + } + + QPoint topRight = rect.topRight(); + QRectF bounds(topRight.x() - 48 - margin, topRight.y() + margin, 48, 48); + renderer.render(&painter, bounds); + painter.end(); + } + } + + if ((thumbnailGenerated && !this->isThumbnailDocument) || emptyThumbnailGenerated) + { + this->SeriesThumbnail->setPixmap(resultPixmap); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::drawTextWithShadow(QPainter *painter, + const QRect &r, + int flags, const + QString &text) +{ + painter->setPen(Qt::darkGray); + painter->drawText(r.adjusted(1, 1, 1, 1), flags, text); + painter->setPen(QColor(Qt::gray)); + painter->drawText(r.adjusted(2, 2, 2, 2), flags, text); + painter->setPen(QPen(QColor(41, 121, 255))); + painter->drawText(r, flags, text); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::updateThumbnailProgressBar() +{ + if (!this->IsCloud) + { + return; + } + + this->NumberOfDownloads++; + QStringList instancesList = this->DicomDatabase->instancesForSeries(this->SeriesInstanceUID); + int numberOfFrames = instancesList.count(); + float percentageOfInstancesOnLocal = float(this->NumberOfDownloads) / numberOfFrames; + int progress = ceil(percentageOfInstancesOnLocal * 100); + progress = progress > 100 ? 100 : progress; + this->SeriesThumbnail->setOperationProgress(progress); + if (this->NumberOfDownloads == 1) + { + this->SeriesThumbnail->operationProgressBar()->show(); + // change icons + QString file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); + if (!file.isEmpty()) + { + this->drawThumbnail(file, numberOfFrames); + } + } +} + +//---------------------------------------------------------------------------- +// ctkDICOMSeriesItemWidget methods + +//---------------------------------------------------------------------------- +ctkDICOMSeriesItemWidget::ctkDICOMSeriesItemWidget(QWidget* parentWidget) + : Superclass(parentWidget) + , d_ptr(new ctkDICOMSeriesItemWidgetPrivate(*this)) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->init(); +} + +//---------------------------------------------------------------------------- +ctkDICOMSeriesItemWidget::~ctkDICOMSeriesItemWidget() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setSeriesItem(const QString &seriesItem) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->SeriesItem = seriesItem; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMSeriesItemWidget::seriesItem() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->SeriesItem; +} + +//------------------------------------------------------------------------------ +void ctkDICOMSeriesItemWidget::setPatientID(const QString &patientID) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->PatientID = patientID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::patientID() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->PatientID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setStudyInstanceUID(const QString& studyInstanceUID) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->StudyInstanceUID = studyInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::studyInstanceUID() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->StudyInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setSeriesInstanceUID(const QString& seriesInstanceUID) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->SeriesInstanceUID = seriesInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::seriesInstanceUID() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->SeriesInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setSeriesNumber(const QString& seriesNumber) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->SeriesNumber = seriesNumber; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::seriesNumber() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->SeriesNumber; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setModality(const QString& modality) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->Modality = modality; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::modality() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->Modality; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setSeriesDescription(const QString& seriesDescription) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->SeriesThumbnail->setText(seriesDescription); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMSeriesItemWidget::seriesDescription() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->SeriesThumbnail->text(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setStopJobs(const bool &stopJobs) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->StopJobs = stopJobs; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::stopJobs() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->StopJobs; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setRaiseJobsPriority(const bool &raiseJobsPriority) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->RaiseJobsPriority = raiseJobsPriority; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::raiseJobsPriority() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->RaiseJobsPriority; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::isCloud() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->IsCloud; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::IsLoaded() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->IsLoaded; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::IsVisible() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->IsVisible; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setThumbnailSize(int thumbnailSize) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->ThumbnailSize = thumbnailSize; +} + +//------------------------------------------------------------------------------ +int ctkDICOMSeriesItemWidget::thumbnailSize() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->ThumbnailSize; +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMScheduler* ctkDICOMSeriesItemWidget::scheduler()const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMSeriesItemWidget::schedulerShared()const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setScheduler(ctkDICOMScheduler& scheduler) +{ + Q_D(ctkDICOMSeriesItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateSeriesProgressBar(QVariant))); + } + + d->Scheduler = QSharedPointer(&scheduler, skipDelete); + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateSeriesProgressBar(QVariant))); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setScheduler(QSharedPointer scheduler) +{ + Q_D(ctkDICOMSeriesItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateSeriesProgressBar(QVariant))); + } + + d->Scheduler = scheduler; + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateSeriesProgressBar(QVariant))); + } +} + +//---------------------------------------------------------------------------- +ctkDICOMDatabase* ctkDICOMSeriesItemWidget::dicomDatabase()const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->DicomDatabase.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMSeriesItemWidget::dicomDatabaseShared()const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->DicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setDicomDatabase(ctkDICOMDatabase& dicomDatabase) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->DicomDatabase = QSharedPointer(&dicomDatabase, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setDicomDatabase(QSharedPointer dicomDatabase) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->DicomDatabase = dicomDatabase; +} + +//------------------------------------------------------------------------------ +void ctkDICOMSeriesItemWidget::generateInstances() +{ + Q_D(ctkDICOMSeriesItemWidget); + if (!d->DicomDatabase) + { + logger.error("generateInstances failed, no DICOM Database has been set. \n"); + return; + } + + ctkJobDetail td; + d->createThumbnail(td); + QStringList instancesList = d->DicomDatabase->instancesForSeries(d->SeriesInstanceUID); + if (!d->StopJobs && + instancesList.count() == 0 && + d->Scheduler && + d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + { + d->Scheduler->queryInstances(d->PatientID, + d->StudyInstanceUID, + d->SeriesInstanceUID, + d->RaiseJobsPriority ? QThread::HighestPriority : QThread::NormalPriority); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(QVariant data) +{ + Q_D(ctkDICOMSeriesItemWidget); + + ctkJobDetail td = data.value(); + if (td.JobUID.isEmpty() || + (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryInstances && + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSeries && + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance&& + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || + td.StudyInstanceUID != d->StudyInstanceUID || + td.SeriesInstanceUID != d->SeriesInstanceUID) + { + return; + } + + d->createThumbnail(td); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(QVariant data) +{ + Q_D(ctkDICOMSeriesItemWidget); + + ctkJobDetail td = data.value(); + if (td.JobUID.isEmpty() || + (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSeries && + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || + td.StudyInstanceUID != d->StudyInstanceUID || + td.SeriesInstanceUID != d->SeriesInstanceUID) + { + return; + } + + d->updateThumbnailProgressBar(); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h new file mode 100644 index 0000000000..b01d42de26 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -0,0 +1,143 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMSeriesItemWidget_h +#define __ctkDICOMSeriesItemWidget_h + +#include "ctkDICOMWidgetsExport.h" + +// Qt includes +#include +#include + +class ctkDICOMSeriesItemWidgetPrivate; +class ctkDICOMDatabase; +class ctkDICOMScheduler; + +/// \ingroup DICOM_Widgets +class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget +{ + Q_OBJECT; + Q_PROPERTY(QString seriesItem READ seriesItem WRITE setSeriesItem); + Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); + Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); + Q_PROPERTY(QString seriesInstanceUID READ seriesInstanceUID WRITE setSeriesInstanceUID); + Q_PROPERTY(QString seriesNumber READ seriesNumber WRITE setSeriesNumber); + Q_PROPERTY(QString modality READ modality WRITE setModality); + Q_PROPERTY(QString seriesDescription READ seriesDescription WRITE setSeriesDescription); + Q_PROPERTY(QString isCloud READ isCloud); + Q_PROPERTY(int thumbnailSize READ thumbnailSize WRITE setThumbnailSize); + Q_PROPERTY(bool stopJobs READ stopJobs WRITE setStopJobs); + Q_PROPERTY(bool raiseJobsPriority READ raiseJobsPriority WRITE setRaiseJobsPriority); + +public: + typedef QWidget Superclass; + explicit ctkDICOMSeriesItemWidget(QWidget* parent = nullptr); + virtual ~ctkDICOMSeriesItemWidget(); + + /// Series Item + void setSeriesItem(const QString& seriesItem); + QString seriesItem() const; + + /// Patient ID + void setPatientID(const QString& patientID); + QString patientID() const; + + /// Study instance UID + void setStudyInstanceUID(const QString& studyInstanceUID); + QString studyInstanceUID() const; + + /// Series instance UID + void setSeriesInstanceUID(const QString& seriesInstanceUID); + QString seriesInstanceUID() const; + + /// Series Number + void setSeriesNumber(const QString& seriesNumber); + QString seriesNumber() const; + + /// Modality + void setModality(const QString& modality); + QString modality() const; + + /// Series Description + void setSeriesDescription(const QString& seriesDescription); + QString seriesDescription() const; + + /// Stop Series widget to run new jobs + void setStopJobs(const bool& stopJobs); + bool stopJobs() const; + + /// Set high priority to all jobs run from the Series widget + void setRaiseJobsPriority(const bool& raiseJobsPriority); + bool raiseJobsPriority() const; + + /// Series lives in the server + bool isCloud() const; + + /// Series has been loaded by the parent widget + bool IsLoaded() const; + + /// Series is visible in the parent widget + bool IsVisible() const; + + /// Series Thumbnail size + /// 300 px by default + void setThumbnailSize(int thumbnailSize); + int thumbnailSize() const; + + /// Return the scheduler. + Q_INVOKABLE ctkDICOMScheduler* scheduler() const; + /// Return the scheduler as a shared pointer + /// (not Python-wrappable). + QSharedPointer schedulerShared() const; + /// Set the scheduler. + Q_INVOKABLE void setScheduler(ctkDICOMScheduler& scheduler); + /// Set the scheduler as a shared pointer + /// (not Python-wrappable). + void setScheduler(QSharedPointer scheduler); + + /// Return the Dicom Database. + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + /// Return Dicom Database as a shared pointer + /// (not Python-wrappable). + QSharedPointer dicomDatabaseShared() const; + /// Set the Dicom Database. + Q_INVOKABLE void setDicomDatabase(ctkDICOMDatabase& dicomDatabase); + /// Set the Dicom Database as a shared pointer + /// (not Python-wrappable). + void setDicomDatabase(QSharedPointer dicomDatabase); + +public Q_SLOTS: + void generateInstances(); + void updateGUIFromScheduler(QVariant data); + void updateSeriesProgressBar(QVariant data); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMSeriesItemWidget); + Q_DISABLE_COPY(ctkDICOMSeriesItemWidget); +}; + +#endif diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp new file mode 100644 index 0000000000..09047d83d1 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -0,0 +1,1308 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// CTK includes +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMEcho.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMScheduler.h" + +// ctkDICOMWidgets includes +#include "ctkDICOMServerNodeWidget2.h" +#include "ui_ctkDICOMServerNodeWidget2.h" + +static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMServerNodeWidget2"); + +class QCenteredStyledItemDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override + { + QStyleOptionViewItem opt = option; + const QWidget *widget = option.widget; + initStyleOption(&opt, index); + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); + if (opt.features & QStyleOptionViewItem::HasCheckIndicator) + { + switch (opt.checkState) + { + case Qt::Unchecked: + opt.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + opt.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + opt.state |= QStyle::State_On; + break; + } + auto rect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget); + opt.rect = QStyle::alignedRect(opt.direction, Qt::AlignCenter, rect.size(), opt.rect); + opt.state = opt.state & ~QStyle::State_HasFocus; + style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget); + } + else if (!opt.icon.isNull()) + { + // draw the icon + QRect iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, widget); + iconRect = QStyle::alignedRect(opt.direction, Qt::AlignCenter, iconRect.size(), opt.rect); + QIcon::Mode mode = QIcon::Normal; + if (!(opt.state & QStyle::State_Enabled)) + { + mode = QIcon::Disabled; + } + else if (opt.state & QStyle::State_Selected) + { + mode = QIcon::Selected; + } + QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + opt.icon.paint(painter, iconRect, opt.decorationAlignment, mode, state); + } + else + { + QStyledItemDelegate::paint(painter, option, index); + } + } +protected: + bool editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) override + { + Q_ASSERT(event); + Q_ASSERT(model); + // make sure that the item is checkable + Qt::ItemFlags flags = model->flags(index); + if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) || + !(flags & Qt::ItemIsEnabled)) + { + return false; + } + // make sure that we have a check state + QVariant value = index.data(Qt::CheckStateRole); + if (!value.isValid()) + { + return false; + } + const QWidget *widget = option.widget; + QStyle *style = option.widget ? widget->style() : QApplication::style(); + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonDblClick) || + (event->type() == QEvent::MouseButtonPress)) + { + QStyleOptionViewItem viewOpt(option); + initStyleOption(&viewOpt, index); + QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget); + checkRect = QStyle::alignedRect(viewOpt.direction, Qt::AlignCenter, checkRect.size(), viewOpt.rect); + QMouseEvent *me = static_cast(event); + if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) + { + return false; + } + if ((event->type() == QEvent::MouseButtonPress) || (event->type() == QEvent::MouseButtonDblClick)) + { + return true; + } + } + else if (event->type() == QEvent::KeyPress) + { + if (static_cast(event)->key() != Qt::Key_Space && + static_cast(event)->key() != Qt::Key_Select) + { + return false; + } + } + else + { + return false; + } + Qt::CheckState state = static_cast(value.toInt()); + if (flags & Qt::ItemIsUserTristate) + { + state = (static_cast((state + 1) % 3)); + } + else + { + state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; + } + return model->setData(index, state, Qt::CheckStateRole); + } +}; + +//---------------------------------------------------------------------------- +class ctkDICOMServerNodeWidget2Private: public Ui_ctkDICOMServerNodeWidget2 +{ + Q_DECLARE_PUBLIC(ctkDICOMServerNodeWidget2); + +protected: + ctkDICOMServerNodeWidget2* const q_ptr; + +public: + ctkDICOMServerNodeWidget2Private(ctkDICOMServerNodeWidget2& obj); + ~ctkDICOMServerNodeWidget2Private(); + + void init(); + void disconnectScheduler(); + void connectScheduler(); + /// Utility function that returns the storageAETitle and + /// storagePort in a map + QMap parameters()const; + + /// Return the list of server names + QStringList serverNodes()const; + /// Return all the information associated to a server defined by its name + QMap serverNodeParameters(const QString &connectionName) const; + QMap serverNodeParameters(int row) const; + QStringList getAllNodesName() const; + int getServerNodeRowFromConnectionName(const QString &connectionName) const; + QString getServerNodeConnectionNameFromRow(int row) const; + + /// Add a server node with the given parameters + /// Return the row index added into the table + int addServerNode(const QMap& parameters); + int addServerNode(ctkDICOMServer* server); + QSharedPointer createServerFromServerNode(const QMap& node); + void updateProxyComboBoxes(const QString &connectionName, int rowCount) const; + + bool SettingsModified; + QSharedPointer Scheduler; + QPushButton *SaveButton; + QPushButton *RestoreButton; +}; + +//---------------------------------------------------------------------------- +ctkDICOMServerNodeWidget2Private::ctkDICOMServerNodeWidget2Private(ctkDICOMServerNodeWidget2& obj) + : q_ptr(&obj) +{ + this->SettingsModified = false; + this->Scheduler = nullptr; + this->RestoreButton = nullptr; + this->SaveButton = nullptr; +} + +//---------------------------------------------------------------------------- +ctkDICOMServerNodeWidget2Private::~ctkDICOMServerNodeWidget2Private() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2Private::init() +{ + Q_Q(ctkDICOMServerNodeWidget2); + + this->setupUi(q); + + // checkable headers. + QHeaderView* previousHeaderView = this->NodeTable->horizontalHeader(); + ctkCheckableHeaderView* headerView = new ctkCheckableHeaderView(Qt::Horizontal, this->NodeTable); + headerView->setSectionsClickable(false); + headerView->setSectionsMovable(false); + headerView->setHighlightSections(false); + headerView->checkableModelHelper()->setPropagateDepth(-1); + headerView->setStretchLastSection(previousHeaderView->stretchLastSection()); + headerView->setMinimumSectionSize(previousHeaderView->minimumSectionSize()); + headerView->setDefaultSectionSize(previousHeaderView->defaultSectionSize()); + this->NodeTable->setHorizontalHeader(headerView); + + this->TestButton->setEnabled(false); + this->RemoveButton->setEnabled(false); + + this->NodeTable->setItemDelegateForColumn(ctkDICOMServerNodeWidget2::QueryRetrieveColumn, + new QCenteredStyledItemDelegate()); + this->NodeTable->setItemDelegateForColumn(ctkDICOMServerNodeWidget2::StorageColumn, + new QCenteredStyledItemDelegate()); + + QIntValidator *validator = new QIntValidator(0, INT_MAX); + this->StoragePort->setValidator(validator); + + q->readSettings(); + + QObject::connect(this->StorageEnabledCheckBox, SIGNAL(stateChanged(int)), + q, SLOT(onSettingsModified())); + QObject::connect(this->StorageAETitle, SIGNAL(textChanged(QString)), + q, SLOT(onSettingsModified())); + QObject::connect(this->StoragePort, SIGNAL(textChanged(QString)), + q, SLOT(onSettingsModified())); + + QObject::connect(this->NodeTable, SIGNAL(cellChanged(int,int)), + q, SLOT(onSettingsModified())); + QObject::connect(this->NodeTable, SIGNAL(itemSelectionChanged()), + q, SLOT(updateGUIState())); + + QObject::connect(this->AddButton, SIGNAL(clicked()), + q, SLOT(onAddServerNode())); + QObject::connect(this->TestButton, SIGNAL(clicked()), + q, SLOT(onTestCurrentServerNode())); + QObject::connect(this->RemoveButton, SIGNAL(clicked()), + q, SLOT(onRemoveCurrentServerNode())); + this->SaveButton = this->buttonBox->button(QDialogButtonBox::StandardButton::Save); + this->SaveButton->setStyleSheet("text-align:left;"); + this->SaveButton->setText(QObject::tr("Apply changes")); + this->RestoreButton = this->buttonBox->button(QDialogButtonBox::StandardButton::Discard); + this->SaveButton->setStyleSheet("text-align:left;"); + this->RestoreButton->setText(QObject::tr("Discard changes")); + QObject::connect(this->RestoreButton, SIGNAL(clicked()), + q, SLOT(readSettings())); + QObject::connect(this->SaveButton, SIGNAL(clicked()), + q, SLOT(saveSettings())); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2Private::disconnectScheduler() +{ + Q_Q(ctkDICOMServerNodeWidget2); + if (!this->Scheduler) + { + return; + } + + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QString, QString)), + q, SLOT(updateGUIState())); + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QString, QString)), + q, SLOT(updateGUIState())); + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), + q, SLOT(updateGUIState())); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2Private::connectScheduler() +{ + Q_Q(ctkDICOMServerNodeWidget2); + if (!this->Scheduler) + { + return; + } + + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobStarted(QString, QString)), + q, SLOT(updateGUIState())); + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFinished(QString, QString)), + q, SLOT(updateGUIState())); + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), + q, SLOT(updateGUIState())); +} + +//---------------------------------------------------------------------------- +QMap ctkDICOMServerNodeWidget2Private::parameters()const +{ + Q_Q(const ctkDICOMServerNodeWidget2); + QMap parameters; + + parameters["StorageAETitle"] = this->StorageAETitle->text(); + parameters["StoragePort"] = q->storagePort(); + + return parameters; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMServerNodeWidget2Private::serverNodes()const +{ + QStringList nodes; + const int count = this->NodeTable->rowCount(); + for (int row = 0; row < count; ++row) + { + QTableWidgetItem* item = this->NodeTable->item(row, ctkDICOMServerNodeWidget2::NameColumn); + nodes << (item ? item->text() : QString("")); + } + // If there are duplicates, serverNodeParameters(QString) will behave + // strangely + Q_ASSERT(nodes.removeDuplicates() == 0); + return nodes; +} + +//---------------------------------------------------------------------------- +QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(const QString &connectionName)const +{ + QMap parameters; + const int count = this->NodeTable->rowCount(); + for (int row = 0; row < count; ++row) + { + if (this->NodeTable->item(row, 0)->text() == connectionName) + { + return this->serverNodeParameters(row); + } + } + + return parameters; +} + +//---------------------------------------------------------------------------- +QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(int row) const +{ + QMap node; + if (row < 0 || row >= this->NodeTable->rowCount()) + { + return node; + } + const int columnCount = this->NodeTable->columnCount(); + for (int column = 0; column < columnCount; ++column) + { + if (!this->NodeTable->item(row, column)) + { + continue; + } + QString label = this->NodeTable->horizontalHeaderItem(column)->text(); + node[label] = this->NodeTable->item(row, column)->data(Qt::DisplayRole); + } + node["QueryRetrieveCheckState"] = this->NodeTable->item(row, ctkDICOMServerNodeWidget2::QueryRetrieveColumn) ? + this->NodeTable->item(row, ctkDICOMServerNodeWidget2::QueryRetrieveColumn)->checkState() : + static_cast(Qt::Unchecked); + node["StorageCheckState"] = this->NodeTable->item(row, ctkDICOMServerNodeWidget2::StorageColumn) ? + this->NodeTable->item(row, ctkDICOMServerNodeWidget2::StorageColumn)->checkState() : + static_cast(Qt::Unchecked); + + QLineEdit *portLineEdit = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::PortColumn)); + if (portLineEdit) + { + node["Port"] = portLineEdit->text(); + } + QSpinBox *timeoutSpinBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::TimeoutColumn)); + if (timeoutSpinBox) + { + node["Timeout"] = timeoutSpinBox->value(); + } + QComboBox *protocolComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProtocolColumn)); + if (protocolComboBox) + { + node["Protocol"] = protocolComboBox->currentText(); + } + QComboBox *proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); + if (proxyComboBox) + { + node["Proxy"] = proxyComboBox->currentText(); + } + + return node; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMServerNodeWidget2Private::getAllNodesName() const +{ + QStringList nodesNames; + const int count = this->NodeTable->rowCount(); + for (int row = 0; row < count; ++row) + { + nodesNames.append(this->NodeTable->item(row, ctkDICOMServerNodeWidget2::NameColumn)->data(Qt::DisplayRole).toString()); + } + + return nodesNames; +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2Private::getServerNodeRowFromConnectionName(const QString &connectionName) const +{ + QMap parameters; + const int count = this->NodeTable->rowCount(); + for (int row = 0; row < count; ++row) + { + if (this->NodeTable->item(row, 0)->text() == connectionName) + { + return row; + } + } + + return -1; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMServerNodeWidget2Private::getServerNodeConnectionNameFromRow(int row) const +{ + if (row < 0 || row >= this->NodeTable->rowCount()) + { + return ""; + } + + return this->NodeTable->item(row, 0)->text(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2Private::addServerNode(const QMap& node) +{ + Q_Q(ctkDICOMServerNodeWidget2); + + if (this->getServerNodeRowFromConnectionName(node["Name"].toString()) != -1) + { + logger.debug("addServerNode failed: the server has a duplicate. The connection name has to be unique \n"); + return -1; + } + + const int rowCount = this->NodeTable->rowCount(); + this->NodeTable->setRowCount(rowCount + 1); + + QTableWidgetItem *newItem; + QString serverName = node["Name"].toString(); + newItem = new QTableWidgetItem(serverName); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::NameColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(Qt::CheckState(node["QueryRetrieveCheckState"].toInt())); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::QueryRetrieveColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(Qt::CheckState(node["StorageCheckState"].toInt())); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::StorageColumn, newItem); + + newItem = new QTableWidgetItem(node["Calling AETitle"].toString()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CallingAETitleColumn, newItem); + + newItem = new QTableWidgetItem(node["Called AETitle"].toString()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CalledAETitleColumn, newItem); + + newItem = new QTableWidgetItem(node["Address"].toString()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QLineEdit *portLineEdit = new QLineEdit(); + QIntValidator *validator = new QIntValidator(0, INT_MAX); + portLineEdit->setValidator(validator); + + portLineEdit->setObjectName("portLineEdit"); + portLineEdit->setText(node["Port"].toString()); + portLineEdit->setAlignment(Qt::AlignHCenter); + QObject::connect(portLineEdit, SIGNAL(textChanged(QString)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::PortColumn, portLineEdit); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QComboBox *protocolComboBox = new QComboBox(); + protocolComboBox->setObjectName("protocolComboBox"); + protocolComboBox->addItem("CGET"); + protocolComboBox->addItem("CMOVE"); + // To Do: protocolComboBox->addItem("WADO"); + protocolComboBox->setCurrentIndex(protocolComboBox->findText(node["Protocol"].toString())); + QObject::connect(protocolComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, newItem); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); + + newItem = new QTableWidgetItem(QString("")); + QSpinBox *timeoutSpinBox = new QSpinBox(); + timeoutSpinBox->setObjectName("timeoutSpinBox"); + timeoutSpinBox->setValue(node["Timeout"].toInt()); + timeoutSpinBox->setMinimum(1); + timeoutSpinBox->setMaximum(INT_MAX); + timeoutSpinBox->setSingleStep(1); + timeoutSpinBox->setSuffix(" s"); + timeoutSpinBox->setAlignment(Qt::AlignHCenter); + QObject::connect(timeoutSpinBox, SIGNAL(valueChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, timeoutSpinBox); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QComboBox *proxyComboBox = new QComboBox(); + proxyComboBox->setObjectName("proxyComboBox"); + QStringListModel* cbModel = new QStringListModel(); + proxyComboBox->setModel(cbModel); + + proxyComboBox->addItem(""); + QStringList nodesNames = this->getAllNodesName(); + nodesNames.removeOne(serverName); + QString proxyName = node["Proxy"].toString(); + if (!nodesNames.contains(proxyName) && !proxyName.isEmpty()) + { + nodesNames.append(proxyName); + } + proxyComboBox->addItems(nodesNames); + proxyComboBox->setCurrentIndex(proxyComboBox->findText(node["Proxy"].toString())); + QObject::connect(proxyComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, proxyComboBox); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, newItem); + + q->onSettingsModified(); + + this->updateProxyComboBoxes(serverName, rowCount); + + return rowCount; +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) +{ + Q_Q(ctkDICOMServerNodeWidget2); + + if (!server) + { + return -1; + } + + if (this->getServerNodeRowFromConnectionName(server->connectionName()) != -1) + { + logger.debug("addServerNode failed: the server has a duplicate. The connection name has to be unique \n"); + return -1; + } + + int rowCount = this->NodeTable->rowCount(); + this->NodeTable->setRowCount(rowCount + 1); + + QTableWidgetItem *newItem; + newItem = new QTableWidgetItem(server->connectionName()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::NameColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(server->queryRetrieveEnabled() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::QueryRetrieveColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(server->storageEnabled() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::StorageColumn, newItem); + + newItem = new QTableWidgetItem(server->callingAETitle()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CallingAETitleColumn, newItem); + + newItem = new QTableWidgetItem(server->calledAETitle()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CalledAETitleColumn, newItem); + + newItem = new QTableWidgetItem(server->host()); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QLineEdit *portLineEdit = new QLineEdit(); + QIntValidator *validator = new QIntValidator(0, INT_MAX); + portLineEdit->setValidator(validator); + + portLineEdit->setObjectName("portLineEdit"); + portLineEdit->setText(QString::number(server->port())); + portLineEdit->setAlignment(Qt::AlignHCenter); + QObject::connect(portLineEdit, SIGNAL(textChanged(QString)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::PortColumn, portLineEdit); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QComboBox *protocolComboBox = new QComboBox(); + protocolComboBox->addItem("CGET"); + protocolComboBox->addItem("CMOVE"); + protocolComboBox->setCurrentIndex(protocolComboBox->findText(server->retrieveProtocolAsString())); + // To Do: protocolComboBox->addItem("WADO"); + QObject::connect(protocolComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, newItem); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); + + newItem = new QTableWidgetItem(QString("")); + QSpinBox *timeoutSpinBox = new QSpinBox(); + timeoutSpinBox->setObjectName("timeoutSpinBox"); + timeoutSpinBox->setValue(server->connectionTimeout()); + timeoutSpinBox->setMinimum(1); + timeoutSpinBox->setMaximum(INT_MAX); + timeoutSpinBox->setSingleStep(1); + timeoutSpinBox->setSuffix(" s"); + timeoutSpinBox->setAlignment(Qt::AlignHCenter); + QObject::connect(timeoutSpinBox, SIGNAL(valueChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, timeoutSpinBox); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, newItem); + + + newItem = new QTableWidgetItem(QString("")); + QComboBox *proxyComboBox = new QComboBox(); + QStringListModel* cbModel = new QStringListModel(); + proxyComboBox->setModel(cbModel); + + proxyComboBox->addItem(""); + QStringList nodesNames = this->getAllNodesName(); + nodesNames.removeOne(server->connectionName()); + + if (server->proxyServer()) + { + QString proxyName = server->proxyServer()->connectionName(); + if (!nodesNames.contains(proxyName)) + { + nodesNames.append(proxyName); + } + } + proxyComboBox->addItems(nodesNames); + if (server->proxyServer()) + { + QString proxyName = server->proxyServer()->connectionName(); + proxyComboBox->setCurrentIndex(proxyComboBox->findText(proxyName)); + } + + QObject::connect(proxyComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onSettingsModified())); + this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, proxyComboBox); + this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, newItem); + + this->updateProxyComboBoxes(server->connectionName(), rowCount); + + if (server->proxyServer()) + { + this->addServerNode(server->proxyServer()); + rowCount++; + } + + q->onSettingsModified(); + + return rowCount; +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMServerNodeWidget2Private::createServerFromServerNode(const QMap &node) +{ + QSharedPointer server = + QSharedPointer(new ctkDICOMServer); + server->setConnectionName(node["Name"].toString()); + server->setQueryRetrieveEnabled(node["QueryRetrieveCheckState"].toInt() == 0 ? false : true); + server->setStorageEnabled(node["StorageCheckState"].toInt() == 0 ? false : true); + server->setCallingAETitle(node["Calling AETitle"].toString()); + server->setCalledAETitle(node["Called AETitle"].toString()); + server->setHost(node["Address"].toString()); + server->setPort(node["Port"].toInt()); + server->setRetrieveProtocolAsString(node["Protocol"].toString()); + server->setConnectionTimeout(node["Timeout"].toInt()); + server->setMoveDestinationAETitle(this->StorageAETitle->text()); + + return server; +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2Private::updateProxyComboBoxes(const QString &connectionName, int rowCount) const +{ + for (int row = 0; row < rowCount; ++row) + { + QComboBox *proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); + if (proxyComboBox) + { + QStringListModel* cbModel = qobject_cast(proxyComboBox->model()); + if (cbModel) + { + QStringList nodesNames = cbModel->stringList(); + if (nodesNames.contains(connectionName)) + { + continue; + } + } + proxyComboBox->addItem(connectionName); + } + } +} + +//---------------------------------------------------------------------------- +ctkDICOMServerNodeWidget2::ctkDICOMServerNodeWidget2(QWidget* parentWidget) + : Superclass(parentWidget) + , d_ptr(new ctkDICOMServerNodeWidget2Private(*this)) +{ + Q_D(ctkDICOMServerNodeWidget2); + + d->init(); +} + + +//---------------------------------------------------------------------------- +ctkDICOMServerNodeWidget2::~ctkDICOMServerNodeWidget2() +{ +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2::onAddServerNode() +{ + Q_D(ctkDICOMServerNodeWidget2); + const int rowCount = d->NodeTable->rowCount(); + d->NodeTable->setRowCount(rowCount + 1); + + QString serverName = "server"; + QTableWidgetItem *newItem = new QTableWidgetItem(serverName); + d->NodeTable->setItem(rowCount, NameColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(Qt::Unchecked); + d->NodeTable->setItem(rowCount, QueryRetrieveColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + newItem->setCheckState(Qt::Unchecked); + d->NodeTable->setItem(rowCount, StorageColumn, newItem); + + newItem = new QTableWidgetItem(); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CallingAETitleColumn, newItem); + + newItem = new QTableWidgetItem(); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::CalledAETitleColumn, newItem); + + newItem = new QTableWidgetItem(); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QLineEdit *portLineEdit = new QLineEdit(); + QIntValidator *validator = new QIntValidator(0, INT_MAX); + portLineEdit->setValidator(validator); + + portLineEdit->setObjectName("portLineEdit"); + portLineEdit->setText("80"); + portLineEdit->setAlignment(Qt::AlignHCenter); + QObject::connect(portLineEdit, SIGNAL(textChanged(QString)), + this, SLOT(onSettingsModified())); + d->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::PortColumn, portLineEdit); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QComboBox *protocolComboBox = new QComboBox(); + protocolComboBox->setObjectName("protocolComboBox"); + protocolComboBox->addItem("CGET"); + protocolComboBox->addItem("CMOVE"); + // To Do: protocolComboBox->addItem("WADO"); + protocolComboBox->setCurrentIndex(protocolComboBox->findText("CGET")); + QObject::connect(protocolComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(onSettingsModified())); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, newItem); + d->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); + + newItem = new QTableWidgetItem(QString("")); + QSpinBox *timeoutSpinBox = new QSpinBox(); + timeoutSpinBox->setObjectName("timeoutSpinBox"); + timeoutSpinBox->setValue(10); + timeoutSpinBox->setMinimum(1); + timeoutSpinBox->setMaximum(INT_MAX); + timeoutSpinBox->setSingleStep(1); + timeoutSpinBox->setSuffix(" s"); + timeoutSpinBox->setAlignment(Qt::AlignHCenter); + QObject::connect(timeoutSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(onSettingsModified())); + d->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, timeoutSpinBox); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, newItem); + + newItem = new QTableWidgetItem(QString("")); + QComboBox *proxyComboBox = new QComboBox(); + proxyComboBox->setObjectName("proxyComboBox"); + QStringListModel* cbModel = new QStringListModel(); + proxyComboBox->setModel(cbModel); + + proxyComboBox->addItem(""); + QStringList nodesNames = d->getAllNodesName(); + nodesNames.removeOne(serverName); + proxyComboBox->addItems(nodesNames); + proxyComboBox->setCurrentIndex(-1); + QObject::connect(proxyComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(onSettingsModified())); + d->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, proxyComboBox); + d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::ProxyColumn, newItem); + + d->NodeTable->setCurrentCell(rowCount, NameColumn); + + this->onSettingsModified(); + + return rowCount; +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::onRemoveCurrentServerNode() +{ + Q_D(ctkDICOMServerNodeWidget2); + + QModelIndexList selection = d->NodeTable->selectionModel()->selectedRows(); + if (selection.count() == 0) + { + return; + } + + QModelIndex index = selection.at(0); + int row = index.row(); + d->NodeTable->removeRow(row); + this->onSettingsModified(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::onTestCurrentServerNode() +{ + Q_D(ctkDICOMServerNodeWidget2); + + QModelIndexList selection = d->NodeTable->selectionModel()->selectedRows(); + if (selection.count() == 0) + { + return; + } + + QModelIndex index = selection.at(0); + QString serverName = d->getServerNodeConnectionNameFromRow(index.row()); + ctkDICOMServer* server = this->getServer(serverName.toStdString().c_str()); + if (!server) + { + return; + } + + ctkDICOMEcho echo; + echo.setConnectionName(server->connectionName()); + echo.setCalledAETitle(server->calledAETitle()); + echo.setCallingAETitle(server->callingAETitle()); + echo.setHost(server->host()); + echo.setPort(server->port()); + echo.setConnectionTimeout(server->connectionTimeout()); + + ctkMessageBox echoMessageBox(this); + QString messageString; + if (echo.echo()) + { + messageString = tr("Node response was positive."); + echoMessageBox.setIcon(QMessageBox::Information); + } + else + { + messageString = tr("Node response was negative."); + echoMessageBox.setIcon(QMessageBox::Warning); + } + + echoMessageBox.setText(messageString); + echoMessageBox.exec(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::updateGUIState() +{ + Q_D(ctkDICOMServerNodeWidget2); + QList selectedItems = d->NodeTable->selectedItems(); + d->RemoveButton->setEnabled(selectedItems.count() > 0); + d->TestButton->setEnabled(selectedItems.count() > 0); + + if (d->RestoreButton && d->SaveButton) + { + d->RestoreButton->setEnabled(d->SettingsModified); + d->SaveButton->setEnabled(d->SettingsModified); + } + + if (d->Scheduler && d->Scheduler->isStorageListenerActive()) + { + d->StorageStatusValueLabel->setText(QObject::tr("Active")); + } + else + { + d->StorageStatusValueLabel->setText(QObject::tr("Inactive")); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::onSettingsModified() +{ + Q_D(ctkDICOMServerNodeWidget2); + d->SettingsModified = true; + this->updateGUIState(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::saveSettings() +{ + Q_D(ctkDICOMServerNodeWidget2); + + if (!d->Scheduler) + { + return; + } + + QSettings settings; + const int rowCount = d->NodeTable->rowCount(); + + settings.remove("DICOM/ServerNodes"); + this->removeAllServers(); + this->stopAllJobs(); + + settings.setValue("DICOM/ServerNodeCount", rowCount); + + QStringList proxyServers; + for (int row = 0; row < rowCount; ++row) + { + QMap node = d->serverNodeParameters(row); + QString proxyName = node["Proxy"].toString(); + if (!proxyName.isEmpty() && node["QueryRetrieveCheckState"].toInt() > 0) + { + proxyServers.append(proxyName); + } + + settings.setValue(QString("DICOM/ServerNodes/%1").arg(row), QVariant(node)); + } + + for (int row = 0; row < rowCount; ++row) + { + QMap node = d->serverNodeParameters(row); + QString serverName = node["Name"].toString(); + if (proxyServers.contains(serverName)) + { + continue; + } + + QSharedPointer server = d->createServerFromServerNode(node); + d->Scheduler->addServer(server); + } + + for (int ii = 0; ii < rowCount; ++ii) + { + QMap node = d->serverNodeParameters(ii); + QString serverName = node["Name"].toString(); + if (!proxyServers.contains(serverName)) + { + continue; + } + + QSharedPointer proxyServer = d->createServerFromServerNode(node); + for (int jj = 0; jj < rowCount; ++jj) + { + QMap tmpNode = d->serverNodeParameters(jj); + QString tmpServerName = tmpNode["Name"].toString(); + if (serverName == tmpServerName) + { + continue; + } + QString tmpProxyName = tmpNode["Proxy"].toString(); + if (serverName == tmpProxyName) + { + ctkDICOMServer* server = this->getServer(tmpServerName.toStdString().c_str()); + if (server) + { + server->setProxyServer(proxyServer); + server->setMoveDestinationAETitle(proxyServer->calledAETitle()); + break; + } + } + } + } + + settings.setValue("DICOM/StorageEnabled", QString::number(this->storageListenerEnabled())); + settings.setValue("DICOM/StorageAETitle", this->storageAETitle()); + settings.setValue("DICOM/StoragePort", this->storagePort()); + settings.sync(); + + d->SettingsModified = false; + + if (d->StorageEnabledCheckBox->isChecked() && !d->Scheduler->isStorageListenerActive()) + { + d->Scheduler->startListener(this->storagePort(), + this->storageAETitle(), + QThread::Priority::NormalPriority); + } + + this->updateGUIState(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::readSettings() +{ + Q_D(ctkDICOMServerNodeWidget2); + + d->NodeTable->setRowCount(0); + + QSettings settings; + + QMap node; + if (settings.status() == QSettings::AccessError || + settings.value("DICOM/ServerNodeCount").toInt() == 0) + { + d->StorageAETitle->setText("CTKSTORE"); + d->StoragePort->setText("11112"); + d->StorageEnabledCheckBox->setChecked(false); + + // a dummy example + QMap defaultServerNode; + defaultServerNode["Name"] = QString("ExampleHost"); + defaultServerNode["QueryRetrieveCheckState"] = static_cast(Qt::Unchecked); + defaultServerNode["StorageCheckState"] = static_cast(Qt::Unchecked); + defaultServerNode["Calling AETitle"] = QString("CTK"); + defaultServerNode["Called AETitle"] = QString("AETITLE"); + defaultServerNode["Address"] = QString("dicom.example.com"); + defaultServerNode["Port"] = QString("11112"); + defaultServerNode["Protocol"] = QString("CGET"); + defaultServerNode["Timeout"] = QString("30"); + defaultServerNode["Proxy"] = QString(""); + d->addServerNode(defaultServerNode); + + // the uk example - see http://www.dicomserver.co.uk/ + // and http://www.medicalconnections.co.uk/ + defaultServerNode["Name"] = QString("MedicalConnections"); + defaultServerNode["QueryRetrieveCheckState"] = static_cast(Qt::Unchecked); + defaultServerNode["StorageCheckState"] = static_cast(Qt::Unchecked); + defaultServerNode["Calling AETitle"] = QString("CTK"); + defaultServerNode["Called AETitle"] = QString("ANYAE"); + defaultServerNode["Address"] = QString("dicomserver.co.uk"); + defaultServerNode["Port"] = QString("104"); + defaultServerNode["Protocol"] = QString("CGET"); + defaultServerNode["Timeout"] = QString("30"); + defaultServerNode["Proxy"] = QString(""); + d->addServerNode(defaultServerNode); + + d->SettingsModified = false; + d->NodeTable->clearSelection(); + this->updateGUIState(); + return; + } + + d->StorageEnabledCheckBox->setChecked(settings.value("DICOM/StorageEnabled").toBool()); + d->StorageAETitle->setText(settings.value("DICOM/StorageAETitle").toString()); + d->StoragePort->setText(settings.value("DICOM/StoragePort").toString()); + + const int count = settings.value("DICOM/ServerNodeCount").toInt(); + for (int row = 0; row < count; ++row) + { + node = settings.value(QString("DICOM/ServerNodes/%1").arg(row)).toMap(); + d->addServerNode(node); + } + + d->SettingsModified = false; + d->NodeTable->clearSelection(); + this->updateGUIState(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::setStorageListenerEnabled(const bool enabled) +{ + Q_D(const ctkDICOMServerNodeWidget2); + d->StorageEnabledCheckBox->setChecked(enabled); + this->onSettingsModified(); +} + +//---------------------------------------------------------------------------- +bool ctkDICOMServerNodeWidget2::storageListenerEnabled() const +{ + Q_D(const ctkDICOMServerNodeWidget2); + return d->StorageEnabledCheckBox->isChecked(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::setStorageAETitle(const QString& storageAETitle) +{ + Q_D(const ctkDICOMServerNodeWidget2); + d->StorageAETitle->setText(storageAETitle); + this->onSettingsModified(); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMServerNodeWidget2::storageAETitle()const +{ + Q_D(const ctkDICOMServerNodeWidget2); + return d->StorageAETitle->text(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::setStoragePort(const int storagePort) +{ + Q_D(const ctkDICOMServerNodeWidget2); + d->StoragePort->setText(QString::number(storagePort)); + this->onSettingsModified(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2::storagePort()const +{ + Q_D(const ctkDICOMServerNodeWidget2); + bool ok = false; + int port = d->StoragePort->text().toInt(&ok); + Q_ASSERT(ok); + return port; +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMScheduler* ctkDICOMServerNodeWidget2::scheduler()const +{ + Q_D(const ctkDICOMServerNodeWidget2); + return d->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMServerNodeWidget2::schedulerShared()const +{ + Q_D(const ctkDICOMServerNodeWidget2); + return d->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::setScheduler(ctkDICOMScheduler& scheduler) +{ + Q_D(ctkDICOMServerNodeWidget2); + d->disconnectScheduler(); + d->Scheduler = QSharedPointer(&scheduler, skipDelete); + d->connectScheduler(); + this->saveSettings(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::setScheduler(QSharedPointer scheduler) +{ + Q_D(ctkDICOMServerNodeWidget2); + d->disconnectScheduler(); + d->Scheduler = scheduler; + d->connectScheduler(); + this->saveSettings(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2::getNumberOfServers() +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("getNumberOfServers failed, no task pool has been set. \n"); + return -1; + } + + return d->Scheduler->getNumberOfServers(); +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMServerNodeWidget2::getNthServer(int id) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("getNthServer failed, no task pool has been set. \n"); + return nullptr; + } + + return d->Scheduler->getNthServer(id); +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMServerNodeWidget2::getServer(const char *connectionName) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("getServer failed, no task pool has been set. \n"); + return nullptr; + } + + return d->Scheduler->getServer(connectionName); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::addServer(ctkDICOMServer* server) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("addServer failed, no task pool has been set. \n"); + return; + } + + d->addServerNode(server); + this->saveSettings(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::removeServer(const char *connectionName) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("removeServer failed, no task pool has been set. \n"); + return; + } + + this->removeNthServer(this->getServerIndexFromName(connectionName)); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::removeNthServer(int id) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("removeNthServer failed, no task pool has been set. \n"); + return; + } + + QString connectionName = this->getServerNameFromIndex(id); + int row = d->getServerNodeRowFromConnectionName(connectionName); + d->NodeTable->removeRow(row); + this->saveSettings(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::removeAllServers() +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("removeAllServers failed, no task pool has been set. \n"); + return; + } + + d->Scheduler->removeAllServers(); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMServerNodeWidget2::getServerNameFromIndex(int id) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("getServerNameFromIndex failed, no task pool has been set. \n"); + return ""; + } + + return d->Scheduler->getServerNameFromIndex(id); +} + +//---------------------------------------------------------------------------- +int ctkDICOMServerNodeWidget2::getServerIndexFromName(const char *connectionName) +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + logger.error("getServerIndexFromName failed, no task pool has been set. \n"); + return -1; + } + + return d->Scheduler->getServerIndexFromName(connectionName); +} + +//---------------------------------------------------------------------------- +void ctkDICOMServerNodeWidget2::stopAllJobs() +{ + Q_D(ctkDICOMServerNodeWidget2); + if (!d->Scheduler) + { + return; + } + + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + d->Scheduler->stopAllJobs(true); + QApplication::restoreOverrideCursor(); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h new file mode 100644 index 0000000000..16bac14ded --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -0,0 +1,124 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMServerNodeWidget2_h +#define __ctkDICOMServerNodeWidget2_h + +// Qt includes +#include +#include +#include +#include + +#include "ctkDICOMWidgetsExport.h" + +class QTableWidgetItem; +class ctkAbstractTask; +class ctkDICOMServer; +class ctkDICOMServerNodeWidget2Private; +class ctkDICOMScheduler; + +/// \ingroup DICOM_Widgets +class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget +{ + Q_OBJECT; + Q_PROPERTY(QString storageAETitle READ storageAETitle WRITE setStorageAETitle); + Q_PROPERTY(int storagePort READ storagePort WRITE setStoragePort); + +public: + typedef QWidget Superclass; + explicit ctkDICOMServerNodeWidget2(QWidget* parent=0); + virtual ~ctkDICOMServerNodeWidget2(); + + /// Storage listener is enabled + /// false by default + void setStorageListenerEnabled(const bool enabled); + bool storageListenerEnabled() const; + + /// Storage AE title + /// "CTKSTORE" by default + void setStorageAETitle(const QString& storageAETitle); + QString storageAETitle() const; + + /// Storage port + /// 11112 by default + void setStoragePort(const int storagePort); + int storagePort() const; + + /// Return the scheduler. + Q_INVOKABLE ctkDICOMScheduler* scheduler() const; + /// Return the scheduler as a shared pointer + /// (not Python-wrappable). + QSharedPointer schedulerShared() const; + /// Set the scheduler. + Q_INVOKABLE void setScheduler(ctkDICOMScheduler& scheduler); + /// Set the scheduler as a shared pointer + /// (not Python-wrappable). + void setScheduler(QSharedPointer scheduler); + + /// Servers + Q_INVOKABLE int getNumberOfServers(); + Q_INVOKABLE ctkDICOMServer* getNthServer(int id); + Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE void addServer(ctkDICOMServer* server); + Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeNthServer(int id); + Q_INVOKABLE void removeAllServers(); + Q_INVOKABLE QString getServerNameFromIndex(int id); + Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + Q_INVOKABLE void stopAllJobs(); + +public Q_SLOTS: + /// Add an empty server node and make it current + /// Return the row index added into the table + int onAddServerNode(); + /// Remove the current row (different from the checked rows) + void onRemoveCurrentServerNode(); + /// Test the current row (different from the checked rows) + void onTestCurrentServerNode(); + + void readSettings(); + void saveSettings(); + void updateGUIState(); + void onSettingsModified(); + +protected: + QScopedPointer d_ptr; + enum ServerColumns{ + NameColumn = 0, + QueryRetrieveColumn, + StorageColumn, + CallingAETitleColumn, + CalledAETitleColumn, + AddressColumn, + PortColumn, + TimeoutColumn, + ProtocolColumn, + ProxyColumn + }; +private: + Q_DECLARE_PRIVATE(ctkDICOMServerNodeWidget2); + Q_DISABLE_COPY(ctkDICOMServerNodeWidget2); +}; + +#endif diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp new file mode 100644 index 0000000000..4bbd2afa92 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -0,0 +1,689 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +//Qt includes +#include +#include +#include + +// CTK includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMJobResponseSet.h" + +// ctkDICOMWidgets includes +#include "ctkDICOMSeriesItemWidget.h" +#include "ctkDICOMStudyItemWidget.h" +#include "ui_ctkDICOMStudyItemWidget.h" + +#include + +static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMStudyItemWidget"); + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget +{ + Q_DECLARE_PUBLIC(ctkDICOMStudyItemWidget); + +protected: + ctkDICOMStudyItemWidget* const q_ptr; + +public: + ctkDICOMStudyItemWidgetPrivate(ctkDICOMStudyItemWidget& obj); + ~ctkDICOMStudyItemWidgetPrivate(); + + void init(QWidget* parentWidget); + void updateColumnsWidths(); + void createSeries(); + void addEmptySeriesItemWidget(const int& rowIndex, + const int& columnIndex); + bool isSeriesItemAlreadyAdded(const QString& seriesItem); + + QString FilteringSeriesDescription; + QStringList FilteringModalities; + + QSharedPointer DicomDatabase; + QSharedPointer Scheduler; + QSharedPointer VisualDICOMBrowser; + + int ThumbnailSize; + QString PatientID; + QString StudyInstanceUID; + QString StudyItem; +}; + +//---------------------------------------------------------------------------- +// ctkDICOMStudyItemWidgetPrivate methods + +//---------------------------------------------------------------------------- +ctkDICOMStudyItemWidgetPrivate::ctkDICOMStudyItemWidgetPrivate(ctkDICOMStudyItemWidget& obj) + : q_ptr(&obj) +{ + this->ThumbnailSize = 300; + this->FilteringSeriesDescription = ""; + this->PatientID = ""; + this->StudyInstanceUID = ""; + this->StudyItem = ""; + + this->DicomDatabase = nullptr; + this->Scheduler = nullptr; + this->VisualDICOMBrowser = nullptr; +} + +//---------------------------------------------------------------------------- +ctkDICOMStudyItemWidgetPrivate::~ctkDICOMStudyItemWidgetPrivate() +{ + Q_Q(ctkDICOMStudyItemWidget); + + for (int row = 0; row < this->SeriesListTableWidget->rowCount(); row++) + { + for (int column = 0 ; column < this->SeriesListTableWidget->columnCount(); column++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(this->SeriesListTableWidget->cellWidget(row, column)); + if (!seriesItemWidget) + { + continue; + } + + q->disconnect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); + } + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidgetPrivate::init(QWidget* parentWidget) +{ + Q_Q(ctkDICOMStudyItemWidget); + this->setupUi(q); + + this->VisualDICOMBrowser = QSharedPointer(parentWidget, skipDelete); + + this->StudyDescriptionTextBrowser->hide(); + this->StudyDescriptionTextBrowser->setReadOnly(true); + this->StudyItemCollapsibleGroupBox->setCollapsed(false); + + q->connect(this->StudySelectionCheckBox, SIGNAL(clicked(bool)), + q, SLOT(onStudySelectionClicked(bool))); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidgetPrivate::updateColumnsWidths() +{ + for (int i = 0; i < this->SeriesListTableWidget->columnCount(); ++i) + { + this->SeriesListTableWidget->setColumnWidth(i, this->ThumbnailSize); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidgetPrivate::createSeries() +{ + Q_Q(ctkDICOMStudyItemWidget); + + if (!this->DicomDatabase) + { + logger.error("createSeries failed, no DICOM Database has been set. \n"); + return; + } + + QStringList seriesList = this->DicomDatabase->seriesForStudy(this->StudyInstanceUID); + if (seriesList.count() == 0) + { + return; + } + + // Sort by SeriesNumber + QMap seriesMap; + foreach (QString seriesItem, seriesList) + { + if (this->isSeriesItemAlreadyAdded(seriesItem)) + { + continue; + } + + QString modality = this->DicomDatabase->fieldForSeries("Modality", seriesItem); + QString seriesDescription = this->DicomDatabase->fieldForSeries("SeriesDescription", seriesItem); + // Filter with modality and seriesDescription + if ((this->FilteringSeriesDescription.isEmpty() || + seriesDescription.contains(this->FilteringSeriesDescription, Qt::CaseInsensitive)) && + (this->FilteringModalities.contains("Any") || this->FilteringModalities.contains(modality))) + { + int seriesNumber = this->DicomDatabase->fieldForSeries("SeriesNumber", seriesItem).toInt(); + while (seriesMap.contains(seriesNumber)) + { + seriesNumber++; + } + // QMap automatically sort in ascending with the key + seriesMap[seriesNumber] = seriesItem; + } + } + + int tableIndex = 0; + int seriesIndex = 0; + int numberOfSeries = seriesMap.count(); + foreach (QString seriesItem, seriesMap) + { + QString seriesInstanceUID = this->DicomDatabase->fieldForSeries("SeriesInstanceUID", seriesItem); + if (seriesInstanceUID.isEmpty()) + { + numberOfSeries--; + continue; + } + seriesIndex++; + + QString modality = this->DicomDatabase->fieldForSeries("Modality", seriesItem); + QString seriesDescription = this->DicomDatabase->fieldForSeries("SeriesDescription", seriesItem); + + q->addSeriesItemWidget(tableIndex, seriesItem, seriesInstanceUID, modality, seriesDescription); + tableIndex++; + + if (seriesIndex == numberOfSeries) + { + int emptyIndex = tableIndex; + int columnIndex = emptyIndex % this->SeriesListTableWidget->columnCount(); + while (columnIndex != 0) + { + int rowIndex = floor(emptyIndex / this->SeriesListTableWidget->columnCount()); + columnIndex = emptyIndex % this->SeriesListTableWidget->columnCount(); + this->addEmptySeriesItemWidget(rowIndex, columnIndex); + emptyIndex++; + } + } + + int iHeight = 0; + for (int rowIndex = 0; rowIndex < this->SeriesListTableWidget->rowCount(); ++rowIndex) + { + iHeight += this->SeriesListTableWidget->verticalHeader()->sectionSize(rowIndex); + } + if (iHeight < this->ThumbnailSize) + { + iHeight = this->ThumbnailSize; + } + iHeight += 25; + this->SeriesListTableWidget->setMinimumHeight(iHeight); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(const int& rowIndex, + const int& columnIndex) +{ + QTableWidgetItem *tableItem = new QTableWidgetItem; + tableItem->setFlags(Qt::NoItemFlags); + tableItem->setSizeHint(QSize(this->ThumbnailSize, this->ThumbnailSize)); + + this->SeriesListTableWidget->setItem(rowIndex, columnIndex, tableItem); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStudyItemWidgetPrivate::isSeriesItemAlreadyAdded(const QString &seriesItem) +{ + bool alreadyAdded = false; + for (int i = 0; i < this->SeriesListTableWidget->rowCount(); i++) + { + for (int j = 0 ; j < this->SeriesListTableWidget->columnCount(); j++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(this->SeriesListTableWidget->cellWidget(i, j)); + if (!seriesItemWidget) + { + continue; + } + + if (seriesItemWidget->seriesItem() == seriesItem) + { + alreadyAdded = true; + break; + } + } + + if (alreadyAdded) + { + break; + } + } + + return alreadyAdded; +} + +//---------------------------------------------------------------------------- +// ctkDICOMStudyItemWidget methods + +//---------------------------------------------------------------------------- +ctkDICOMStudyItemWidget::ctkDICOMStudyItemWidget(QWidget* parentWidget) + : Superclass(parentWidget) + , d_ptr(new ctkDICOMStudyItemWidgetPrivate(*this)) +{ + Q_D(ctkDICOMStudyItemWidget); + d->init(parentWidget); +} + +//---------------------------------------------------------------------------- +ctkDICOMStudyItemWidget::~ctkDICOMStudyItemWidget() +{ +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setStudyItem(const QString &studyItem) +{ + Q_D(ctkDICOMStudyItemWidget); + d->StudyItem = studyItem; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMStudyItemWidget::studyItem() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudyItem; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::setPatientID(const QString &patientID) +{ + Q_D(ctkDICOMStudyItemWidget); + d->PatientID = patientID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStudyItemWidget::patientID() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->PatientID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setStudyInstanceUID(const QString& studyInstanceUID) +{ + Q_D(ctkDICOMStudyItemWidget); + d->StudyInstanceUID = studyInstanceUID; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStudyItemWidget::studyInstanceUID() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudyInstanceUID; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setTitle(const QString& title) +{ + Q_D(ctkDICOMStudyItemWidget); + d->StudyItemCollapsibleGroupBox->setTitle(title); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStudyItemWidget::title() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudyItemCollapsibleGroupBox->title(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setDescription(const QString& description) +{ + Q_D(ctkDICOMStudyItemWidget); + if (description.isEmpty()) + { + d->StudyDescriptionTextBrowser->hide(); + } + else + { + d->StudyDescriptionTextBrowser->setText(description); + d->StudyDescriptionTextBrowser->show(); + } +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStudyItemWidget::description() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudyDescriptionTextBrowser->toPlainText(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setCollapsed(bool collapsed) +{ + Q_D(ctkDICOMStudyItemWidget); + d->StudyItemCollapsibleGroupBox->setCollapsed(collapsed); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStudyItemWidget::collapsed()const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudyItemCollapsibleGroupBox->collapsed(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) +{ + Q_D(ctkDICOMStudyItemWidget); + d->SeriesListTableWidget->setColumnCount(numberOfSeriesPerRow); + d->updateColumnsWidths(); +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidget::numberOfSeriesPerRow() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->SeriesListTableWidget->columnCount(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setThumbnailSize(int thumbnailSize) +{ + Q_D(ctkDICOMStudyItemWidget); + d->ThumbnailSize = thumbnailSize; + d->updateColumnsWidths(); +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidget::thumbnailSize() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->ThumbnailSize; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::setSelection(bool selected) +{ + Q_D(const ctkDICOMStudyItemWidget); + if (selected) + { + d->SeriesListTableWidget->selectAll(); + } + else + { + d->SeriesListTableWidget->clearSelection(); + } + + d->StudySelectionCheckBox->setChecked(selected); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMStudyItemWidget::selection() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->StudySelectionCheckBox->isChecked(); +} + +//---------------------------------------------------------------------------- +ctkDICOMScheduler* ctkDICOMStudyItemWidget::scheduler()const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMStudyItemWidget::schedulerShared()const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setScheduler(ctkDICOMScheduler& scheduler) +{ + Q_D(ctkDICOMStudyItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } + + d->Scheduler = QSharedPointer(&scheduler, skipDelete); + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setScheduler(QSharedPointer scheduler) +{ + Q_D(ctkDICOMStudyItemWidget); + if (d->Scheduler) + { + QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } + + d->Scheduler = scheduler; + + if (d->Scheduler) + { + QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + this, SLOT(updateGUIFromScheduler(QVariant))); + } +} + +//---------------------------------------------------------------------------- +ctkDICOMDatabase* ctkDICOMStudyItemWidget::dicomDatabase()const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->DicomDatabase.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMStudyItemWidget::dicomDatabaseShared()const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->DicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setDicomDatabase(ctkDICOMDatabase& dicomDatabase) +{ + Q_D(ctkDICOMStudyItemWidget); + d->DicomDatabase = QSharedPointer(&dicomDatabase, skipDelete); +} + +//---------------------------------------------------------------------------- +void ctkDICOMStudyItemWidget::setDicomDatabase(QSharedPointer dicomDatabase) +{ + Q_D(ctkDICOMStudyItemWidget); + d->DicomDatabase = dicomDatabase; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::setFilteringSeriesDescription(const QString& filteringSeriesDescription) +{ + Q_D(ctkDICOMStudyItemWidget); + d->FilteringSeriesDescription = filteringSeriesDescription; +} + +//------------------------------------------------------------------------------ +QString ctkDICOMStudyItemWidget::filteringSeriesDescription() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->FilteringSeriesDescription; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::setFilteringModalities(const QStringList &filteringModalities) +{ + Q_D(ctkDICOMStudyItemWidget); + d->FilteringModalities = filteringModalities; +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMStudyItemWidget::filteringModalities() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->FilteringModalities; +} + +//------------------------------------------------------------------------------ +QTableWidget *ctkDICOMStudyItemWidget::seriesListTableWidget() +{ + Q_D(ctkDICOMStudyItemWidget); + return d->SeriesListTableWidget; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::addSeriesItemWidget(const int& tableIndex, + const QString &seriesItem, + const QString &seriesInstanceUID, + const QString &modality, + const QString &seriesDescription) +{ + Q_D(ctkDICOMStudyItemWidget); + if (!d->DicomDatabase) + { + logger.error("addSeriesItemWidget failed, no DICOM Database has been set. \n"); + return; + } + + QString seriesNumber = d->DicomDatabase->fieldForSeries("SeriesNumber", seriesItem); + ctkDICOMSeriesItemWidget* seriesItemWidget = new ctkDICOMSeriesItemWidget; + seriesItemWidget->setSeriesItem(seriesItem); + seriesItemWidget->setPatientID(d->PatientID); + seriesItemWidget->setStudyInstanceUID(d->StudyInstanceUID); + seriesItemWidget->setSeriesInstanceUID(seriesInstanceUID); + seriesItemWidget->setSeriesNumber(seriesNumber); + seriesItemWidget->setModality(modality); + seriesItemWidget->setSeriesDescription(seriesDescription); + seriesItemWidget->setThumbnailSize(d->ThumbnailSize); + seriesItemWidget->setDicomDatabase(d->DicomDatabase); + seriesItemWidget->setScheduler(d->Scheduler); + seriesItemWidget->generateInstances(); + seriesItemWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + this->connect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + d->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); + + QTableWidgetItem *tableItem = new QTableWidgetItem; + tableItem->setSizeHint(QSize(d->ThumbnailSize, d->ThumbnailSize)); + + int rowIndex = floor(tableIndex / d->SeriesListTableWidget->columnCount()); + int columnIndex = tableIndex % d->SeriesListTableWidget->columnCount(); + if (columnIndex == 0) + { + d->SeriesListTableWidget->insertRow(rowIndex); + d->SeriesListTableWidget->setRowHeight(rowIndex, d->ThumbnailSize + 30); + } + + d->SeriesListTableWidget->setItem(rowIndex, columnIndex, tableItem); + d->SeriesListTableWidget->setCellWidget(rowIndex, columnIndex, seriesItemWidget); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::removeSeriesItemWidget(const QString& seriesItem) +{ + Q_D(ctkDICOMStudyItemWidget); + + for (int row = 0; row < d->SeriesListTableWidget->rowCount(); row++) + { + for (int column = 0 ; column < d->SeriesListTableWidget->columnCount(); column++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(d->SeriesListTableWidget->cellWidget(row, column)); + if (!seriesItemWidget || seriesItemWidget->seriesItem() != seriesItem) + { + continue; + } + + d->SeriesListTableWidget->removeCellWidget(row, column); + this->disconnect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + d->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); + delete seriesItemWidget; + QTableWidgetItem *tableItem = d->SeriesListTableWidget->item(row, column); + delete tableItem; + + d->addEmptySeriesItemWidget(row, column); + break; + } + } +} + +//------------------------------------------------------------------------------ +ctkCollapsibleGroupBox *ctkDICOMStudyItemWidget::collapsibleGroupBox() +{ + Q_D(ctkDICOMStudyItemWidget); + return d->StudyItemCollapsibleGroupBox; +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::generateSeries(bool toggled) +{ + Q_D(ctkDICOMStudyItemWidget); + if (!toggled) + { + return; + } + + d->createSeries(); + QStringList seriesList = d->DicomDatabase->seriesForStudy(d->StudyInstanceUID); + if (seriesList.count() == 0 && d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + { + d->Scheduler->querySeries(d->PatientID, + d->StudyInstanceUID, + QThread::NormalPriority); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::updateGUIFromScheduler(QVariant data) +{ + Q_D(ctkDICOMStudyItemWidget); + + ctkJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + d->createSeries(); + } + + if (td.JobUID.isEmpty() || + td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QuerySeries || + td.PatientID != d->PatientID || + td.StudyInstanceUID != d->StudyInstanceUID) + { + return; + } + + d->createSeries(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::onStudySelectionClicked(bool toggled) +{ + Q_D(ctkDICOMStudyItemWidget); + + this->setSelection(toggled); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h new file mode 100644 index 0000000000..2b139d6097 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -0,0 +1,154 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMStudyItemWidget_h +#define __ctkDICOMStudyItemWidget_h + +#include "ctkDICOMWidgetsExport.h" + +// Qt includes +#include +#include + +class ctkCollapsibleGroupBox; +class ctkDICOMStudyItemWidgetPrivate; +class ctkDICOMDatabase; +class ctkDICOMScheduler; + +class QTableWidget; + +/// \ingroup DICOM_Widgets +class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget +{ + Q_OBJECT; + Q_PROPERTY(QString studyItem READ studyItem WRITE setStudyItem); + Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); + Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); + Q_PROPERTY(QString title READ title WRITE setTitle); + Q_PROPERTY(QString description READ description WRITE setDescription); + Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed); + Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); + Q_PROPERTY(int thumbnailSize READ thumbnailSize WRITE setThumbnailSize); + +public: + typedef QWidget Superclass; + explicit ctkDICOMStudyItemWidget(QWidget* parent = nullptr); + virtual ~ctkDICOMStudyItemWidget(); + + /// Study item + void setStudyItem(const QString& studyItem); + QString studyItem() const; + + /// Patient ID + void setPatientID(const QString& patientID); + QString patientID() const; + + /// Study instance UID + void setStudyInstanceUID(const QString& studyInstanceUID); + QString studyInstanceUID() const; + + /// Study title + void setTitle(const QString& title); + QString title() const; + + /// Study Description + void setDescription(const QString& description); + QString description() const; + + /// Study GroupBox collapsed + /// False by default + void setCollapsed(bool collapsed); + bool collapsed() const; + + /// Number of series displayed per row + /// 6 by default + void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); + int numberOfSeriesPerRow() const; + + /// Series Thumbnail size + /// 300 px by default + void setThumbnailSize(int thumbnailSize); + int thumbnailSize() const; + + /// Study is selected + void setSelection(bool selected); + bool selection() const; + + /// Query Filters + /// Empty by default + void setFilteringSeriesDescription(const QString& filteringSeriesDescription); + QString filteringSeriesDescription() const; + /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default + void setFilteringModalities(const QStringList& filteringModalities); + QStringList filteringModalities() const; + + /// Return the scheduler. + Q_INVOKABLE ctkDICOMScheduler* scheduler() const; + /// Return the scheduler as a shared pointer + /// (not Python-wrappable). + QSharedPointer schedulerShared() const; + /// Set the scheduler. + Q_INVOKABLE void setScheduler(ctkDICOMScheduler& scheduler); + /// Set the scheduler as a shared pointer + /// (not Python-wrappable). + void setScheduler(QSharedPointer scheduler); + + /// Return the Dicom Database. + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + /// Return Dicom Database as a shared pointer + /// (not Python-wrappable). + QSharedPointer dicomDatabaseShared() const; + /// Set the Dicom Database. + Q_INVOKABLE void setDicomDatabase(ctkDICOMDatabase& dicomDatabase); + /// Set the Dicom Database as a shared pointer + /// (not Python-wrappable). + void setDicomDatabase(QSharedPointer dicomDatabase); + + /// Series list table. + Q_INVOKABLE QTableWidget* seriesListTableWidget(); + + /// Add/Remove Series item widget + Q_INVOKABLE void addSeriesItemWidget(const int& tableIndex, + const QString &seriesItem, + const QString &seriesInstanceUID, + const QString& modality, + const QString& seriesDescription); + Q_INVOKABLE void removeSeriesItemWidget(const QString& seriesItem); + + /// Collapsible group box. + Q_INVOKABLE ctkCollapsibleGroupBox* collapsibleGroupBox(); + +public Q_SLOTS: + void generateSeries(bool toggled = true); + void updateGUIFromScheduler(QVariant data); + void onStudySelectionClicked(bool); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(ctkDICOMStudyItemWidget); + Q_DISABLE_COPY(ctkDICOMStudyItemWidget); +}; + +#endif diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp index 861c995915..fed5766fc9 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp @@ -131,7 +131,7 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, QImage& EI_Status result = dcmImage->getStatus(); if (result != EIS_Normal) { - qCritical() << Q_FUNC_INFO << QString("Rendering of DICOM image failed for thumbnail failed: ") + DicomImage::getString(result); + logger.warn(QString("Rendering of DICOM image failed for thumbnail failed: ") + DicomImage::getString(result)); return false; } // Select first window defined in image. If none, compute min/max window as best guess. @@ -214,12 +214,12 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(const QString dcmImagePath, c } //------------------------------------------------------------------------------ -void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image) +void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image, Qt::GlobalColor color) { Q_D(ctkDICOMThumbnailGenerator); if (image.width() != d->Width || image.height() != d->Height) { image = QImage(d->Width, d->Height, QImage::Format_RGB32); } - image.fill(Qt::darkGray); + image.fill(color); } diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h index 292cdfddd1..2ea7be7024 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h @@ -56,7 +56,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstr /// Generate a blank thumbnail image (currently a solid gray box of the requested thumbnail size). /// It can be used as a placeholder for invalid images or duringan image is loaded. - Q_INVOKABLE void generateBlankThumbnail(QImage& image); + Q_INVOKABLE void generateBlankThumbnail(QImage& image, Qt::GlobalColor color = Qt::darkGray); /// Set thumbnail width void setWidth(int width); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp new file mode 100644 index 0000000000..56abbd9085 --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -0,0 +1,2979 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// CTK includes +#include +#include +#include +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMDatabase.h" +#include "ctkDICOMIndexer.h" +#include "ctkDICOMScheduler.h" +#include "ctkDICOMServer.h" +#include "ctkDICOMJobResponseSet.h" +#include "ctkUtils.h" + +// ctkDICOMWidgets includes +#include "ctkDICOMObjectListWidget.h" +#include "ctkDICOMVisualBrowserWidget.h" +#include "ctkDICOMStudyItemWidget.h" +#include "ctkDICOMSeriesItemWidget.h" +#include "ctkDICOMServerNodeWidget2.h" +#include "ctkDICOMVisualBrowserWidget.h" +#include "ui_ctkDICOMVisualBrowserWidget.h" + +static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMVisualBrowserWidget"); + +class ctkDICOMMetadataDialog : public QDialog +{ +public: + ctkDICOMMetadataDialog(QWidget *parent = 0) + : QDialog(parent) + { + this->setWindowFlags(Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::Window); + this->setModal(true); + this->setSizeGripEnabled(true); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + this->tagListWidget = new ctkDICOMObjectListWidget(); + layout->addWidget(this->tagListWidget); + } + + virtual ~ctkDICOMMetadataDialog() + { + } + + void setFileList(const QStringList& fileList) + { + this->tagListWidget->setFileList(fileList); + } + + void closeEvent(QCloseEvent *evt) + { + // just hide the window when close button is clicked + evt->ignore(); + this->hide(); + } + + void showEvent(QShowEvent *event) + { + QDialog::showEvent(event); + // QDialog would reset window position and size when shown. + // Restore its previous size instead (user may look at metadata + // of different series one after the other and would be inconvenient to + // set the desired size manually each time). + if (!this->savedGeometry.isEmpty()) + { + this->restoreGeometry(this->savedGeometry); + if (this->isMaximized()) + { + this->setGeometry(QApplication::desktop()->availableGeometry(this)); + } + } + } + + void hideEvent(QHideEvent *event) + { + this->savedGeometry = this->saveGeometry(); + QDialog::hideEvent(event); + } + +protected: + ctkDICOMObjectListWidget* tagListWidget; + QByteArray savedGeometry; +}; + +//---------------------------------------------------------------------------- +class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget +{ + Q_DECLARE_PUBLIC( ctkDICOMVisualBrowserWidget ); + +protected: + ctkDICOMVisualBrowserWidget* const q_ptr; + QToolButton *patientsTabMenuToolButton; + +public: + ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMVisualBrowserWidget& obj); + ~ctkDICOMVisualBrowserWidgetPrivate(); + + void init(); + void disconnectScheduler(); + void connectScheduler(); + void importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); + void importFiles(const QStringList& files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); + void importOldSettings(); + void updateModalityCheckableComboBox(); + void createPatients(); + void updateFiltersWarnings(); + void retrieveSeries(); + bool updateServer(ctkDICOMServer* server); + void removeAllPatientItemWidgets(); + bool isPatientTabAlreadyAdded(const QString& patientItem); + void updateSeriesTablesSelection(ctkDICOMSeriesItemWidget* selectedSeriesItemWidget); + QStringList getPatientUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets); + QStringList getStudyUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets); + QStringList getSeriesUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets); + QStringList filterPatientList(const QStringList& patientList, + const QString& filterName, + const QString& filterValue); + QStringList filterStudyList(const QStringList& studyList, + const QString& filterName, + const QString& filterValue); + QStringList filterSeriesList(const QStringList& seriesList, + const QString& filterName, + const QString& filterValue); + QStringList filterSeriesList(const QStringList& seriesList, + const QString& filterName, + const QStringList& filterValues); + + // Return a sanitized version of the string that is safe to be used + // as a filename component. + // All non-ASCII characters are replaced, because they may be used on an internal hard disk, + // but it may not be possible to use them on file systems of an external drive or network storage. + QString filenameSafeString(const QString& str) + { + QString safeStr; + const QString illegalChars("/\\<>:\"|?*"); + foreach (const QChar& c, str) + { + int asciiCode = c.toLatin1(); + if (asciiCode >= 32 && asciiCode <= 127 && !illegalChars.contains(c)) + { + safeStr.append(c); + } + else + { + safeStr.append("_"); + } + } + // remove leading/trailing whitespaces + return safeStr.trimmed(); + } + + // local count variables to keep track of the number of items + // added to the database during an import operation + int PatientsAddedDuringImport; + int StudiesAddedDuringImport; + int SeriesAddedDuringImport; + int InstancesAddedDuringImport; + ctkFileDialog* ImportDialog; + + QSharedPointer MetadataDialog; + + // Settings key that stores database directory + QString DatabaseDirectorySettingsKey; + + // If database directory is specified with relative path then this directory will be used as a base + QString DatabaseDirectoryBase; + + // Default database path to use if there is nothing in settings + QString DefaultDatabaseDirectory; + QString DatabaseDirectory; + + QSharedPointer DicomDatabase; + QSharedPointer Scheduler; + QSharedPointer Indexer; + + QString FilteringPatientID; + QString FilteringPatientName; + + QString FilteringStudyDescription; + ctkDICOMPatientItemWidget::DateType FilteringDate; + + QString FilteringSeriesDescription; + QStringList PreviousFilteringModalities; + QStringList FilteringModalities; + + int NumberOfStudiesPerPatient; + int NumberOfSeriesPerRow; + int MinimumThumbnailSize; + bool SendActionVisible; + bool DeleteActionVisible; + bool IsGUIUpdating; + bool IsLoading; + + ctkDICOMServerNodeWidget2* ServerNodeWidget; +}; + +CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QString, databaseDirectoryBase, DatabaseDirectoryBase); +CTK_SET_CPP(ctkDICOMVisualBrowserWidget, const QString&, setDatabaseDirectoryBase, DatabaseDirectoryBase); + +//---------------------------------------------------------------------------- +// ctkDICOMVisualBrowserWidgetPrivate methods + +//---------------------------------------------------------------------------- +ctkDICOMVisualBrowserWidgetPrivate::ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMVisualBrowserWidget& obj) + : q_ptr(&obj) +{ + this->DicomDatabase = QSharedPointer (new ctkDICOMDatabase); + + this->Scheduler = QSharedPointer (new ctkDICOMScheduler); + this->Scheduler->setDicomDatabase(this->DicomDatabase); + + this->Indexer = QSharedPointer (new ctkDICOMIndexer); + this->Indexer->setDatabase(this->DicomDatabase.data()); + + this->MetadataDialog = QSharedPointer (new ctkDICOMMetadataDialog()); + this->MetadataDialog->setObjectName("DICOMMetadata"); + this->MetadataDialog->setWindowTitle(ctkDICOMVisualBrowserWidget::tr("DICOM File Metadata")); + + this->DatabaseDirectorySettingsKey = ""; + this->DatabaseDirectoryBase = ""; + this->DefaultDatabaseDirectory = ""; + this->DatabaseDirectory = ""; + + this->NumberOfStudiesPerPatient = 2; + this->NumberOfSeriesPerRow = 6; + this->MinimumThumbnailSize = 300; + this->SendActionVisible = false; + this->DeleteActionVisible = true; + + this->FilteringPatientID = ""; + this->FilteringPatientName = ""; + this->FilteringStudyDescription = ""; + this->FilteringSeriesDescription = ""; + this->FilteringDate = ctkDICOMPatientItemWidget::DateType::Any; + + this->FilteringModalities.append("Any"); + this->FilteringModalities.append("CR"); + this->FilteringModalities.append("CT"); + this->FilteringModalities.append("MR"); + this->FilteringModalities.append("NM"); + this->FilteringModalities.append("US"); + this->FilteringModalities.append("PT"); + this->FilteringModalities.append("XA"); + + this->PatientsAddedDuringImport = 0; + this->StudiesAddedDuringImport = 0; + this->SeriesAddedDuringImport = 0; + this->InstancesAddedDuringImport = 0; + this->ImportDialog = nullptr; + + this->IsGUIUpdating = false; + this->IsLoading = false; + + this->ServerNodeWidget = new ctkDICOMServerNodeWidget2(); + this->ServerNodeWidget->setScheduler(this->Scheduler); + this->connectScheduler(); +} + +//---------------------------------------------------------------------------- +ctkDICOMVisualBrowserWidgetPrivate::~ctkDICOMVisualBrowserWidgetPrivate() +{ + this->removeAllPatientItemWidgets(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::init() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + this->setupUi(q); + + this->WarningPushButton->hide(); + QObject::connect(this->FilteringPatientIDSearchBox, SIGNAL(textChanged(QString)), + q, SLOT(onFilteringPatientIDChanged())); + + QObject::connect(this->FilteringPatientNameSearchBox, SIGNAL(textChanged(QString)), + q, SLOT(onFilteringPatientNameChanged())); + + QObject::connect(this->FilteringStudyDescriptionSearchBox, SIGNAL(textChanged(QString)), + q, SLOT(onFilteringStudyDescriptionChanged())); + + QObject::connect(this->FilteringSeriesDescriptionSearchBox, SIGNAL(textChanged(QString)), + q, SLOT(onFilteringSeriesDescriptionChanged())); + + QObject::connect(this->FilteringModalityCheckableComboBox, SIGNAL(checkedIndexesChanged()), + q, SLOT(onFilteringModalityCheckableComboBoxChanged())); + this->updateModalityCheckableComboBox(); + + QObject::connect(this->FilteringDateComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onFilteringDateComboBoxChanged(int))); + + QObject::connect(this->QueryPatientPushButton, SIGNAL(clicked()), + q, SLOT(onQueryPatient())); + + this->ServersSettingsCollapsibleGroupBox->layout()->addWidget(this->ServerNodeWidget); + + QSize iconSize(28, 28); + this->PatientsTabWidget->setIconSize(iconSize); + this->PatientsTabWidget->clear(); + + // setup patients menu on a tool button on the tab bar + QTabBar* tabWidget = this->PatientsTabWidget->tabBar(); + tabWidget->setDocumentMode(true); + tabWidget->setExpanding(true); + + this->patientsTabMenuToolButton = new QToolButton(q); + this->patientsTabMenuToolButton->setObjectName("patientsTabMenuToolButton"); + this->patientsTabMenuToolButton->setIconSize(iconSize); + this->patientsTabMenuToolButton->setFixedHeight(40); + this->patientsTabMenuToolButton->setCheckable(false); + this->patientsTabMenuToolButton->setChecked(false); + this->patientsTabMenuToolButton->setIcon(QIcon(":/Icons/more_vert.svg")); + this->patientsTabMenuToolButton->hide(); + this->patientsTabMenuToolButton->setStyleSheet(this->patientsTabMenuToolButton->styleSheet() + + "QToolButton{padding-top: 5px; padding-bottom: 5px}"); + + + QObject::connect(this->patientsTabMenuToolButton, SIGNAL(clicked()), + q, SLOT(onPatientsTabMenuToolButtonClicked())); + + this->PatientsTabWidget->setCornerWidget(this->patientsTabMenuToolButton, Qt::TopRightCorner); + + + QObject::connect(this->PatientsTabWidget, SIGNAL(currentChanged(int)), + q, SLOT(onPatientItemChanged(int))); + + QObject::connect(this->ClosePushButton, SIGNAL(clicked()), + q, SLOT(onClose())); + + QObject::connect(this->ImportPushButton, SIGNAL(clicked()), + q, SLOT(onImport())); + + // Initialize directoryMode widget + QFormLayout *layout = new QFormLayout; + QComboBox* importDirectoryModeComboBox = new QComboBox(); + importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Add Link"), ctkDICOMVisualBrowserWidget::ImportDirectoryAddLink); + importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Copy"), ctkDICOMVisualBrowserWidget::ImportDirectoryCopy); + importDirectoryModeComboBox->setToolTip( + ctkDICOMVisualBrowserWidget::tr("Indicate if the files should be copied to the local database" + " directory or if only links should be created ?")); + layout->addRow(new QLabel(ctkDICOMVisualBrowserWidget::tr("Import Directory Mode:")), importDirectoryModeComboBox); + layout->setContentsMargins(0, 0, 0, 0); + QWidget* importDirectoryBottomWidget = new QWidget(); + importDirectoryBottomWidget->setLayout(layout); + + // Default values + importDirectoryModeComboBox->setCurrentIndex( + importDirectoryModeComboBox->findData(q->importDirectoryMode())); + + //Initialize import widget + this->ImportDialog = new ctkFileDialog(); + this->ImportDialog->setBottomWidget(importDirectoryBottomWidget); + this->ImportDialog->setFileMode(QFileDialog::Directory); + // XXX Method setSelectionMode must be called after setFileMode + this->ImportDialog->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->ImportDialog->setLabelText(QFileDialog::Accept, ctkDICOMVisualBrowserWidget::tr("Import")); + this->ImportDialog->setWindowTitle(ctkDICOMVisualBrowserWidget::tr("Import DICOM files from directory ...")); + this->ImportDialog->setWindowModality(Qt::ApplicationModal); + + q->connect(this->ImportDialog, SIGNAL(filesSelected(QStringList)), + q,SLOT(onImportDirectoriesSelected(QStringList))); + + q->connect(importDirectoryModeComboBox, SIGNAL(currentIndexChanged(int)), + q, SLOT(onImportDirectoryComboBoxCurrentIndexChanged(int))); + + this->ProgressFrame->hide(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::disconnectScheduler() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + if (!this->Scheduler) + { + return; + } + + ctkDICOMVisualBrowserWidget::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + q, SLOT(updateGUIFromScheduler(QVariant))); + ctkDICOMVisualBrowserWidget::disconnect(this->Scheduler.data(), SIGNAL(taskFailed(QString, QString)), + q, SLOT(onTaskFailed(QString, QString))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progress(int)), q, SLOT(onIndexingProgress(int))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progressStep(QString)),q, SLOT(onIndexingProgressStep(QString))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progressDetail(QString)), q, SLOT(onIndexingProgressDetail(QString))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(indexingComplete(int,int,int,int)), q, SLOT(onIndexingComplete(int,int,int,int))); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::connectScheduler() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + if (!this->Scheduler) + { + return; + } + + ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + q, SLOT(updateGUIFromScheduler(QVariant))); + ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), + q, SLOT(onTaskFailed(QString, QString))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progress(int)), q, SLOT(onIndexingProgress(int))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressStep(QString)),q, SLOT(onIndexingProgressStep(QString))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressDetail(QString)), q, SLOT(onIndexingProgressDetail(QString))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(indexingComplete(int,int,int,int)), q, SLOT(onIndexingComplete(int,int,int,int))); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode) +{ + if (!this->DicomDatabase) + { + logger.error("importDirectory failed, no DICOM Database has been set. \n"); + return; + } + + if (!this->Scheduler || !this->Indexer) + { + logger.error("importDirectory failed, no task pool has been set. \n"); + return; + } + + if (!QDir(directory).exists()) + { + logger.error(QString("importDirectory failed, input directory %1 does not exist. \n").arg(directory)); + return; + } + // Start background indexing + this->Indexer->addDirectory(directory, mode == ctkDICOMVisualBrowserWidget::ImportDirectoryCopy); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::importFiles(const QStringList &files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode) +{ + if (!this->DicomDatabase) + { + logger.error("importFiles failed, no DICOM Database has been set. \n"); + return; + } + + if (!this->Scheduler || !this->Indexer) + { + logger.error("importFiles failed, no task pool has been set. \n"); + return; + } + + // Start background indexing + this->Indexer->addListOfFiles(files, mode == ctkDICOMVisualBrowserWidget::ImportDirectoryCopy); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::importOldSettings() +{ + // Backward compatibility + QSettings settings; + int dontConfirmCopyOnImport = settings.value("MainWindow/DontConfirmCopyOnImport", static_cast(QMessageBox::InvalidRole)).toInt(); + if (dontConfirmCopyOnImport == QMessageBox::AcceptRole) + { + settings.setValue("DICOM/ImportDirectoryMode", static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryCopy)); + } + settings.remove("MainWindow/DontConfirmCopyOnImport"); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::updateModalityCheckableComboBox() +{ + QAbstractItemModel* model = this->FilteringModalityCheckableComboBox->checkableModel(); + int wasBlocking = this->FilteringModalityCheckableComboBox->blockSignals(true); + + if ((!this->PreviousFilteringModalities.contains("Any") && + this->FilteringModalities.contains("Any")) || + this->FilteringModalities.count() == 0) + { + this->FilteringModalities.clear(); + this->FilteringModalities.append("Any"); + this->FilteringModalities.append("CR"); + this->FilteringModalities.append("CT"); + this->FilteringModalities.append("MR"); + this->FilteringModalities.append("NM"); + this->FilteringModalities.append("US"); + this->FilteringModalities.append("PT"); + this->FilteringModalities.append("XA"); + + for (int i = 0; i < this->FilteringModalityCheckableComboBox->count(); ++i) + { + QModelIndex modelIndex = this->FilteringModalityCheckableComboBox->checkableModel()->index(i, 0); + this->FilteringModalityCheckableComboBox->setCheckState(modelIndex, Qt::CheckState::Checked); + } + this->FilteringModalityCheckableComboBox->blockSignals(wasBlocking); + return; + } + + for (int i = 0; i < this->FilteringModalityCheckableComboBox->count(); ++i) + { + QModelIndex modelIndex = this->FilteringModalityCheckableComboBox->checkableModel()->index(i, 0); + this->FilteringModalityCheckableComboBox->setCheckState(modelIndex, Qt::CheckState::Unchecked); + } + + foreach (QString modality, this->FilteringModalities) + { + QModelIndexList indexList = model->match(model->index(0,0), 0, modality); + if (indexList.length() == 0) + { + continue; + } + + QModelIndex index = indexList[0]; + this->FilteringModalityCheckableComboBox->setCheckState(index, Qt::CheckState::Checked); + } + + if (this->FilteringModalityCheckableComboBox->allChecked()) + { + this->FilteringModalityCheckableComboBox->blockSignals(wasBlocking); + return; + } + + int anyCheckState = Qt::CheckState::Unchecked; + QModelIndex anyModelIndex = this->FilteringModalityCheckableComboBox->checkableModel()->index(0, 0); + for (int i = 1; i < this->FilteringModalityCheckableComboBox->count(); ++i) + { + QModelIndex modelIndex = this->FilteringModalityCheckableComboBox->checkableModel()->index(i, 0); + if (this->FilteringModalityCheckableComboBox->checkState(modelIndex) != Qt::CheckState::Checked) + { + anyCheckState = Qt::CheckState::PartiallyChecked; + break; + } + } + + if (anyCheckState == Qt::CheckState::PartiallyChecked) + { + this->FilteringModalityCheckableComboBox->setCheckState(anyModelIndex, Qt::CheckState::PartiallyChecked); + this->FilteringModalities.removeAll("Any"); + } + else + { + this->FilteringModalityCheckableComboBox->setCheckState(anyModelIndex, Qt::CheckState::Checked); + this->FilteringModalities.append("Any"); + } + + this->FilteringModalityCheckableComboBox->blockSignals(wasBlocking); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::createPatients() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + if (this->IsGUIUpdating) + { + return; + } + + if (!this->DicomDatabase) + { + logger.error("createPatients failed, no DICOM database has been set. \n"); + return; + } + + QStringList patientList = this->DicomDatabase->patients(); + if (patientList.count() == 0) + { + this->patientsTabMenuToolButton->hide(); + return; + } + + this->patientsTabMenuToolButton->show(); + + this->IsGUIUpdating = true; + + int wasBlocking = this->PatientsTabWidget->blockSignals(true); + foreach (QString patientItem, patientList) + { + QString patientID = this->DicomDatabase->fieldForPatient("PatientID", patientItem); + QString patientName = this->DicomDatabase->fieldForPatient("PatientsName", patientItem); + patientName.replace(R"(^)", R"( )"); + if (this->isPatientTabAlreadyAdded(patientItem)) + { + continue; + } + + // Filter with patientID and patientsName + if ((!this->FilteringPatientID.isEmpty() && !patientID.contains(this->FilteringPatientID, Qt::CaseInsensitive)) || + (!this->FilteringPatientName.isEmpty() && !patientName.contains(this->FilteringPatientName, Qt::CaseInsensitive))) + { + continue; + } + + q->addPatientItemWidget(patientItem); + } + + this->PatientsTabWidget->setCurrentIndex(0); + this->PatientsTabWidget->blockSignals(wasBlocking); + q->onPatientItemChanged(0); + + this->IsGUIUpdating = false; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() +{ + if (!this->DicomDatabase) + { + logger.error("updateFiltersWarnings failed, no DICOM database has been set. \n"); + return; + } + + // Loop over all the data in the dicom database and apply the filters. + // If there are no series, highlight which are the filters that produce no results + QString background = "QWidget { background-color: white }"; + this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); + this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); + this->FilteringDateComboBox->setStyleSheet(this->FilteringDateComboBox->styleSheet() + background); + this->FilteringStudyDescriptionSearchBox->setStyleSheet(this->FilteringStudyDescriptionSearchBox->styleSheet() + background); + this->FilteringSeriesDescriptionSearchBox->setStyleSheet(this->FilteringSeriesDescriptionSearchBox->styleSheet() + background); + this->FilteringModalityCheckableComboBox->setStyleSheet(this->FilteringModalityCheckableComboBox->styleSheet() + background); + + background = "QWidget { background-color: yellow }"; + QStringList patientList = this->DicomDatabase->patients(); + if (patientList.count() == 0) + { + this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); + this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); + return; + } + + QStringList filteredPatientListByName = + this->filterPatientList(patientList, "PatientsName", this->FilteringPatientName); + QStringList filteredPatientListByID = + this->filterPatientList(patientList, "PatientID", this->FilteringPatientID); + + if (filteredPatientListByName.count() == 0) + { + this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); + } + + if (filteredPatientListByID.count() == 0) + { + this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); + } + + QStringList filteredPatientList = filteredPatientListByName + filteredPatientListByID; + if (filteredPatientList.count() == 0) + { + return; + } + + QStringList studiesList; + foreach (QString patientItem, filteredPatientList) + { + studiesList.append(this->DicomDatabase->studiesForPatient(patientItem)); + } + + QStringList filteredStudyListByDate = + this->filterStudyList(studiesList, "StudyDate", + QString::number(ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(this->FilteringDate))); + QStringList filteredStudyListByDescription = + this->filterStudyList(studiesList, "StudyDescription", this->FilteringStudyDescription); + + if (filteredStudyListByDate.count() == 0) + { + this->FilteringDateComboBox->setStyleSheet(this->FilteringDateComboBox->styleSheet() + background); + } + + if (filteredStudyListByDescription.count() == 0) + { + this->FilteringStudyDescriptionSearchBox->setStyleSheet(this->FilteringStudyDescriptionSearchBox->styleSheet() + background); + } + + QStringList filteredStudyList = filteredStudyListByDate + filteredStudyListByDescription; + if (filteredStudyList.count() == 0) + { + return; + } + + QStringList seriesList; + foreach (QString studyItem, filteredStudyList) + { + QString studyInstanceUID = this->DicomDatabase->fieldForStudy("StudyInstanceUID", studyItem); + seriesList.append(this->DicomDatabase->seriesForStudy(studyInstanceUID)); + } + + QStringList filteredSeriesListByModality = + this->filterSeriesList(seriesList, "Modality", this->FilteringModalities); + QStringList filteredSeriesListByDescription = + this->filterSeriesList(seriesList, "SeriesDescription", this->FilteringSeriesDescription); + + if (filteredSeriesListByModality.count() == 0) + { + this->FilteringModalityCheckableComboBox->setStyleSheet(this->FilteringModalityCheckableComboBox->styleSheet() + background); + } + + if (filteredSeriesListByDescription.count() == 0) + { + this->FilteringSeriesDescriptionSearchBox->setStyleSheet(this->FilteringSeriesDescriptionSearchBox->styleSheet() + background); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + if (!this->Scheduler) + { + logger.error("retrieveSeries failed, no task pool has been set. \n"); + return; + } + + if (this->IsLoading) + { + return; + } + + ctkDICOMPatientItemWidget* currentPatientItemWidget = + qobject_cast(this->PatientsTabWidget->currentWidget()); + if (!currentPatientItemWidget) + { + return; + } + + this->IsLoading = true; + + QList seriesWidgetsList; + for (int patientIndex = 0; patientIndex < this->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(this->PatientsTabWidget->widget(patientIndex)); + if (!patientItemWidget) + { + continue; + } + + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) + { + QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + for (int row = 0; row < seriesListTableWidget->rowCount(); row++) + { + for (int column = 0 ; column < seriesListTableWidget->columnCount(); column++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(seriesListTableWidget->cellWidget(row, column)); + if (!seriesItemWidget) + { + continue; + } + + seriesWidgetsList.append(seriesItemWidget); + } + } + } + } + + QList selectedSeriesWidgetsList; + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) + { + QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + QModelIndexList indexList = seriesListTableWidget->selectionModel()->selectedIndexes(); + foreach (QModelIndex index, indexList) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast + (seriesListTableWidget->cellWidget(index.row(), index.column())); + if (!seriesItemWidget) + { + continue; + } + + selectedSeriesWidgetsList.append(seriesItemWidget); + } + } + + if (selectedSeriesWidgetsList.count() == 0) + { + this->IsLoading = false; + return; + } + + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + + bool deleteActionWasVisible = this->DeleteActionVisible; + this->DeleteActionVisible = false; + + bool queryPatientButtonWasEnabled = this->QueryPatientPushButton->isEnabled(); + this->QueryPatientPushButton->setEnabled(false); + + QStringList seriesInstanceUIDsToStop; + QStringList selectedSeriesInstanceUIDs; + foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, seriesWidgetsList) + { + if (!seriesItemWidget) + { + continue; + } + + if (!selectedSeriesWidgetsList.contains(seriesItemWidget)) + { + seriesItemWidget->setStopJobs(true); + seriesInstanceUIDsToStop.append(seriesItemWidget->seriesInstanceUID()); + } + else + { + selectedSeriesInstanceUIDs.append(seriesItemWidget->seriesInstanceUID()); + } + } + + this->Scheduler->stopJobsByUIDs({}, + {}, + seriesInstanceUIDsToStop); + + bool wait = true; + while(wait) + { + this->Scheduler->waitForDone(300); + + wait = false; + foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, selectedSeriesWidgetsList) + { + if (!seriesItemWidget) + { + continue; + } + + if (seriesItemWidget->isCloud()) + { + wait = true; + break; + } + } + } + + this->updateFiltersWarnings(); + this->ProgressFrame->hide(); + this->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); + + foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, seriesWidgetsList) + { + if (!seriesItemWidget) + { + continue; + } + + seriesItemWidget->setStopJobs(false); + } + + q->emit seriesRetrieved(selectedSeriesInstanceUIDs); + + this->IsLoading = false; + this->DeleteActionVisible = deleteActionWasVisible; + this->QueryPatientPushButton->setEnabled(queryPatientButtonWasEnabled); + QApplication::restoreOverrideCursor(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::removeAllPatientItemWidgets() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + + int wasBlocking = this->PatientsTabWidget->blockSignals(true); + for (int patientIndex = 0; patientIndex < this->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(this->PatientsTabWidget->widget(patientIndex)); + if (!patientItemWidget) + { + continue; + } + + this->PatientsTabWidget->removeTab(patientIndex); + q->disconnect(patientItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + q, SLOT(showPatientContextMenu(const QPoint&))); + + QList studyItemWidgets = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgets) + { + q->disconnect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemDoubleClicked(QTableWidgetItem *)), + q, SLOT(onLoad())); + } + + delete patientItemWidget; + patientIndex--; + } + this->PatientsTabWidget->blockSignals(wasBlocking); +} + +//---------------------------------------------------------------------------- +bool ctkDICOMVisualBrowserWidgetPrivate::isPatientTabAlreadyAdded(const QString &patientItem) +{ + bool alreadyAdded = false; + for (int index = 0; index < this->PatientsTabWidget->count(); ++index) + { + if (patientItem == this->PatientsTabWidget->tabWhatsThis(index)) + { + alreadyAdded = true; + break; + } + } + + return alreadyAdded; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::updateSeriesTablesSelection(ctkDICOMSeriesItemWidget* selectedSeriesItemWidget) +{ + if (!selectedSeriesItemWidget) + { + return; + } + + ctkDICOMPatientItemWidget* currentPatientItemWidget = + qobject_cast(this->PatientsTabWidget->currentWidget()); + if (!currentPatientItemWidget) + { + return; + } + + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + QList selectedItems = seriesListTableWidget->selectedItems(); + foreach (QTableWidgetItem *selectedItem, selectedItems) + { + if (!selectedItem) + { + continue; + } + + int row = selectedItem->row(); + int column = selectedItem->column(); + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(seriesListTableWidget->cellWidget(row, column)); + + if (seriesItemWidget == selectedSeriesItemWidget) + { + seriesListTableWidget->itemClicked(selectedItem); + return; + } + } + } +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::getPatientUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + QStringList selectedPatientUIDs; + + if(!this->DicomDatabase) + { + return selectedPatientUIDs; + } + + foreach (QWidget *selectedWidget, selectedWidgets) + { + if (!selectedWidget) + { + continue; + } + + if (level == ctkDICOMModel::PatientType) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(selectedWidget); + if (patientItemWidget) + { + QString patientID = patientItemWidget->patientID(); + selectedPatientUIDs << patientID; + } + } + } + + return selectedPatientUIDs; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::getStudyUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + QStringList selectedStudyUIDs; + + if(!this->DicomDatabase) + { + return selectedStudyUIDs; + } + + foreach (QWidget *selectedWidget, selectedWidgets) + { + if (!selectedWidget) + { + continue; + } + + if (level == ctkDICOMModel::PatientType) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(selectedWidget); + if (patientItemWidget) + { + selectedStudyUIDs << this->DicomDatabase->studiesForPatient(patientItemWidget->patientItem()); + } + } + else if (level == ctkDICOMModel::StudyType) + { + ctkDICOMStudyItemWidget* studyItemWidget = + qobject_cast(selectedWidget); + if (studyItemWidget) + { + selectedStudyUIDs << studyItemWidget->studyInstanceUID(); + } + } + } + + return selectedStudyUIDs; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::getSeriesUIDsFromWidgets(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + QStringList selectedStudyUIDs; + QStringList selectedSeriesUIDs; + + if(!this->DicomDatabase) + { + return selectedSeriesUIDs; + } + + foreach (QWidget *selectedWidget, selectedWidgets) + { + if (!selectedWidget) + { + continue; + } + + if (level == ctkDICOMModel::PatientType) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(selectedWidget); + if (patientItemWidget) + { + selectedStudyUIDs << this->DicomDatabase->studiesForPatient(patientItemWidget->patientItem()); + } + } + else if (level == ctkDICOMModel::StudyType) + { + ctkDICOMStudyItemWidget* studyItemWidget = + qobject_cast(selectedWidget); + if (studyItemWidget) + { + selectedStudyUIDs << studyItemWidget->studyInstanceUID(); + } + } + + if (level == ctkDICOMModel::SeriesType) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(selectedWidget); + if (seriesItemWidget) + { + selectedSeriesUIDs << seriesItemWidget->seriesInstanceUID(); + } + } + else + { + foreach(const QString& uid, selectedStudyUIDs) + { + selectedSeriesUIDs << this->DicomDatabase->seriesForStudy(uid); + } + } + } + + return selectedSeriesUIDs; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::filterPatientList(const QStringList& patientList, + const QString& filterName, + const QString& filterValue) +{ + QStringList filteredPatientList; + if (!this->DicomDatabase) + { + logger.error("filterPatientList failed, no DICOM Database has been set. \n"); + return filteredPatientList; + } + + foreach (QString patientItem, patientList) + { + QString filter = this->DicomDatabase->fieldForPatient(filterName, patientItem); + if (!filter.contains(filterValue, Qt::CaseInsensitive)) + { + continue; + } + + filteredPatientList.append(patientItem); + } + + return filteredPatientList; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::filterStudyList(const QStringList& studyList, + const QString &filterName, + const QString &filterValue) +{ + QStringList filteredStudyList; + if (!this->DicomDatabase) + { + logger.error("filterStudyList failed, no DICOM Database has been set. \n"); + return filteredStudyList; + } + + foreach (QString studyItem, studyList) + { + QString filter = this->DicomDatabase->fieldForStudy(filterName, studyItem); + if (filterName == "StudyDate") + { + int nDays = filterValue.toInt(); + if (nDays != -1) + { + QDate endDate = QDate::currentDate(); + QDate startDate = endDate.addDays(-nDays); + filter.replace(QString("-"), QString("")); + QDate studyDate = QDate::fromString(filter, "yyyyMMdd"); + if (studyDate < startDate || studyDate > endDate) + { + continue; + } + } + } + else if (!filter.contains(filterValue)) + { + continue; + } + + filteredStudyList.append(studyItem); + } + + return filteredStudyList; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringList& seriesList, + const QString &filterName, + const QString &filterValue) +{ + QStringList filteredSeriesList; + if (!this->DicomDatabase) + { + logger.error("filterSeriesList failed, no DICOM Database has been set. \n"); + return filteredSeriesList; + } + + foreach (QString seriesItem, seriesList) + { + QString filter = this->DicomDatabase->fieldForSeries(filterName, seriesItem); + if (!filter.contains(filterValue)) + { + continue; + } + + filteredSeriesList.append(seriesItem); + } + + return filteredSeriesList; +} + +//---------------------------------------------------------------------------- +QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringList &seriesList, + const QString &filterName, + const QStringList &filterValues) +{ + QStringList filteredSeriesList; + if (!this->DicomDatabase) + { + logger.error("filterSeriesList failed, no DICOM Database has been set. \n"); + return filteredSeriesList; + } + + foreach (QString seriesItem, seriesList) + { + QString filter = this->DicomDatabase->fieldForSeries(filterName, seriesItem); + if (!filterValues.contains("Any") && !filterValues.contains(filter)) + { + continue; + } + + filteredSeriesList.append(seriesItem); + } + + return filteredSeriesList; +} + +//---------------------------------------------------------------------------- +// ctkDICOMVisualBrowserWidget methods + +//---------------------------------------------------------------------------- +ctkDICOMVisualBrowserWidget::ctkDICOMVisualBrowserWidget(QWidget* parentWidget) + : Superclass(parentWidget) + , d_ptr(new ctkDICOMVisualBrowserWidgetPrivate(*this)) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->init(); +} + +//---------------------------------------------------------------------------- +ctkDICOMVisualBrowserWidget::~ctkDICOMVisualBrowserWidget() +{ +} + +//---------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidget::databaseDirectory() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + + // If override settings is specified then try to get database directory from there first + return d->DatabaseDirectory; +} + +//---------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidget::databaseDirectorySettingsKey() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->DatabaseDirectorySettingsKey; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setDatabaseDirectorySettingsKey(const QString &key) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->DatabaseDirectorySettingsKey = key; + + QSettings settings; + QString databaseDirectory = ctk::absolutePathFromInternal(settings.value(d->DatabaseDirectorySettingsKey, "").toString(), d->DatabaseDirectoryBase); + this->setDatabaseDirectory(databaseDirectory); +} + +//---------------------------------------------------------------------------- +static void skipDelete(QObject* obj) +{ + Q_UNUSED(obj); + // this deleter does not delete the object from memory + // useful if the pointer is not owned by the smart pointer +} + +//---------------------------------------------------------------------------- +ctkDICOMScheduler* ctkDICOMVisualBrowserWidget::scheduler()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->Scheduler.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMVisualBrowserWidget::schedulerShared()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->Scheduler; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setScheduler(ctkDICOMScheduler& Scheduler) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->disconnectScheduler(); + d->Scheduler = QSharedPointer(&Scheduler, skipDelete); + d->ServerNodeWidget->setScheduler(d->Scheduler); + d->connectScheduler(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setScheduler(QSharedPointer Scheduler) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->disconnectScheduler(); + d->Scheduler = Scheduler; + d->ServerNodeWidget->setScheduler(d->Scheduler); + d->connectScheduler(); +} + +//---------------------------------------------------------------------------- +ctkDICOMDatabase* ctkDICOMVisualBrowserWidget::dicomDatabase()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->DicomDatabase.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkDICOMVisualBrowserWidget::dicomDatabaseShared()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->DicomDatabase; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setTagsToPrecache(const QStringList tags) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("setTagsToPrecache failed, no DICOM Database has been set. \n"); + return; + } + + d->DicomDatabase->setTagsToPrecache(tags); +} + +//---------------------------------------------------------------------------- +const QStringList ctkDICOMVisualBrowserWidget::tagsToPrecache() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("Get tagsToPrecache failed, no DICOM Database has been set. \n"); + return QStringList(); + } + + return d->DicomDatabase->tagsToPrecache(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setStorageAETitle(QString storageAETitle) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->ServerNodeWidget->setStorageAETitle(storageAETitle); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidget::storageAETitle()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->storageAETitle(); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::setStoragePort(int storagePort) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->ServerNodeWidget->setStoragePort(storagePort); +} + +//---------------------------------------------------------------------------- +int ctkDICOMVisualBrowserWidget::storagePort()const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->storagePort(); +} + +//---------------------------------------------------------------------------- +int ctkDICOMVisualBrowserWidget::getNumberOfServers() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->getNumberOfServers(); +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMVisualBrowserWidget::getNthServer(int id) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->getNthServer(id); +} + +//---------------------------------------------------------------------------- +ctkDICOMServer* ctkDICOMVisualBrowserWidget::getServer(const char *connectionName) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->getServer(connectionName); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::addServer(ctkDICOMServer* server) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->addServer(server); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::removeServer(const char *connectionName) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->removeServer(connectionName); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::removeNthServer(int id) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->removeNthServer(id); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::removeAllServers() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->removeAllServers(); +} + +//---------------------------------------------------------------------------- +QString ctkDICOMVisualBrowserWidget::getServerNameFromIndex(int id) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->getServerNameFromIndex(id); +} + +//---------------------------------------------------------------------------- +int ctkDICOMVisualBrowserWidget::getServerIndexFromName(const char *connectionName) +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget->getServerIndexFromName(connectionName); +} + +//------------------------------------------------------------------------------ +ctkDICOMServerNodeWidget2 *ctkDICOMVisualBrowserWidget::serverSettingsWidget() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServerNodeWidget; +} + +//------------------------------------------------------------------------------ +ctkCollapsibleGroupBox *ctkDICOMVisualBrowserWidget::serverSettingsGroupBox() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->ServersSettingsCollapsibleGroupBox; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringPatientID(const QString& filteringPatientID) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringPatientID = filteringPatientID; + d->FilteringPatientIDSearchBox->setText(d->FilteringPatientID); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMVisualBrowserWidget::filteringPatientID() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringPatientID; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringPatientName(const QString& filteringPatientName) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringPatientName = filteringPatientName; + d->FilteringPatientNameSearchBox->setText(d->FilteringPatientName); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMVisualBrowserWidget::filteringPatientName() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringPatientName; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringStudyDescription(const QString& filteringStudyDescription) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringStudyDescription = filteringStudyDescription; + d->FilteringStudyDescriptionSearchBox->setText(d->FilteringStudyDescription); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMVisualBrowserWidget::filteringStudyDescription() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringStudyDescription; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType &filteringDate) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringDate = filteringDate; + d->FilteringDateComboBox->setCurrentIndex(d->FilteringDate); +} + +//------------------------------------------------------------------------------ +ctkDICOMPatientItemWidget::DateType ctkDICOMVisualBrowserWidget::filteringDate() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringDate; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringSeriesDescription(const QString& filteringSeriesDescription) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringSeriesDescription = filteringSeriesDescription; + d->FilteringSeriesDescriptionSearchBox->setText(d->FilteringSeriesDescription); +} + +//------------------------------------------------------------------------------ +QString ctkDICOMVisualBrowserWidget::filteringSeriesDescription() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringSeriesDescription; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setFilteringModalities(const QStringList &filteringModalities) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringModalities = filteringModalities; + d->updateModalityCheckableComboBox(); +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMVisualBrowserWidget::filteringModalities() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->FilteringModalities; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->NumberOfStudiesPerPatient = numberOfStudiesPerPatient; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::numberOfStudiesPerPatient() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->NumberOfStudiesPerPatient; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->NumberOfSeriesPerRow = numberOfSeriesPerRow; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::numberOfSeriesPerRow() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->NumberOfSeriesPerRow; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setMinimumThumbnailSize(int minimumThumbnailSize) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->MinimumThumbnailSize = minimumThumbnailSize; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::minimumThumbnailSize() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->MinimumThumbnailSize; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setSendActionVisible(bool visible) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->SendActionVisible = visible; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMVisualBrowserWidget::isSendActionVisible() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->SendActionVisible; +} + + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setDeleteActionVisible(bool visible) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->DeleteActionVisible = visible; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMVisualBrowserWidget::isDeleteActionVisible() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->DeleteActionVisible; +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::addPatientItemWidget(const QString& patientItem) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("addPatientItemWidget failed, no DICOM database has been set. \n"); + return; + } + + QString patientName = d->DicomDatabase->fieldForPatient("PatientsName", patientItem); + QString patientID = d->DicomDatabase->fieldForPatient("PatientID", patientItem); + + ctkDICOMPatientItemWidget *patientItemWidget = new ctkDICOMPatientItemWidget(this); + patientItemWidget->setPatientItem(patientItem); + patientItemWidget->setPatientID(patientID); + patientItemWidget->setFilteringStudyDescription(d->FilteringStudyDescription); + patientItemWidget->setFilteringDate(d->FilteringDate); + patientItemWidget->setFilteringSeriesDescription(d->FilteringSeriesDescription); + patientItemWidget->setFilteringModalities(d->FilteringModalities); + patientItemWidget->setMinimumThumbnailSize(d->MinimumThumbnailSize); + patientItemWidget->setNumberOfSeriesPerRow(d->NumberOfSeriesPerRow); + patientItemWidget->setNumberOfStudiesPerPatient(d->NumberOfStudiesPerPatient); + patientItemWidget->setDicomDatabase(d->DicomDatabase); + patientItemWidget->setScheduler(d->Scheduler); + patientItemWidget->setContextMenuPolicy(Qt::CustomContextMenu); + this->connect(patientItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(showPatientContextMenu(const QPoint&))); + + patientName.replace(R"(^)", R"( )"); + int index = d->PatientsTabWidget->addTab(patientItemWidget, QIcon(":/Icons/patient.svg"), patientName); + d->PatientsTabWidget->setTabWhatsThis(index, patientItem); +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidget::removePatientItemWidget(const QString &patientItem) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + + if (!patientItemWidget || patientItemWidget->patientItem() != patientItem) + { + continue; + } + + d->PatientsTabWidget->removeTab(patientIndex); + this->disconnect(patientItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(showPatientContextMenu(const QPoint&))); + delete patientItemWidget; + break; + } +} + +//------------------------------------------------------------------------------ +ctkDICOMPatientItemWidget* ctkDICOMVisualBrowserWidget::getPatientItemWidgetByPatientName(const QString &patientName) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + return nullptr; + } + + for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (!patientItemWidget) + { + continue; + } + + QString tempPatientName = d->DicomDatabase->fieldForPatient("PatientsName", patientItemWidget->patientItem()); + tempPatientName.replace(R"(^)", R"( )"); + if (tempPatientName != patientName) + { + continue; + } + + return patientItemWidget; + } + + return nullptr; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::patientsAddedDuringImport() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->PatientsAddedDuringImport; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::studiesAddedDuringImport() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->StudiesAddedDuringImport; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::seriesAddedDuringImport() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->SeriesAddedDuringImport; +} + +//------------------------------------------------------------------------------ +int ctkDICOMVisualBrowserWidget::instancesAddedDuringImport() +{ + Q_D(ctkDICOMVisualBrowserWidget); + return d->InstancesAddedDuringImport; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::resetItemsAddedDuringImportCounters() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->PatientsAddedDuringImport = 0; + d->StudiesAddedDuringImport = 0; + d->SeriesAddedDuringImport = 0; + d->InstancesAddedDuringImport = 0; +} + +//------------------------------------------------------------------------------ +ctkDICOMVisualBrowserWidget::ImportDirectoryMode ctkDICOMVisualBrowserWidget::importDirectoryMode() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + ctkDICOMVisualBrowserWidgetPrivate* mutable_d = const_cast(d); + mutable_d->importOldSettings(); + QSettings settings; + return static_cast(settings.value( + "DICOM/ImportDirectoryMode", static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryAddLink)).toInt() ); +} + +//------------------------------------------------------------------------------ +ctkFileDialog *ctkDICOMVisualBrowserWidget::importDialog() const +{ + Q_D(const ctkDICOMVisualBrowserWidget); + return d->ImportDialog; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setImportDirectoryMode(ImportDirectoryMode mode) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + QSettings settings; + settings.setValue("DICOM/ImportDirectoryMode", static_cast(mode)); + if (!d->ImportDialog) + { + return; + } + if (!(d->ImportDialog->options() & QFileDialog::DontUseNativeDialog)) + { + return; // Native dialog does not support modifying or getting widget elements. + } + QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild(); + comboBox->setCurrentIndex(comboBox->findData(mode)); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("setDatabaseDirectory failed, no DICOM database has been set. \n"); + return; + } + + QString absDirectory = ctk::absolutePathFromInternal(directory, d->DatabaseDirectoryBase); + + // close the active DICOM database + d->DicomDatabase->closeDatabase(); + + // open DICOM database on the directory + QString databaseFileName = QDir(absDirectory).filePath("ctkDICOM.sql"); + + bool success = true; + if (!QDir(absDirectory).exists() + || (!QDir(absDirectory).isEmpty() && !QFile(databaseFileName).exists())) + { + logger.warn(tr("Database folder does not contain ctkDICOM.sql file: ") + absDirectory + "\n"); + success = false; + } + + if (success) + { + bool databaseOpenSuccess = false; + try + { + d->DicomDatabase->openDatabase(databaseFileName); + databaseOpenSuccess = d->DicomDatabase->isOpen(); + } + catch (std::exception e) + { + databaseOpenSuccess = false; + } + if (!databaseOpenSuccess || d->DicomDatabase->schemaVersionLoaded().isEmpty()) + { + logger.warn(tr("Database error: %1 \n").arg(d->DicomDatabase->lastError())); + d->DicomDatabase->closeDatabase(); + success = false; + } + } + + if (success) + { + if (d->DicomDatabase->schemaVersionLoaded() != d->DicomDatabase->schemaVersion()) + { + logger.warn(tr("Database version mismatch: version of selected database = %1, version required = %2 \n") + .arg(d->DicomDatabase->schemaVersionLoaded()).arg(d->DicomDatabase->schemaVersion())); + d->DicomDatabase->closeDatabase(); + success = false; + } + } + + // Save new database directory in this object and in application settings. + d->DatabaseDirectory = absDirectory; + if (!d->DatabaseDirectorySettingsKey.isEmpty()) + { + QSettings settings; + settings.setValue(d->DatabaseDirectorySettingsKey, ctk::internalPathFromAbsolute(absDirectory, d->DatabaseDirectoryBase)); + settings.sync(); + } + + // pass DICOM database instance to Import widget + emit databaseDirectoryChanged(absDirectory); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::openImportDialog() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->ProgressFrame->show(); + d->ProgressDetailLineEdit->hide(); + int dialogCode = d->ImportDialog->exec(); + if (dialogCode == QDialog::Rejected) + { + d->ProgressFrame->hide(); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::importDirectories(QStringList directories, ImportDirectoryMode mode) +{ + Q_D(ctkDICOMVisualBrowserWidget); + foreach (const QString& directory, directories) + { + d->importDirectory(directory, mode); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::importDirectory(QString directory, ImportDirectoryMode mode) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->importDirectory(directory, mode); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::importFiles(const QStringList &files, ImportDirectoryMode mode) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->importFiles(files, mode); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::waitForImportFinished() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->Scheduler || !d->Indexer) + { + logger.error("waitForImportFinished failed, no task pool has been set. \n"); + return; + } + d->Indexer->waitForImportFinished(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onImportDirectory(QString directory, ImportDirectoryMode mode) +{ + this->importDirectory(directory, mode); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onIndexingProgress(int percent) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->ProgressBar->setValue(percent); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onIndexingProgressStep(const QString & step) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->ProgressLabel->setText(step); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onIndexingProgressDetail(const QString & detail) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + if (detail.isEmpty()) + { + d->ProgressDetailLineEdit->hide(); + } + else + { + d->ProgressDetailLineEdit->setText(detail); + d->ProgressDetailLineEdit->show(); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onIndexingComplete(int patientsAdded, int studiesAdded, int seriesAdded, int imagesAdded) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + d->PatientsAddedDuringImport += patientsAdded; + d->StudiesAddedDuringImport += studiesAdded; + d->SeriesAddedDuringImport += seriesAdded; + d->InstancesAddedDuringImport += imagesAdded; + + d->ProgressFrame->hide(); + d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); + + // allow users of this widget to know that the process has finished + emit directoryImported(); + + d->createPatients(); +} + +//------------------------------------------------------------------------------ +QStringList ctkDICOMVisualBrowserWidget::fileListForCurrentSelection(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("fileListForCurrentSelection failed, no DICOM database has been set. \n"); + return QStringList(); + } + + QStringList selectedSeriesUIDs = d->getSeriesUIDsFromWidgets(level, selectedWidgets); + + QStringList fileList; + foreach(const QString& selectedSeriesUID, selectedSeriesUIDs) + { + fileList << d->DicomDatabase->filesForSeries(selectedSeriesUID); + } + return fileList; +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::showMetadata(const QStringList &fileList) +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->MetadataDialog->setFileList(fileList); + d->MetadataDialog->show(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("removeSelectedItems failed, no DICOM database has been set. \n"); + return; + } + + QStringList selectedPatientUIDs; + QStringList selectedStudyUIDs; + QStringList selectedSeriesUIDs; + + if (level == ctkDICOMModel::RootType) + { + for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (!patientItemWidget) + { + continue; + } + QString patientItem = patientItemWidget->patientItem(); + QString patientID = patientItemWidget->patientID(); + selectedStudyUIDs << d->DicomDatabase->studiesForPatient(patientItem); + selectedPatientUIDs << patientID; + } + + if (!this->confirmDeleteSelectedUIDs(selectedPatientUIDs)) + { + return; + } + + d->removeAllPatientItemWidgets(); + } + else if (level == ctkDICOMModel::PatientType) + { + selectedPatientUIDs = d->getPatientUIDsFromWidgets(ctkDICOMModel::PatientType, selectedWidgets); + if (!this->confirmDeleteSelectedUIDs(selectedPatientUIDs)) + { + return; + } + } + else if (level == ctkDICOMModel::StudyType) + { + selectedStudyUIDs = d->getStudyUIDsFromWidgets(ctkDICOMModel::StudyType, selectedWidgets); + if (!this->confirmDeleteSelectedUIDs(selectedStudyUIDs)) + { + return; + } + } + else if (level == ctkDICOMModel::SeriesType) + { + selectedSeriesUIDs = d->getSeriesUIDsFromWidgets(ctkDICOMModel::SeriesType, selectedWidgets); + if (!this->confirmDeleteSelectedUIDs(selectedSeriesUIDs)) + { + return; + } + } + + foreach (QWidget *selectedWidget, selectedWidgets) + { + if (!selectedWidget) + { + continue; + } + else if (level == ctkDICOMModel::PatientType) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(selectedWidget); + if (patientItemWidget) + { + QString patientItem = patientItemWidget->patientItem(); + selectedStudyUIDs << d->DicomDatabase->studiesForPatient(patientItem); + + this->removePatientItemWidget(patientItem); + } + } + else if (level == ctkDICOMModel::StudyType) + { + ctkDICOMStudyItemWidget* studyItemWidget = + qobject_cast(selectedWidget); + if (studyItemWidget) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (patientItemWidget) + { + patientItemWidget->removeStudyItemWidget(studyItemWidget->studyItem()); + } + } + } + + if (level == ctkDICOMModel::SeriesType) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(selectedWidget); + if (seriesItemWidget) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (patientItemWidget) + { + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + { + if (!studyItemWidget || studyItemWidget->studyInstanceUID() != seriesItemWidget->studyInstanceUID()) + { + continue; + } + + studyItemWidget->removeSeriesItemWidget(seriesItemWidget->seriesItem()); + break; + } + } + } + } + else + { + foreach(const QString& uid, selectedStudyUIDs) + { + selectedSeriesUIDs << d->DicomDatabase->seriesForStudy(uid); + } + } + } + + // Stop fetching jobs for selected widgets. + d->Scheduler->stopJobsByUIDs(selectedPatientUIDs, + selectedStudyUIDs, + selectedSeriesUIDs); + + foreach(const QString & uid, selectedSeriesUIDs) + { + d->DicomDatabase->removeSeries(uid, false, level == ctkDICOMModel::RootType); + } + foreach(const QString & uid, selectedStudyUIDs) + { + d->DicomDatabase->removeStudy(uid, level == ctkDICOMModel::RootType); + } + foreach(const QString & uid, selectedPatientUIDs) + { + d->DicomDatabase->removePatient(uid, level == ctkDICOMModel::RootType); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringPatientIDChanged() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringPatientID = d->FilteringPatientIDSearchBox->text(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringPatientNameChanged() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringPatientName = d->FilteringPatientNameSearchBox->text(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringStudyDescriptionChanged() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringStudyDescription = d->FilteringStudyDescriptionSearchBox->text(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringSeriesDescriptionChanged() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringSeriesDescription = d->FilteringSeriesDescriptionSearchBox->text(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringModalityCheckableComboBoxChanged() +{ + Q_D(ctkDICOMVisualBrowserWidget); + + d->PreviousFilteringModalities = d->FilteringModalities; + d->FilteringModalities.clear(); + QModelIndexList indexList = d->FilteringModalityCheckableComboBox->checkedIndexes(); + foreach(QModelIndex index, indexList) + { + QVariant value = d->FilteringModalityCheckableComboBox->checkableModel()->data(index); + d->FilteringModalities.append(value.toString()); + } + d->updateModalityCheckableComboBox(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onFilteringDateComboBoxChanged(int index) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->FilteringDate = static_cast(index); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onQueryPatient() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (d->IsGUIUpdating) + { + return; + } + + if (!d->DicomDatabase) + { + logger.error("onQueryPatient failed, no DICOM database has been set. \n"); + return; + } + + // Stop any fetching task. + this->onStop(); + + // Clear the UI. + d->removeAllPatientItemWidgets(); + + bool filtersEmpty = + d->FilteringPatientID.isEmpty() && + d->FilteringPatientName.isEmpty() && + d->FilteringStudyDescription.isEmpty() && + d->FilteringSeriesDescription.isEmpty() && + d->FilteringDate == ctkDICOMPatientItemWidget::DateType::Any && + d->FilteringModalities.contains("Any"); + + if (d->DicomDatabase && + d->DicomDatabase->patients().count() == 0 && + filtersEmpty) + { + QString background = "QWidget { background-color: yellow }"; + d->FilteringPatientIDSearchBox->setStyleSheet(d->FilteringPatientIDSearchBox->styleSheet() + background); + d->FilteringPatientNameSearchBox->setStyleSheet(d->FilteringPatientNameSearchBox->styleSheet() + background); + d->FilteringDateComboBox->setStyleSheet(d->FilteringDateComboBox->styleSheet() + background); + d->FilteringStudyDescriptionSearchBox->setStyleSheet(d->FilteringStudyDescriptionSearchBox->styleSheet() + background); + d->FilteringSeriesDescriptionSearchBox->setStyleSheet(d->FilteringSeriesDescriptionSearchBox->styleSheet() + background); + d->FilteringModalityCheckableComboBox->setStyleSheet(d->FilteringModalityCheckableComboBox->styleSheet() + background); + + d->WarningPushButton->setText(tr("No filters have been set and no patients have been found in the local database." + "\nPlease set at least one filter to query the servers")); + d->WarningPushButton->show(); + d->patientsTabMenuToolButton->hide(); + return; + } + else + { + d->WarningPushButton->hide(); + } + + d->createPatients(); + + if (filtersEmpty || (d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() == 0)) + { + d->updateFiltersWarnings(); + } + else if (d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + { + QMap parameters; + parameters["Name"] = d->FilteringPatientName; + parameters["ID"] = d->FilteringPatientID; + parameters["Study"] = d->FilteringStudyDescription; + parameters["Series"] = d->FilteringSeriesDescription; + if (!d->FilteringModalities.contains("Any")) + { + parameters["Modalities"] = d->FilteringModalities; + } + + int nDays = ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(d->FilteringDate); + if (nDays != -1) + { + QDate endDate = QDate::currentDate(); + QString formattedEndDate = endDate.toString("yyyyMMdd"); + + QDate startDate = endDate.addDays(-nDays); + QString formattedStartDate = startDate.toString("yyyyMMdd"); + + parameters["StartDate"] = formattedStartDate; + parameters["EndDate"] = formattedEndDate; + } + + d->Scheduler->setFilters(parameters); + d->Scheduler->queryPatients(QThread::NormalPriority); + + d->QueryPatientPushButton->setIcon(QIcon(":/Icons/wait.svg")); + d->ProgressFrame->show(); + d->ProgressDetailLineEdit->hide(); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->ProgressFrame->hide(); + d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); + + ctkJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + d->updateFiltersWarnings(); + return; + } + else if (td.TypeOfJob == ctkDICOMJobResponseSet::JobType::QueryStudies || + td.TypeOfJob == ctkDICOMJobResponseSet::JobType::QuerySeries) + { + d->updateFiltersWarnings(); + return; + } + else if (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryPatients) + { + return; + } + + d->updateFiltersWarnings(); + if (td.NumberOfDataSets == 0) + { + d->WarningPushButton->setText(tr("The query provided no results. Please refine your filters.")); + d->WarningPushButton->show(); + } + + d->createPatients(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onTaskFailed(QString jobUID, QString jobType) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (jobType == "ctkDICOMQueryJob") + { + d->updateFiltersWarnings(); + d->ProgressFrame->hide(); + d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); + } + + d->WarningPushButton->setText(tr("%1 job failed to fetch the data." + "\nFor more information please open the error report console. \n").arg(jobUID)); + d->WarningPushButton->show(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onPatientItemChanged(int index) +{ + Q_D(ctkDICOMVisualBrowserWidget); + ctkDICOMPatientItemWidget* patientItem = + qobject_cast(d->PatientsTabWidget->widget(index)); + if (!patientItem || patientItem->patientItem().isEmpty()) + { + return; + } + + patientItem->generateStudies(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (!patientItemWidget) + { + return; + } + + QList selectedWidgets; + selectedWidgets.append(patientItemWidget); + + QPoint globalPos = patientItemWidget->mapToGlobal(point); + QMenu *patientMenu = new QMenu(); + + QString loadString = tr("Load patient files"); + QAction *loadAction = new QAction(loadString, patientMenu); + patientMenu->addAction(loadAction); + + QString metadataString = tr("View patient DICOM metadata"); + QAction *metadataAction = new QAction(metadataString, patientMenu); + patientMenu->addAction(metadataAction); + + QString deleteString = tr("Delete patient"); + QAction *deleteAction = new QAction(deleteString, patientMenu); + patientMenu->addAction(deleteAction); + deleteAction->setVisible(this->isDeleteActionVisible()); + + QString exportString = tr("Export patient to file system"); + QAction *exportAction = new QAction(exportString, patientMenu); + patientMenu->addAction(exportAction); + + QString sendString = tr("Send patient to DICOM server"); + QAction* sendAction = new QAction(sendString, patientMenu); + sendAction->setVisible(this->isSendActionVisible()); + patientMenu->addAction(sendAction); + + QAction *selectedAction = patientMenu->exec(globalPos); + if (selectedAction == loadAction) + { + // first select all the series for all studies + ctkDICOMPatientItemWidget* currentPatientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (currentPatientItemWidget) + { + currentPatientItemWidget->setSelection(true); + } + + this->onLoad(); + } + else if (selectedAction == metadataAction) + { + this->showMetadata(this->fileListForCurrentSelection(ctkDICOMModel::PatientType, selectedWidgets)); + } + else if (selectedAction == deleteAction) + { + this->removeSelectedItems(ctkDICOMModel::PatientType, selectedWidgets); + } + else if (selectedAction == exportAction) + { + this->exportSelectedItems(ctkDICOMModel::PatientType, selectedWidgets); + } + else if (selectedAction == sendAction) + { + emit sendRequested(this->fileListForCurrentSelection(ctkDICOMModel::PatientType, selectedWidgets)); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + ctkDICOMStudyItemWidget* studyItemWidget = + qobject_cast(QObject::sender()); + if (!studyItemWidget) + { + return; + } + + studyItemWidget->setSelection(true); + + ctkDICOMPatientItemWidget* currentPatientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (!currentPatientItemWidget) + { + return; + } + + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + QList selectedWidgets; + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + { + if (!studyItemWidget || !studyItemWidget->selection()) + { + continue; + } + + selectedWidgets.append(studyItemWidget); + } + + int numberOfSelectedStudies = selectedWidgets.count(); + + QPoint globalPos = studyItemWidget->mapToGlobal(point); + QMenu *studyMenu = new QMenu(); + + QString loadString = numberOfSelectedStudies == 1 ? tr("Load study") : + tr("Load %1 studies").arg(numberOfSelectedStudies); + QAction *loadAction = new QAction(loadString, studyMenu); + studyMenu->addAction(loadAction); + + QString metadataString = numberOfSelectedStudies == 1 ? tr("View study DICOM metadata") : + tr("View %1 studies DICOM metadata").arg(numberOfSelectedStudies); + QAction *metadataAction = new QAction(metadataString, studyMenu); + studyMenu->addAction(metadataAction); + + QString deleteString = numberOfSelectedStudies == 1 ? tr("Delete study") : + tr("Delete %1 studies").arg(numberOfSelectedStudies); + QAction *deleteAction = new QAction(deleteString, studyMenu); + studyMenu->addAction(deleteAction); + deleteAction->setVisible(this->isDeleteActionVisible()); + + QString exportString = numberOfSelectedStudies == 1 ? tr("Export study to file system") : + tr("Export %1 studies to file system").arg(numberOfSelectedStudies); + QAction *exportAction = new QAction(exportString, studyMenu); + studyMenu->addAction(exportAction); + + QString sendString = numberOfSelectedStudies == 1 ? tr("Send study to DICOM server") : + tr("Send %1 studies to DICOM server").arg(numberOfSelectedStudies); + QAction* sendAction = new QAction(sendString, studyMenu); + sendAction->setVisible(this->isSendActionVisible()); + studyMenu->addAction(sendAction); + + QAction *selectedAction = studyMenu->exec(globalPos); + if (selectedAction == loadAction) + { + this->onLoad(); + } + else if (selectedAction == metadataAction) + { + this->showMetadata(this->fileListForCurrentSelection(ctkDICOMModel::StudyType, selectedWidgets)); + } + else if (selectedAction == deleteAction) + { + this->removeSelectedItems(ctkDICOMModel::StudyType, selectedWidgets); + } + else if (selectedAction == exportAction) + { + this->exportSelectedItems(ctkDICOMModel::StudyType, selectedWidgets); + } + else if (selectedAction == sendAction) + { + emit sendRequested(this->fileListForCurrentSelection(ctkDICOMModel::StudyType, selectedWidgets)); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) +{ + Q_D(ctkDICOMVisualBrowserWidget); + + ctkDICOMSeriesItemWidget* selectedSeriesItemWidget = + qobject_cast(QObject::sender()); + if (!selectedSeriesItemWidget) + { + return; + } + + ctkDICOMPatientItemWidget* currentPatientItemWidget = + qobject_cast(d->PatientsTabWidget->currentWidget()); + if (!currentPatientItemWidget) + { + return; + } + + d->updateSeriesTablesSelection(selectedSeriesItemWidget); + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + QList selectedWidgets; + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + { + if (!studyItemWidget) + { + continue; + } + QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + QList selectedItems = seriesListTableWidget->selectedItems(); + foreach (QTableWidgetItem *selectedItem, selectedItems) + { + if (!selectedItem) + { + continue; + } + + int row = selectedItem->row(); + int column = selectedItem->column(); + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(seriesListTableWidget->cellWidget(row, column)); + + selectedWidgets.append(seriesItemWidget); + } + } + + int numberOfSelectedSeries = selectedWidgets.count(); + + QPoint globalPos = selectedSeriesItemWidget->mapToGlobal(point); + QMenu *seriesMenu = new QMenu(); + + QString loadString = numberOfSelectedSeries == 1 ? tr("Load series") : + tr("Load %1 series").arg(numberOfSelectedSeries); + QAction *loadAction = new QAction(loadString, seriesMenu); + seriesMenu->addAction(loadAction); + + QString metadataString = numberOfSelectedSeries == 1 ? tr("View series DICOM metadata") : + tr("View %1 series DICOM metadata").arg(numberOfSelectedSeries); + QAction *metadataAction = new QAction(metadataString, seriesMenu); + seriesMenu->addAction(metadataAction); + + QString deleteString = numberOfSelectedSeries == 1 ? tr("Delete series") : + tr("Delete %1 series").arg(numberOfSelectedSeries); + QAction *deleteAction = new QAction(deleteString, seriesMenu); + seriesMenu->addAction(deleteAction); + deleteAction->setVisible(this->isDeleteActionVisible()); + + QString exportString = numberOfSelectedSeries == 1 ? tr("Export series to file system") : + tr("Export %1 series to file system").arg(numberOfSelectedSeries); + QAction *exportAction = new QAction(exportString, seriesMenu); + seriesMenu->addAction(exportAction); + + QString sendString = numberOfSelectedSeries == 1 ? tr("Send series to DICOM server") : + tr("Send %1 series to DICOM server").arg(numberOfSelectedSeries); + QAction* sendAction = new QAction(sendString, seriesMenu); + sendAction->setVisible(this->isSendActionVisible()); + seriesMenu->addAction(sendAction); + + QAction *selectedAction = seriesMenu->exec(globalPos); + if (selectedAction == loadAction) + { + this->onLoad(); + } + else if (selectedAction == metadataAction) + { + this->showMetadata(this->fileListForCurrentSelection(ctkDICOMModel::SeriesType, selectedWidgets)); + } + else if (selectedAction == deleteAction) + { + this->removeSelectedItems(ctkDICOMModel::SeriesType, selectedWidgets); + } + else if (selectedAction == exportAction) + { + this->exportSelectedItems(ctkDICOMModel::SeriesType, selectedWidgets); + } + else if (selectedAction == sendAction) + { + emit sendRequested(this->fileListForCurrentSelection(ctkDICOMModel::SeriesType, selectedWidgets)); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() +{ + Q_D(ctkDICOMVisualBrowserWidget); + + QPoint globalPos = this->mapToGlobal(d->patientsTabMenuToolButton->geometry().bottomLeft()); + globalPos.setY(globalPos.y() + d->patientsTabMenuToolButton->height() * 3); + globalPos.setX(globalPos.x()); + + QMenu *patientMenu = new QMenu(); + patientMenu->move(globalPos); + for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) + { + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (!patientItemWidget) + { + continue; + } + + QString patientItem = patientItemWidget->patientItem(); + QString patientName = d->DicomDatabase->fieldForPatient("PatientsName", patientItem); + patientName.replace(R"(^)", R"( )"); + QAction *changePatientAction = new QAction(patientName, patientMenu); + if (patientItemWidget == d->PatientsTabWidget->currentWidget()) + { + changePatientAction->setIcon(QIcon(":Icons/patient.svg")); + QFont font(changePatientAction->font()); + font.setBold(true); + changePatientAction->setFont(font); + } + patientMenu->addAction(changePatientAction); + } + + patientMenu->addSeparator(); + QString deleteString = tr("Delete all Patients"); + QAction *deleteAction = new QAction(deleteString, patientMenu); + deleteAction->setIcon(QIcon(":Icons/delete.svg")); + patientMenu->addAction(deleteAction); + deleteAction->setVisible(this->isDeleteActionVisible()); + + QAction *selectedAction = patientMenu->exec(globalPos); + if (selectedAction == deleteAction) + { + this->removeSelectedItems(ctkDICOMModel::RootType); + d->patientsTabMenuToolButton->hide(); + } + else if (selectedAction) + { + QString patientName = selectedAction->text(); + ctkDICOMPatientItemWidget *patientItemWidget = this->getPatientItemWidgetByPatientName(patientName); + if (patientItemWidget) + { + d->PatientsTabWidget->setCurrentWidget(patientItemWidget); + } + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::exportSelectedItems(ctkDICOMModel::IndexType level, + QList selectedWidgets) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("exportSelectedItems failed, no DICOM database has been set. \n"); + return; + } + + ctkFileDialog* directoryDialog = new ctkFileDialog(); + directoryDialog->setOption(QFileDialog::ShowDirsOnly); + directoryDialog->setFileMode(QFileDialog::DirectoryOnly); + bool res = directoryDialog->exec(); + if (!res) + { + delete directoryDialog; + return; + } + QStringList dirs = directoryDialog->selectedFiles(); + delete directoryDialog; + QString dirPath = dirs[0]; + + QStringList selectedSeriesUIDs = d->getSeriesUIDsFromWidgets(level, selectedWidgets); + + this->exportSeries(dirPath, selectedSeriesUIDs); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("exportSeries failed, no DICOM database has been set. \n"); + return; + } + + foreach (const QString& uid, uids) + { + QStringList filesForSeries = d->DicomDatabase->filesForSeries(uid); + + // Use the first file to get the overall series information + QString firstFilePath = filesForSeries[0]; + QHash descriptions (d->DicomDatabase->descriptionsForFile(firstFilePath)); + QString patientName = descriptions["PatientsName"]; + QString patientIDTag = QString("0010,0020"); + QString patientID = d->DicomDatabase->fileValue(firstFilePath, patientIDTag); + QString studyDescription = descriptions["StudyDescription"]; + QString seriesDescription = descriptions["SeriesDescription"]; + QString studyDateTag = QString("0008,0020"); + QString studyDate = d->DicomDatabase->fileValue(firstFilePath,studyDateTag); + QString seriesNumberTag = QString("0020,0011"); + QString seriesNumber = d->DicomDatabase->fileValue(firstFilePath,seriesNumberTag); + + QString sep = "/"; + QString nameSep = "-"; + QString destinationDir = dirPath + sep + d->filenameSafeString(patientID); + if (!patientName.isEmpty()) + { + destinationDir += nameSep + d->filenameSafeString(patientName); + } + destinationDir += sep + d->filenameSafeString(studyDate); + if (!studyDescription.isEmpty()) + { + destinationDir += nameSep + d->filenameSafeString(studyDescription); + } + destinationDir += sep + d->filenameSafeString(seriesNumber); + if (!seriesDescription.isEmpty()) + { + destinationDir += nameSep + d->filenameSafeString(seriesDescription); + } + destinationDir += sep; + + // create the destination directory if necessary + if (!QDir().exists(destinationDir)) + { + if (!QDir().mkpath(destinationDir)) + { + //: %1 is the destination directory + QString errorString = tr("Unable to create export destination directory:\n\n%1" + "\n\nHalting export.") + .arg(destinationDir); + ctkMessageBox createDirectoryErrorMessageBox(this); + createDirectoryErrorMessageBox.setText(errorString); + createDirectoryErrorMessageBox.setIcon(QMessageBox::Warning); + createDirectoryErrorMessageBox.exec(); + return; + } + } + + int fileNumber = 0; + foreach (const QString& filePath, filesForSeries) + { + // File name example: my/destination/folder/000001.dcm + QString destinationFileName = QStringLiteral("%1%2.dcm").arg(destinationDir).arg(fileNumber, 6, 10, QLatin1Char('0')); + + if (!QFile::exists(filePath)) + { + //: %1 is the file path + QString errorString = tr("Export source file not found:\n\n%1" + "\n\nHalting export.\n\nError may be fixed via Repair.") + .arg(filePath); + ctkMessageBox copyErrorMessageBox(this); + copyErrorMessageBox.setText(errorString); + copyErrorMessageBox.setIcon(QMessageBox::Warning); + copyErrorMessageBox.exec(); + return; + } + if (QFile::exists(destinationFileName)) + { + //: %1 is the destination file name + QString errorString = tr("Export destination file already exists:\n\n%1" + "\n\nHalting export.") + .arg(destinationFileName); + ctkMessageBox copyErrorMessageBox(this); + copyErrorMessageBox.setText(errorString); + copyErrorMessageBox.setIcon(QMessageBox::Warning); + copyErrorMessageBox.exec(); + return; + } + + bool copyResult = QFile::copy(filePath, destinationFileName); + if (!copyResult) + { + //: %1 and %2 refers to source and destination file paths + QString errorString = tr("Failed to copy\n\n%1\n\nto\n\n%2" + "\n\nHalting export.") + .arg(filePath) + .arg(destinationFileName); + ctkMessageBox copyErrorMessageBox(this); + copyErrorMessageBox.setText(errorString); + copyErrorMessageBox.setIcon(QMessageBox::Warning); + copyErrorMessageBox.exec(); + return; + } + + fileNumber++; + } + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onImportDirectoriesSelected(QStringList directories) +{ + Q_D(ctkDICOMVisualBrowserWidget); + this->importDirectories(directories, this->importDirectoryMode()); + d->updateFiltersWarnings(); + + // Clear selection + d->ImportDialog->clearSelection(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onImportDirectoryComboBoxCurrentIndexChanged(int index) +{ + Q_D(ctkDICOMVisualBrowserWidget); + Q_UNUSED(index); + if (!(d->ImportDialog->options() & QFileDialog::DontUseNativeDialog)) + { + return; // Native dialog does not support modifying or getting widget elements. + } + QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild(); + ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = + static_cast(comboBox->itemData(index).toInt()); + this->setImportDirectoryMode(mode); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onClose() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (d->IsGUIUpdating) + { + return; + } + + this->onStop(); + this->close(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onLoad() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (d->IsGUIUpdating) + { + return; + } + + d->retrieveSeries(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onImport() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (d->IsGUIUpdating) + { + return; + } + + if (d->Scheduler && + d->Scheduler->numberOfJobs() > d->Scheduler->numberOfPersistentJobs()) + { + QString warningString = tr("The browser is already fetching/importing data." + "\n\n The queued tasks will be deleted. The running tasks will be stopped."); + ctkMessageBox warningMessageBox(this); + warningMessageBox.setText(warningString); + warningMessageBox.setIcon(QMessageBox::Warning); + warningMessageBox.exec(); + + this->onStop(); + } + + this->openImportDialog(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onStop(bool stopPersistentTasks) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->Scheduler) + { + return; + } + + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + d->Scheduler->stopAllJobs(stopPersistentTasks); + d->updateFiltersWarnings(); + d->ProgressFrame->hide(); + d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); + QApplication::restoreOverrideCursor(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setCurrentTabWidget(ctkDICOMPatientItemWidget *patientItemWidget) +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->PatientsTabWidget->setCurrentWidget(patientItemWidget); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::closeEvent(QCloseEvent *event) +{ + this->onStop(); + event->accept(); +} + +//------------------------------------------------------------------------------ +bool ctkDICOMVisualBrowserWidget::confirmDeleteSelectedUIDs(QStringList uids) +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (!d->DicomDatabase) + { + logger.error("confirmDeleteSelectedUIDs failed, no DICOM database has been set. \n"); + return false; + } + + if (uids.isEmpty()) + { + return false; + } + + ctkMessageBox confirmDeleteDialog(this); + QString message = tr("Do you want to delete the following selected items from the LOCAL database? \n" + "The data will not be deleted from the PACs server. \n"); + + // add the information about the selected UIDs + int numUIDs = uids.size(); + for (int i = 0; i < numUIDs; ++i) + { + QString uid = uids.at(i); + + // try using the given UID to find a descriptive string + QString patientName = d->DicomDatabase->nameForPatient(uid); + QString studyDescription = d->DicomDatabase->descriptionForStudy(uid); + QString seriesDescription = d->DicomDatabase->descriptionForSeries(uid); + + if (!patientName.isEmpty()) + { + message += QString("\n") + patientName; + } + else if (!studyDescription.isEmpty()) + { + message += QString("\n") + studyDescription; + } + else if (!seriesDescription.isEmpty()) + { + message += QString("\n") + seriesDescription; + } + else + { + // if all other descriptors are empty, use the UID + message += QString("\n") + uid; + } + } + confirmDeleteDialog.setText(message); + confirmDeleteDialog.setIcon(QMessageBox::Question); + + confirmDeleteDialog.addButton(tr("Delete"), QMessageBox::AcceptRole); + confirmDeleteDialog.addButton(tr("Cancel"), QMessageBox::RejectRole); + confirmDeleteDialog.setDontShowAgainSettingsKey("VisualDICOMBrowser/DontConfirmDeleteSelected"); + + int response = confirmDeleteDialog.exec(); + + if (response == QMessageBox::AcceptRole) + { + return true; + } + else + { + return false; + } +} diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h new file mode 100644 index 0000000000..04664b998a --- /dev/null +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -0,0 +1,373 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Davide Punzo, punzodavide@hotmail.it, + and development was supported by the Center for Intelligent Image-guided Interventions (CI3). + +=========================================================================*/ + +#ifndef __ctkDICOMVisualBrowserWidget_h +#define __ctkDICOMVisualBrowserWidget_h + +#include "ctkDICOMWidgetsExport.h" + +// Qt includes +#include +#include + +// CTK includes +#include +#include "ctkDICOMModel.h" +#include "ctkErrorLogLevel.h" + +class ctkCollapsibleGroupBox; +class ctkDICOMVisualBrowserWidgetPrivate; +class ctkDICOMDatabase; +class ctkFileDialog; +class ctkDICOMScheduler; +class ctkDICOMServer; +class ctkDICOMServerNodeWidget2; +class ctkDICOMJobResponseSet; + +/// \ingroup DICOM_Widgets +/// +/// \brief The DICOM visual browser widget provides an interface to organize DICOM +/// data stored in a local/server ctkDICOMDatabases. +/// +/// Using a local database avoids redundant calculations and speed up subsequent +/// access. +/// +/// The operations are queued by the scheduler into jobs with a priority and +/// executed by workers in separate threads. +/// +/// Supported operations are: +/// +/// * Filtering and navigation with thumbnails of local database and servers results +/// * Import from file system to local database +/// * Query/Retrieve from servers (DIMSE C-GET/C-MOVE ) +/// * Storage listener +/// * Send (emits only a signal for the moment, requires external implementation) +/// * Remove (only from local database, not from server) +/// * Metadata exploration +/// +class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget +{ + Q_OBJECT; + Q_ENUMS(ImportDirectoryMode) + Q_PROPERTY(QString databaseDirectory READ databaseDirectory WRITE setDatabaseDirectory) + Q_PROPERTY(QString databaseDirectorySettingsKey READ databaseDirectorySettingsKey WRITE setDatabaseDirectorySettingsKey) + Q_PROPERTY(QString databaseDirectoryBase READ databaseDirectoryBase WRITE setDatabaseDirectoryBase) + Q_PROPERTY(QString filteringPatientID READ filteringPatientID WRITE setFilteringPatientID); + Q_PROPERTY(QString filteringPatientName READ filteringPatientName WRITE setFilteringPatientName); + Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); + Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); + Q_PROPERTY(int minimumThumbnailSize READ minimumThumbnailSize WRITE setMinimumThumbnailSize); + Q_PROPERTY(bool sendActionVisible READ isSendActionVisible WRITE setSendActionVisible) + Q_PROPERTY(bool deleteActionVisible READ isDeleteActionVisible WRITE setDeleteActionVisible) + Q_PROPERTY(QString storageAETitle READ storageAETitle WRITE setStorageAETitle); + Q_PROPERTY(int storagePort READ storagePort WRITE setStoragePort); + +public: + typedef QWidget Superclass; + explicit ctkDICOMVisualBrowserWidget(QWidget* parent = nullptr); + virtual ~ctkDICOMVisualBrowserWidget(); + + /// Directory being used to store the dicom database + QString databaseDirectory() const; + + /// Get settings key used to store DatabaseDirectory in application settings. + QString databaseDirectorySettingsKey() const; + + /// Set settings key that stores DatabaseDirectory in application settings. + /// Calling this method sets DatabaseDirectory from current value stored in the settings + /// (overwriting current value of DatabaseDirectory). + void setDatabaseDirectorySettingsKey(const QString& settingsKey); + + /// Get the directory that will be used as a basis if databaseDirectory is specified with a relative path. + /// @see setDatabaseDirectoryBase, setDatabaseDirectory + QString databaseDirectoryBase() const; + + /// Set the directory that will be used as a basis if databaseDirectory is specified with a relative path. + /// If DatabaseDirectoryBase is empty (by default it is) then the current working directory is used as a basis. + /// @see databaseDirectoryBase, setDatabaseDirectory + void setDatabaseDirectoryBase(const QString& base); + + /// Return the task pool. + Q_INVOKABLE ctkDICOMScheduler* scheduler() const; + /// Return the task pool as a shared pointer + /// (not Python-wrappable). + QSharedPointer schedulerShared() const; + /// Set the task pool. + Q_INVOKABLE void setScheduler(ctkDICOMScheduler& scheduler); + /// Set the task pool as a shared pointer + /// (not Python-wrappable). + void setScheduler(QSharedPointer scheduler); + + /// Return the Dicom Database. + Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; + /// Return Dicom Database as a shared pointer + /// (not Python-wrappable). + QSharedPointer dicomDatabaseShared() const; + + /// See ctkDICOMDatabase for description - these accessors + /// delegate to the corresponding routines of the internal + /// instance of the database. + /// @see ctkDICOMDatabase + Q_INVOKABLE void setTagsToPrecache(const QStringList tags); + Q_INVOKABLE const QStringList tagsToPrecache(); + + /// Storage AE title + /// "CTKSTORE" by default + void setStorageAETitle(QString storageAETitle); + QString storageAETitle() const; + + /// Storage port + /// 11112 by default + void setStoragePort(int storagePort); + int storagePort() const; + + /// Servers + Q_INVOKABLE int getNumberOfServers(); + Q_INVOKABLE ctkDICOMServer* getNthServer(int id); + Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE void addServer(ctkDICOMServer* server); + Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeNthServer(int id); + Q_INVOKABLE void removeAllServers(); + Q_INVOKABLE QString getServerNameFromIndex(int id); + Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + Q_INVOKABLE ctkDICOMServerNodeWidget2* serverSettingsWidget(); + Q_INVOKABLE ctkCollapsibleGroupBox* serverSettingsGroupBox(); + + /// Query Filters + /// Empty by default + void setFilteringPatientID(const QString& filteringPatientID); + QString filteringPatientID() const; + /// Empty by default + void setFilteringPatientName(const QString& filteringPatientName); + QString filteringPatientName() const; + /// Empty by default + void setFilteringStudyDescription(const QString& filteringStudyDescription); + QString filteringStudyDescription() const; + /// Available values: + /// Any, + /// Today, + /// Yesterday, + /// LastWeek, + /// LastMonth, + /// LastYear. + /// Any by default. + void setFilteringDate(const ctkDICOMPatientItemWidget::DateType& filteringDate); + ctkDICOMPatientItemWidget::DateType filteringDate() const; + /// Empty by default + void setFilteringSeriesDescription(const QString& filteringSeriesDescription); + QString filteringSeriesDescription() const; + /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default + void setFilteringModalities(const QStringList& filteringModalities); + QStringList filteringModalities() const; + + /// Number of non collapsed studies per patient + /// 2 by default + void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); + int numberOfStudiesPerPatient() const; + + /// Number of series displayed per row + /// 6 by default + void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); + int numberOfSeriesPerRow() const; + + /// Minimum thumbnail size in pixel + /// 300 by default + void setMinimumThumbnailSize(int minimumThumbnailSize); + int minimumThumbnailSize() const; + + /// Set if send action on right click context menu is available + /// false by default + void setSendActionVisible(bool visible); + bool isSendActionVisible() const; + + /// Set if cancel action on right click context menu is available + /// true by default + void setDeleteActionVisible(bool visible); + bool isDeleteActionVisible() const; + + /// Add/Remove Patient item widget + Q_INVOKABLE void addPatientItemWidget(const QString& patientItem); + Q_INVOKABLE void removePatientItemWidget(const QString& patientItem); + + /// Get Patient item widget + Q_INVOKABLE ctkDICOMPatientItemWidget* getPatientItemWidgetByPatientName(const QString& patientName); + + /// Accessors to status of last directory import operation + int patientsAddedDuringImport(); + int studiesAddedDuringImport(); + int seriesAddedDuringImport(); + int instancesAddedDuringImport(); + + /// Set counters of imported patients, studies, series, instances to zero. + void resetItemsAddedDuringImportCounters(); + + enum ImportDirectoryMode + { + ImportDirectoryCopy = 0, + ImportDirectoryAddLink + }; + + /// \brief Get value of ImportDirectoryMode settings. + /// + /// \sa setImportDirectoryMode(ctkDICOMBrowser::ImportDirectoryMode) + ImportDirectoryMode importDirectoryMode()const; + + /// \brief Return instance of import dialog. + /// + /// \internal + Q_INVOKABLE ctkFileDialog* importDialog()const; + +public Q_SLOTS: + /// \brief Set value of ImportDirectoryMode settings. + /// + /// Setting the value will update the comboBox found at the bottom + /// of the import dialog. + /// + /// \sa importDirectoryMode() + void setImportDirectoryMode(ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); + + void setDatabaseDirectory(const QString& directory); + + /// \brief Pop-up file dialog allowing to select and import one or multiple + /// DICOM directories. + /// + /// The dialog is extended with two additional controls: + /// + /// * **ImportDirectoryMode** combox: Allow user to select "Add Link" or "Copy" mode. + /// Associated settings is stored using key `DICOM/ImportDirectoryMode`. + void openImportDialog(); + + /// \brief Import directories + /// + /// This can be used to externally trigger an import (i.e. for testing or to support drag-and-drop) + /// + /// By default, \a mode is ImportDirectoryMode::ImportDirectoryAddLink is set. + /// + /// \sa importDirectory(QString directory, int mode) + void importDirectories(QStringList directories, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + + /// \brief Import a directory + /// + /// This can be used to externally trigger an import (i.e. for testing or to support drag-and-drop) + /// + /// By default, \a mode is ImportDirectoryMode::ImportDirectoryAddLink is set. + void importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + + /// \brief Import a list of files + /// + /// This can be used to externally trigger an import (i.e. for testing or to support drag-and-drop) + /// + /// By default, \a mode is ImportDirectoryMode::ImportDirectoryAddLink is set. + void importFiles(const QStringList& files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + + /// Wait for all import operations to complete. + /// Number of imported patients, studies, series, images since the last resetItemsAddedDuringImportCounters + /// can be retrieved by calling patientsAddedDuringImport(), studiesAddedDuringImport(), seriesAddedDuringImport(), + /// instancesAddedDuringImport() methods. + void waitForImportFinished(); + + /// \deprecated importDirectory() should be used + void onImportDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + + /// slots to capture status updates from the database during an + /// import operation + void onIndexingProgress(int); + void onIndexingProgressStep(const QString&); + void onIndexingProgressDetail(const QString&); + void onIndexingComplete(int patientsAdded, int studiesAdded, int seriesAdded, int imagesAdded); + + void onFilteringPatientIDChanged(); + void onFilteringPatientNameChanged(); + void onFilteringStudyDescriptionChanged(); + void onFilteringSeriesDescriptionChanged(); + void onFilteringModalityCheckableComboBoxChanged(); + void onFilteringDateComboBoxChanged(int); + void onQueryPatient(); + void updateGUIFromScheduler(QVariant); + void onTaskFailed(QString, QString); + void onPatientItemChanged(int); + void onClose(); + void onLoad(); + void onImport(); + void onStop(bool stopPersistentTasks = false); + void setCurrentTabWidget(ctkDICOMPatientItemWidget *patientItemWidget); + +Q_SIGNALS: + /// Emitted when directory is changed + void databaseDirectoryChanged(const QString&); + /// Emitted when retrieveSeries finish to retrieve the series. + void seriesRetrieved(const QStringList& seriesInstanceUIDs); + /// Emitted when user requested network send. String list contains list of files to be exported. + void sendRequested(const QStringList&); + /// Emitted when the directory import operation has completed + void directoryImported(); + +protected: + void closeEvent(QCloseEvent *); + QScopedPointer d_ptr; + + /// Confirm with the user that they wish to delete the selected uids. + /// Add information about the selected UIDs to a message box, checks + /// for patient name, series description, study description, if all + /// empty, uses the UID. + /// Returns true if the user confirms the delete, false otherwise. + /// Remembers if the user doesn't want to show the confirmation again. + bool confirmDeleteSelectedUIDs(QStringList uids); + + /// Get file list for right click selection + QStringList fileListForCurrentSelection(ctkDICOMModel::IndexType level, QList selectedWidget); + /// Show window that displays DICOM fields of all selected items + void showMetadata(const QStringList& fileList); + /// Remove items (both database and widget) + void removeSelectedItems(ctkDICOMModel::IndexType level, QList selectedWidgets = QList()); + /// Export the items associated with the selected widget + void exportSelectedItems(ctkDICOMModel::IndexType level, QList selectedWidgets); + /// Export the series associated with the selected UIDs + void exportSeries(QString dirPath, QStringList uids); + +protected Q_SLOTS: + /// \brief Import directories + /// + /// This is used when user selected one or multiple + /// directories from the Import Dialog. + /// + /// \sa importDirectories(QString directory, int mode) + void onImportDirectoriesSelected(QStringList directories); + void onImportDirectoryComboBoxCurrentIndexChanged(int index); + + /// Called when a right mouse click is made on a tab of the patient tab widget + void showPatientContextMenu(const QPoint& point); + /// Called when a right mouse click is made in the studies table + void showStudyContextMenu(const QPoint& point); + /// Called when a right mouse click is made in the studies table + void showSeriesContextMenu(const QPoint& point); + /// Called when clicking patients tab menu + void onPatientsTabMenuToolButtonClicked(); + +private: + Q_DECLARE_PRIVATE(ctkDICOMVisualBrowserWidget); + Q_DISABLE_COPY(ctkDICOMVisualBrowserWidget); +}; + +#endif diff --git a/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui b/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui index b655e6e29c..abb2bf3810 100644 --- a/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui +++ b/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui @@ -6,29 +6,120 @@ 0 0 - 141 - 133 + 244 + 170
Thumbnail - - + + + 4 + + + 4 + + + 4 + + 4 0 - - - - Qt::AlignCenter + + + + + 0 + 0 + + + + QFrame::NoFrame + + QFrame::Raised + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 7 + + + + + 16777215 + 7 + + + + + + + 0 + + + Qt::AlignCenter + + + false + + + Qt::Horizontal + + + false + + + QProgressBar::TopToBottom + + + + - - + + Qt::AlignCenter diff --git a/Libs/Widgets/ctkThumbnailLabel.cpp b/Libs/Widgets/ctkThumbnailLabel.cpp index 71879d211a..6a42223acd 100644 --- a/Libs/Widgets/ctkThumbnailLabel.cpp +++ b/Libs/Widgets/ctkThumbnailLabel.cpp @@ -88,14 +88,43 @@ void ctkThumbnailLabelPrivate::setupUi(QWidget* widget) q->layout()->setSizeConstraint(QLayout::SetNoConstraint); // no text by default q->setText(QString()); + this->OperationProgressBar->hide(); } //---------------------------------------------------------------------------- void ctkThumbnailLabelPrivate::updateThumbnail() { + Q_Q(ctkThumbnailLabel); + QSize size = q->size(); + + if (this->TextLabel->isVisible()) + { + if (this->TextPosition & Qt::AlignTop) + { + size.setHeight(size.height() - this->TextLabel->height()); + } + else if (this->TextPosition & Qt::AlignBottom) + { + size.setHeight(size.height() - this->TextLabel->height()); + } + else if (this->TextPosition & Qt::AlignLeft) + { + size.setWidth(size.width() - this->TextLabel->width()); + } + else if (this->TextPosition & Qt::AlignRight) + { + size.setWidth(size.width() - this->TextLabel->width()); + } + } + + if (this->OperationProgressBar->isVisible()) + { + size.setHeight(size.height() - this->OperationProgressBar->height()); + } + this->PixmapLabel->setPixmap( this->OriginalThumbnail.isNull() ? QPixmap() : - this->OriginalThumbnail.scaled(this->PixmapLabel->size(), + this->OriginalThumbnail.scaled(size, Qt::KeepAspectRatio, this->TransformationMode)); } @@ -118,6 +147,34 @@ ctkThumbnailLabel::~ctkThumbnailLabel() { } +//---------------------------------------------------------------------------- +QLabel* ctkThumbnailLabel::textLabel() +{ + Q_D(ctkThumbnailLabel); + return d->TextLabel; +} + +//---------------------------------------------------------------------------- +QFrame *ctkThumbnailLabel::pixmapFrame() +{ + Q_D(ctkThumbnailLabel); + return d->PixmapFrame; +} + +//---------------------------------------------------------------------------- +QLabel* ctkThumbnailLabel::pixmapLabel() +{ + Q_D(ctkThumbnailLabel); + return d->PixmapLabel; +} + +//---------------------------------------------------------------------------- +QProgressBar *ctkThumbnailLabel::operationProgressBar() +{ + Q_D(ctkThumbnailLabel); + return d->OperationProgressBar; +} + //---------------------------------------------------------------------------- void ctkThumbnailLabel::setText(const QString &text) { @@ -214,6 +271,20 @@ const QPixmap* ctkThumbnailLabel::pixmap()const return d->OriginalThumbnail.isNull() ? 0 : &(d->OriginalThumbnail); } +//---------------------------------------------------------------------------- +int ctkThumbnailLabel::operationProgress() const +{ + Q_D(const ctkThumbnailLabel); + return d->OperationProgressBar->value(); +} + +//---------------------------------------------------------------------------- +void ctkThumbnailLabel::setOperationProgress(const int &progress) +{ + Q_D(ctkThumbnailLabel); + d->OperationProgressBar->setValue(progress); +} + //---------------------------------------------------------------------------- Qt::TransformationMode ctkThumbnailLabel::transformationMode()const { diff --git a/Libs/Widgets/ctkThumbnailLabel.h b/Libs/Widgets/ctkThumbnailLabel.h index ed22a94ba1..418c13fcf4 100644 --- a/Libs/Widgets/ctkThumbnailLabel.h +++ b/Libs/Widgets/ctkThumbnailLabel.h @@ -29,6 +29,10 @@ class ctkThumbnailLabelPrivate; +class QFrame; +class QLabel; +class QProgressBar; + /// \ingroup Widgets /// ctkThumbnailLabel is an advanced label that gives control over /// the pixmap size and text location. @@ -49,6 +53,8 @@ class CTK_WIDGETS_EXPORT ctkThumbnailLabel : public QWidget /// Optional pixmap for the label. /// No pixmap by default Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) + /// Progress bar status. + Q_PROPERTY(int operationProgress READ operationProgress WRITE setOperationProgress) /// Controls the quality of the resizing of the pixmap. /// Qt::FastTransformation by default Q_PROPERTY(Qt::TransformationMode transformationMode READ transformationMode WRITE setTransformationMode) @@ -64,6 +70,11 @@ class CTK_WIDGETS_EXPORT ctkThumbnailLabel : public QWidget explicit ctkThumbnailLabel(QWidget* parent=0); virtual ~ctkThumbnailLabel(); + Q_INVOKABLE QLabel* textLabel(); + Q_INVOKABLE QFrame* pixmapFrame(); + Q_INVOKABLE QLabel* pixmapLabel(); + Q_INVOKABLE QProgressBar* operationProgressBar(); + void setText(const QString& text); QString text()const; @@ -73,6 +84,9 @@ class CTK_WIDGETS_EXPORT ctkThumbnailLabel : public QWidget void setPixmap(const QPixmap& pixmap); const QPixmap* pixmap()const; + void setOperationProgress(const int& progress); + int operationProgress()const; + Qt::TransformationMode transformationMode()const; void setTransformationMode(Qt::TransformationMode mode); From 4dddac24c81ab7bd40c4d4b4d5a097741ccdd35b Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Mon, 8 Jan 2024 12:58:54 +0100 Subject: [PATCH 02/73] BUG: Fix query bugs and warning background of query filters widget --- Libs/DICOM/Core/ctkDICOMDatabase.cpp | 2 +- .../Widgets/ctkDICOMPatientItemWidget.cpp | 8 +- .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 4 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 222 +++++++++--------- 4 files changed, 125 insertions(+), 111 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index 04eb9cdd2f..d7d39d90a5 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -2767,7 +2767,7 @@ void ctkDICOMDatabase::insert(QList> jobR insertImageStatement.prepare("INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'URL', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ?, ? )"); insertImageStatement.addBindValue(sopInstanceUID); insertImageStatement.addBindValue(storedFilePathInDatabase); - insertImageStatement.addBindValue("url"); + insertImageStatement.addBindValue(url); insertImageStatement.addBindValue(seriesInstanceUID); insertImageStatement.addBindValue(QDateTime::currentDateTime()); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index b8b0137e74..05bc6473c1 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -251,6 +251,11 @@ void ctkDICOMPatientItemWidgetPrivate::createStudies() QStringList studiesList = this->DicomDatabase->studiesForPatient(this->PatientItem); + if (studiesList.count() == 0) + { + return; + } + // Filter with studyDescription and studyDate and sort by Date QMap studiesMap; foreach (QString studyItem, studiesList) @@ -692,8 +697,7 @@ void ctkDICOMPatientItemWidget::generateStudies() Q_D(ctkDICOMPatientItemWidget); d->createStudies(); - QStringList studiesList = d->DicomDatabase->studiesForPatient(d->PatientItem); - if (studiesList.count() && d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + if (d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) { d->Scheduler->queryStudies(d->PatientID, QThread::NormalPriority); } diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 4bbd2afa92..69f650b49a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -649,8 +649,8 @@ void ctkDICOMStudyItemWidget::generateSeries(bool toggled) } d->createSeries(); - QStringList seriesList = d->DicomDatabase->seriesForStudy(d->StudyInstanceUID); - if (seriesList.count() == 0 && d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) + + if (d->Scheduler && d->Scheduler->getNumberOfQueryRetrieveServers() > 0) { d->Scheduler->querySeries(d->PatientID, d->StudyInstanceUID, diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 56abbd9085..85e2a539d3 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -140,6 +140,7 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget void updateModalityCheckableComboBox(); void createPatients(); void updateFiltersWarnings(); + void setBackgroundColorToWidget(Qt::GlobalColor color, QWidget* widget); void retrieveSeries(); bool updateServer(ctkDICOMServer* server); void removeAllPatientItemWidgets(); @@ -152,17 +153,11 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget QStringList getSeriesUIDsFromWidgets(ctkDICOMModel::IndexType level, QList selectedWidgets); QStringList filterPatientList(const QStringList& patientList, - const QString& filterName, - const QString& filterValue); - QStringList filterStudyList(const QStringList& studyList, - const QString& filterName, - const QString& filterValue); - QStringList filterSeriesList(const QStringList& seriesList, - const QString& filterName, - const QString& filterValue); - QStringList filterSeriesList(const QStringList& seriesList, - const QString& filterName, - const QStringList& filterValues); + const QMap& filters); + QStringList filterStudyList(const QStringList& patientList, + const QMap& filters); + QStringList filterSeriesList(const QStringList& patientList, + const QMap& filters); // Return a sanitized version of the string that is safe to be used // as a filename component. @@ -642,41 +637,31 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() // Loop over all the data in the dicom database and apply the filters. // If there are no series, highlight which are the filters that produce no results - QString background = "QWidget { background-color: white }"; - this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); - this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); - this->FilteringDateComboBox->setStyleSheet(this->FilteringDateComboBox->styleSheet() + background); - this->FilteringStudyDescriptionSearchBox->setStyleSheet(this->FilteringStudyDescriptionSearchBox->styleSheet() + background); - this->FilteringSeriesDescriptionSearchBox->setStyleSheet(this->FilteringSeriesDescriptionSearchBox->styleSheet() + background); - this->FilteringModalityCheckableComboBox->setStyleSheet(this->FilteringModalityCheckableComboBox->styleSheet() + background); - - background = "QWidget { background-color: yellow }"; + Qt::GlobalColor color = Qt::white; + this->setBackgroundColorToWidget(color, this->FilteringPatientIDSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringPatientNameSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringDateComboBox); + this->setBackgroundColorToWidget(color, this->FilteringStudyDescriptionSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringSeriesDescriptionSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringModalityCheckableComboBox); + + color = Qt::yellow; QStringList patientList = this->DicomDatabase->patients(); if (patientList.count() == 0) { - this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); - this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); + this->setBackgroundColorToWidget(color, this->FilteringPatientIDSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringPatientNameSearchBox); return; } - QStringList filteredPatientListByName = - this->filterPatientList(patientList, "PatientsName", this->FilteringPatientName); - QStringList filteredPatientListByID = - this->filterPatientList(patientList, "PatientID", this->FilteringPatientID); - - if (filteredPatientListByName.count() == 0) - { - this->FilteringPatientNameSearchBox->setStyleSheet(this->FilteringPatientNameSearchBox->styleSheet() + background); - } - - if (filteredPatientListByID.count() == 0) - { - this->FilteringPatientIDSearchBox->setStyleSheet(this->FilteringPatientIDSearchBox->styleSheet() + background); - } - - QStringList filteredPatientList = filteredPatientListByName + filteredPatientListByID; + QMap filters; + filters.insert("PatientsName", this->FilteringPatientName); + filters.insert("PatientID", this->FilteringPatientID); + QStringList filteredPatientList = this->filterPatientList(patientList, filters); if (filteredPatientList.count() == 0) { + this->setBackgroundColorToWidget(color, this->FilteringPatientIDSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringPatientNameSearchBox); return; } @@ -686,25 +671,15 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() studiesList.append(this->DicomDatabase->studiesForPatient(patientItem)); } - QStringList filteredStudyListByDate = - this->filterStudyList(studiesList, "StudyDate", - QString::number(ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(this->FilteringDate))); - QStringList filteredStudyListByDescription = - this->filterStudyList(studiesList, "StudyDescription", this->FilteringStudyDescription); - - if (filteredStudyListByDate.count() == 0) - { - this->FilteringDateComboBox->setStyleSheet(this->FilteringDateComboBox->styleSheet() + background); - } - - if (filteredStudyListByDescription.count() == 0) - { - this->FilteringStudyDescriptionSearchBox->setStyleSheet(this->FilteringStudyDescriptionSearchBox->styleSheet() + background); - } + filters.clear(); + filters.insert("StudyDate", QString::number(ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(this->FilteringDate))); + filters.insert("StudyDescription", this->FilteringStudyDescription); - QStringList filteredStudyList = filteredStudyListByDate + filteredStudyListByDescription; + QStringList filteredStudyList = this->filterStudyList(studiesList, filters); if (filteredStudyList.count() == 0) { + this->setBackgroundColorToWidget(color, this->FilteringDateComboBox); + this->setBackgroundColorToWidget(color, this->FilteringStudyDescriptionSearchBox); return; } @@ -715,20 +690,38 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() seriesList.append(this->DicomDatabase->seriesForStudy(studyInstanceUID)); } - QStringList filteredSeriesListByModality = - this->filterSeriesList(seriesList, "Modality", this->FilteringModalities); - QStringList filteredSeriesListByDescription = - this->filterSeriesList(seriesList, "SeriesDescription", this->FilteringSeriesDescription); + filters.clear(); + filters.insert("Modality", this->FilteringModalities); + filters.insert("SeriesDescription", this->FilteringSeriesDescription); + + QStringList filteredSeriesList = this->filterSeriesList(seriesList, filters); + if (filteredSeriesList.count() == 0) + { + this->setBackgroundColorToWidget(color, this->FilteringSeriesDescriptionSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringModalityCheckableComboBox); + } +} - if (filteredSeriesListByModality.count() == 0) +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToWidget(Qt::GlobalColor color, + QWidget *widget) +{ + if (!widget) { - this->FilteringModalityCheckableComboBox->setStyleSheet(this->FilteringModalityCheckableComboBox->styleSheet() + background); + return; } - if (filteredSeriesListByDescription.count() == 0) + QPalette pal = widget->palette(); + QComboBox* comboBox = qobject_cast(widget); + if (comboBox) { - this->FilteringSeriesDescriptionSearchBox->setStyleSheet(this->FilteringSeriesDescriptionSearchBox->styleSheet() + background); + pal.setColor(QPalette::Button, color); } + else + { + pal.setColor(widget->backgroundRole(), color); + } + widget->setPalette(pal); } //---------------------------------------------------------------------------- @@ -1114,8 +1107,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getSeriesUIDsFromWidgets(ctkDICO //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::filterPatientList(const QStringList& patientList, - const QString& filterName, - const QString& filterValue) + const QMap& filters) { QStringList filteredPatientList; if (!this->DicomDatabase) @@ -1126,8 +1118,19 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterPatientList(const QStringL foreach (QString patientItem, patientList) { - QString filter = this->DicomDatabase->fieldForPatient(filterName, patientItem); - if (!filter.contains(filterValue, Qt::CaseInsensitive)) + bool filtered = false; + for(QString key : filters.keys()) + { + QString filter = this->DicomDatabase->fieldForPatient(key, patientItem); + QString filterValue = filters.value(key).toString(); + if (!filter.contains(filterValue, Qt::CaseInsensitive)) + { + filtered = true; + break; + } + } + + if (filtered) { continue; } @@ -1140,8 +1143,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterPatientList(const QStringL //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::filterStudyList(const QStringList& studyList, - const QString &filterName, - const QString &filterValue) + const QMap& filters) { QStringList filteredStudyList; if (!this->DicomDatabase) @@ -1152,23 +1154,35 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterStudyList(const QStringLis foreach (QString studyItem, studyList) { - QString filter = this->DicomDatabase->fieldForStudy(filterName, studyItem); - if (filterName == "StudyDate") + bool filtered = false; + for(QString key : filters.keys()) { - int nDays = filterValue.toInt(); - if (nDays != -1) + QString filter = this->DicomDatabase->fieldForStudy(key, studyItem); + QString filterValue = filters.value(key).toString(); + if (key == "StudyDate") { - QDate endDate = QDate::currentDate(); - QDate startDate = endDate.addDays(-nDays); - filter.replace(QString("-"), QString("")); - QDate studyDate = QDate::fromString(filter, "yyyyMMdd"); - if (studyDate < startDate || studyDate > endDate) + int nDays = filterValue.toInt(); + if (nDays != -1) { - continue; + QDate endDate = QDate::currentDate(); + QDate startDate = endDate.addDays(-nDays); + filter.replace(QString("-"), QString("")); + QDate studyDate = QDate::fromString(filter, "yyyyMMdd"); + if (studyDate < startDate || studyDate > endDate) + { + filtered = true; + break; + } } } + else if (!filter.contains(filterValue, Qt::CaseInsensitive)) + { + filtered = true; + break; + } } - else if (!filter.contains(filterValue)) + + if (filtered) { continue; } @@ -1181,8 +1195,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterStudyList(const QStringLis //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringList& seriesList, - const QString &filterName, - const QString &filterValue) + const QMap& filters) { QStringList filteredSeriesList; if (!this->DicomDatabase) @@ -1193,34 +1206,31 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringLi foreach (QString seriesItem, seriesList) { - QString filter = this->DicomDatabase->fieldForSeries(filterName, seriesItem); - if (!filter.contains(filterValue)) + bool filtered = false; + for(QString key : filters.keys()) { - continue; + QString filter = this->DicomDatabase->fieldForSeries(key, seriesItem); + if (key == "Modality") + { + QStringList filterValues = filters.value(key).toStringList(); + if (!filterValues.contains("Any") && !filterValues.contains(filter)) + { + filtered = true; + break; + } + } + else + { + QString filterValue = filters.value(key).toString(); + if (!filter.contains(filterValue, Qt::CaseInsensitive)) + { + filtered = true; + break; + } + } } - filteredSeriesList.append(seriesItem); - } - - return filteredSeriesList; -} - -//---------------------------------------------------------------------------- -QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringList &seriesList, - const QString &filterName, - const QStringList &filterValues) -{ - QStringList filteredSeriesList; - if (!this->DicomDatabase) - { - logger.error("filterSeriesList failed, no DICOM Database has been set. \n"); - return filteredSeriesList; - } - - foreach (QString seriesItem, seriesList) - { - QString filter = this->DicomDatabase->fieldForSeries(filterName, seriesItem); - if (!filterValues.contains("Any") && !filterValues.contains(filter)) + if (filtered) { continue; } From eb372e4c2e9d38dc5a2be5dc43e481bbed0abd62 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Wed, 10 Jan 2024 15:41:03 +0100 Subject: [PATCH 03/73] ENH: Minor improvements for the visual DICOM browser --- Libs/Core/ctkAbstractScheduler.h | 11 +- Libs/DICOM/Core/ctkDICOMEcho.cpp | 42 -- Libs/DICOM/Core/ctkDICOMEcho.h | 4 - Libs/DICOM/Core/ctkDICOMJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJob.h | 4 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 5 + Libs/DICOM/Core/ctkDICOMQuery.cpp | 42 -- Libs/DICOM/Core/ctkDICOMQuery.h | 4 - Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 42 -- Libs/DICOM/Core/ctkDICOMRetrieve.h | 4 - Libs/DICOM/Core/ctkDICOMScheduler.cpp | 43 +- Libs/DICOM/Core/ctkDICOMScheduler.h | 4 +- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 42 -- Libs/DICOM/Core/ctkDICOMStorageListener.h | 4 - .../Widgets/Resources/UI/Icons/more_vert.svg | 39 +- .../Resources/UI/ctkDICOMPatientItemWidget.ui | 125 ++-- .../Resources/UI/ctkDICOMSeriesItemWidget.ui | 6 - .../Resources/UI/ctkDICOMServerNodeWidget2.ui | 233 ++++---- .../Resources/UI/ctkDICOMStudyItemWidget.ui | 21 +- .../UI/ctkDICOMVisualBrowserWidget.ui | 123 ++-- .../Widgets/ctkDICOMPatientItemWidget.cpp | 40 +- .../DICOM/Widgets/ctkDICOMPatientItemWidget.h | 19 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 175 ++++-- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 17 +- .../Widgets/ctkDICOMServerNodeWidget2.cpp | 20 +- .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 150 ++++- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 35 +- .../Widgets/ctkDICOMThumbnailGenerator.cpp | 2 +- .../Widgets/ctkDICOMThumbnailGenerator.h | 3 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 541 +++++++++++++++--- .../Widgets/ctkDICOMVisualBrowserWidget.h | 47 +- .../Widgets/Resources/UI/ctkThumbnailLabel.ui | 22 +- Libs/Widgets/ctkDirectoryButton.cpp | 5 +- Libs/Widgets/ctkDirectoryButton.h | 2 +- Libs/Widgets/ctkThumbnailLabel.cpp | 32 +- Libs/Widgets/ctkThumbnailLabel.h | 3 +- 36 files changed, 1151 insertions(+), 762 deletions(-) diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index ef2a264d5c..fbc6de7301 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -26,6 +26,7 @@ // Qt includes #include +#include // CTK includes #include "ctkCoreExport.h" @@ -40,15 +41,15 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject virtual ~ctkAbstractScheduler(); Q_SIGNALS: - void jobStarted(QString jobUID, QString jobType); - void jobFinished(QString jobUID, QString jobType); - void jobCanceled(QString jobUID, QString jobType); - void jobFailed(QString jobUID, QString jobType); + void jobStarted(QVariant data); + void jobFinished(QVariant data); + void jobCanceled(QVariant data); + void jobFailed(QVariant data); public Q_SLOTS: virtual void onJobStarted() = 0; virtual void onJobFinished() = 0; - virtual void onJobCanceled() = 0; + virtual void onJobCanceled() = 0; virtual void onJobFailed() = 0; private: diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp index 96c508b6e7..544289aa71 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.cpp +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -89,8 +89,6 @@ ctkDICOMEcho::ctkDICOMEcho(QObject* parentObject) Q_D(ctkDICOMEcho); d->SCU.setVerbosePCMode(false); - - this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -98,46 +96,6 @@ ctkDICOMEcho::~ctkDICOMEcho() { } -//----------------------------------------------------------------------------- -void ctkDICOMEcho::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) -{ - OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; - if (level == ctkErrorLogLevel::LogLevel::Fatal) - { - dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Critical || - level == ctkErrorLogLevel::LogLevel::Error) - { - dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Warning) - { - dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Info) - { - dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Debug) - { - dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Trace || - level == ctkErrorLogLevel::LogLevel::Status) - { - dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; - } - - OFLog::configure(dcmtkLogLevel); -} - -//----------------------------------------------------------------------------- -ctkErrorLogLevel::LogLevel ctkDICOMEcho::DCMTKLogLevel() const -{ - return logger.logLevel(); -} - /// Set methods for connectivity //------------------------------------------------------------------------------ void ctkDICOMEcho::setConnectionName(const QString& connectionName) diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h index 2e8d034cb4..3c5e9bbd8d 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.h +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -72,10 +72,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMEcho : public QObject void setConnectionTimeout(const int timeout); int connectionTimeout() const; - /// Log level for dcmtk. Default: Error. - Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); - Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; - /// Echo connection. bool echo(); diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 6527406d85..6ca0adbdc2 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -158,5 +158,5 @@ void ctkDICOMJob::copyJobResponseSets(QList(new ctkDICOMJobResponseSet); jobResponseSetCopy->deepCopy(jobResponseSet.data()); this->JobResponseSets.append(jobResponseSetCopy); - } + } } diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index f51504adda..bb01357639 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -24,7 +24,7 @@ #ifndef __ctkDICOMJob_h #define __ctkDICOMJob_h -// Qt includes +// Qt includes #include #include #include @@ -52,7 +52,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob typedef ctkAbstractJob Superclass; explicit ctkDICOMJob(); virtual ~ctkDICOMJob(); - + enum DICOMLevels{ Patients, Studies, diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 99c32ebfcc..d35c46221b 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -30,6 +30,7 @@ #include #include +#include "ctkDICOMJob.h" #include "ctkDICOMCoreExport.h" #include "ctkDICOMItem.h" @@ -133,6 +134,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject struct CTK_DICOM_CORE_EXPORT ctkJobDetail { explicit ctkJobDetail(): TypeOfJob(ctkDICOMJobResponseSet::JobType::None), + JobClass(""), + DICOMLevel(ctkDICOMJob::DICOMLevels::Patients), JobUID(""), PatientID(""), StudyInstanceUID(""), @@ -143,6 +146,8 @@ struct CTK_DICOM_CORE_EXPORT ctkJobDetail { virtual ~ctkJobDetail(){}; ctkDICOMJobResponseSet::JobType TypeOfJob; + QString JobClass; + ctkDICOMJob::DICOMLevels DICOMLevel; QString JobUID; QString PatientID; QString StudyInstanceUID; diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index 64c1c8eb48..ad53f2de5d 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -152,8 +152,6 @@ ctkDICOMQuery::ctkDICOMQuery(QObject* parentObject) d->SCU.setVerbosePCMode(false); d->SCU.query = this; // give the dcmtk level access to this for emitting signals - - this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -161,46 +159,6 @@ ctkDICOMQuery::~ctkDICOMQuery() { } -//----------------------------------------------------------------------------- -void ctkDICOMQuery::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) -{ - OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; - if (level == ctkErrorLogLevel::LogLevel::Fatal) - { - dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Critical || - level == ctkErrorLogLevel::LogLevel::Error) - { - dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Warning) - { - dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Info) - { - dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Debug) - { - dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Trace || - level == ctkErrorLogLevel::LogLevel::Status) - { - dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; - } - - OFLog::configure(dcmtkLogLevel); -} - -//----------------------------------------------------------------------------- -ctkErrorLogLevel::LogLevel ctkDICOMQuery::DCMTKLogLevel() const -{ - return logger.logLevel(); -} - /// Set methods for connectivity //------------------------------------------------------------------------------ void ctkDICOMQuery::setConnectionName(const QString& connectionName) diff --git a/Libs/DICOM/Core/ctkDICOMQuery.h b/Libs/DICOM/Core/ctkDICOMQuery.h index 559cbe0f98..041c2a82ca 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.h +++ b/Libs/DICOM/Core/ctkDICOMQuery.h @@ -104,10 +104,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject /// operation is canceled? Q_INVOKABLE bool wasCanceled(); - /// Log level for dcmtk. Default: Error. - Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); - Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; - /// Query a remote DICOM Image Store SCP. /// You must at least set the host and port before calling query() Q_INVOKABLE bool query(ctkDICOMDatabase& database); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index 137a5adb56..303996167c 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -692,8 +692,6 @@ ctkDICOMRetrieve::ctkDICOMRetrieve(QObject* parent) d->SCU.setVerbosePCMode(false); d->SCU.retrieve = this; // give the dcmtk level access to this for emitting signals - - this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -966,46 +964,6 @@ bool ctkDICOMRetrieve::wasCanceled() return d->Canceled; } -//----------------------------------------------------------------------------- -void ctkDICOMRetrieve::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) -{ - OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; - if (level == ctkErrorLogLevel::LogLevel::Fatal) - { - dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Critical || - level == ctkErrorLogLevel::LogLevel::Error) - { - dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Warning) - { - dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Info) - { - dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Debug) - { - dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Trace || - level == ctkErrorLogLevel::LogLevel::Status) - { - dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; - } - - OFLog::configure(dcmtkLogLevel); -} - -//----------------------------------------------------------------------------- -ctkErrorLogLevel::LogLevel ctkDICOMRetrieve::DCMTKLogLevel() const -{ - return logger.logLevel(); -} - //------------------------------------------------------------------------------ bool ctkDICOMRetrieve::moveStudy(const QString& studyInstanceUID, const QString& patientID) diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.h b/Libs/DICOM/Core/ctkDICOMRetrieve.h index efadabaaa0..a82545bb0e 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.h @@ -90,10 +90,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieve : public QObject /// operation is canceled? Q_INVOKABLE bool wasCanceled(); - /// Log level for dcmtk. Default: Error. - Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); - Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; - /// where to insert new data sets obtained via get (must be set for /// get to succeed) Q_INVOKABLE void setDatabase(ctkDICOMDatabase& dicomDatabase); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 52143e061e..0cdc802a0e 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -23,6 +23,7 @@ // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" +#include "ctkDICOMJobResponseSet.h" #include "ctkDICOMQueryJob.h" #include "ctkDICOMRetrieveJob.h" #include "ctkDICOMScheduler.h" @@ -1013,6 +1014,34 @@ QSharedPointer ctkDICOMScheduler::threadPoolShared() const return d->ThreadPool; } +//---------------------------------------------------------------------------- +QVariant ctkDICOMScheduler::jobToDetail(ctkDICOMJob* job) +{ + ctkJobDetail td; + td.JobClass = job->className(); + td.DICOMLevel = job->dicomLevel(); + td.JobUID = job->jobUID(); + td.PatientID = job->patientID(); + td.StudyInstanceUID = job->studyInstanceUID(); + td.SeriesInstanceUID = job->seriesInstanceUID(); + td.SOPInstanceUID = job->sopInstanceUID(); + ctkDICOMQueryJob* queryJob = qobject_cast(job); + if (queryJob && queryJob->server()) + { + td.ConnectionName = queryJob->server()->connectionName(); + } + ctkDICOMRetrieveJob* retrieveJob = qobject_cast(job); + if (retrieveJob && retrieveJob->server()) + { + td.ConnectionName = retrieveJob->server()->connectionName(); + } + + QVariant data; + data.setValue(td); + + return data; +} + //---------------------------------------------------------------------------- void ctkDICOMScheduler::onJobStarted() { @@ -1023,8 +1052,7 @@ void ctkDICOMScheduler::onJobStarted() } logger.debug(job->loggerReport("started")); - QString jobType = job->className(); - emit this->jobStarted(job->jobUID(), jobType); + emit this->jobStarted(this->jobToDetail(job)); } //---------------------------------------------------------------------------- @@ -1037,8 +1065,7 @@ void ctkDICOMScheduler::onJobCanceled() } logger.debug(job->loggerReport("canceled")); - QString jobType = job->className(); - emit this->jobCanceled(job->jobUID(), jobType); + emit this->jobCanceled(this->jobToDetail(job)); } //---------------------------------------------------------------------------- @@ -1052,12 +1079,12 @@ void ctkDICOMScheduler::onJobFailed() logger.debug(job->loggerReport("failed")); + QVariant data = this->jobToDetail(job); QString jobUID = job->jobUID(); - QString jobType = job->className(); this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobFailed(jobUID, jobType); + emit this->jobFailed(data); } //---------------------------------------------------------------------------- @@ -1071,12 +1098,12 @@ void ctkDICOMScheduler::onJobFinished() logger.debug(job->loggerReport("finished")); + QVariant data = this->jobToDetail(job); QString jobUID = job->jobUID(); - QString jobType = job->className(); this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobFinished(jobUID, jobType); + emit this->jobFinished(data); } //---------------------------------------------------------------------------- diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 8235730732..135f0d52bb 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -27,7 +27,6 @@ // Qt includes #include #include -#include // ctkCore includes #include "ctkAbstractScheduler.h" @@ -205,6 +204,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler /// (not Python-wrappable). QSharedPointer threadPoolShared() const; + /// Utility method to transform/pass informations between threads by qt signals + Q_INVOKABLE QVariant jobToDetail(ctkDICOMJob* job); + Q_SIGNALS: void queueJobs(); void progressJobDetail(QVariant data); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index e5c8eedb13..1e272a6c5d 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -242,8 +242,6 @@ ctkDICOMStorageListener::ctkDICOMStorageListener(QObject* parentObject) { Q_D(ctkDICOMStorageListener); d->SCU.listener = this; // give the dcmtk level access to this for emitting signals - - this->setDCMTKLogLevel(logger.logLevel()); } //------------------------------------------------------------------------------ @@ -253,46 +251,6 @@ ctkDICOMStorageListener::~ctkDICOMStorageListener() d->JobResponseSets.clear(); } -//----------------------------------------------------------------------------- -void ctkDICOMStorageListener::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level) -{ - OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; - if (level == ctkErrorLogLevel::LogLevel::Fatal) - { - dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Critical || - level == ctkErrorLogLevel::LogLevel::Error) - { - dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Warning) - { - dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Info) - { - dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Debug) - { - dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Trace || - level == ctkErrorLogLevel::LogLevel::Status) - { - dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; - } - - OFLog::configure(dcmtkLogLevel); -} - -//----------------------------------------------------------------------------- -ctkErrorLogLevel::LogLevel ctkDICOMStorageListener::DCMTKLogLevel() const -{ - return logger.logLevel(); -} - //------------------------------------------------------------------------------ bool ctkDICOMStorageListener::listen() { diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index e941fc8d15..61a4fa83d0 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -71,10 +71,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject Q_INVOKABLE void setJobUID(const QString& jobUID); Q_INVOKABLE QString jobUID() const; - /// Log level for dcmtk. Default: Error. - Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); - Q_INVOKABLE ctkErrorLogLevel::LogLevel DCMTKLogLevel() const; - /// Start listen connection. bool listen(); diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg index e172f878a6..6d94a46582 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/more_vert.svg @@ -1 +1,38 @@ - \ No newline at end of file + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui index 1dd5a16998..d9d087cf4e 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMPatientItemWidget.ui @@ -22,9 +22,6 @@ 0 - - - QFrame::StyledPanel @@ -49,16 +46,8 @@ - - - 12 - - - - - - Patient Information + Patient Information false @@ -103,7 +92,7 @@ - + @@ -111,38 +100,18 @@ 0 - - - 11 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - 0 - ID - - false - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - 10 + 3 - + @@ -150,11 +119,6 @@ 0 - - - 11 - - Sex @@ -162,32 +126,27 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - 10 + 3 - + - - - 11 - - - Qt::AlignCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - 10 + 3 Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - + @@ -195,11 +154,6 @@ 0 - - - 11 - - Birth Date @@ -207,38 +161,28 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - 10 + 3 - + - - - 11 - - - Qt::AlignCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - 10 + 3 Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - + - - - 11 - - QFrame::NoFrame @@ -255,10 +199,39 @@ - Qt::AlignCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 3 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 100 + 0 + + + + Name - 10 + 3 + + + + + + + + + + 3 Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -294,9 +267,6 @@ 2 - - - QFrame::NoFrame @@ -318,12 +288,9 @@ 0 0 996 - 181 + 191 - - background-color: rgb(255, 255, 255); - diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui index b0c5fd2f72..e5ccee5702 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMSeriesItemWidget.ui @@ -37,9 +37,6 @@ 0 - - - QFrame::Raised @@ -64,9 +61,6 @@ - - - Qt::AlignBottom|Qt::AlignHCenter diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui index 82d75eaa8f..2f617941c8 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget2.ui @@ -29,119 +29,7 @@ 5 - - - - Servers - - - Qt::AlignCenter - - - - - - - - 1 - 0 - - - - false - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideRight - - - 10 - - - true - - - false - - - 165 - - - 165 - - - true - - - false - - - true - - - false - - - false - - - - Name - - - - - Query/Retrieve - - - - - Storage - - - - - Calling AETitle - - - - - Called AETitle - - - - - Address - - - - - Port - - - - - Timeout - - - - - Protocol - - - - - Proxy - - - - - + @@ -153,9 +41,9 @@ Storage - false + true - + false @@ -281,7 +169,7 @@ - + 3 @@ -354,7 +242,7 @@ - + 0 @@ -380,6 +268,117 @@ + + + + Servers + + + + + + + 1 + 0 + + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideRight + + + 10 + + + true + + + false + + + 165 + + + 165 + + + true + + + false + + + true + + + false + + + false + + + + Name + + + + + Query/Retrieve + + + + + Storage + + + + + Calling AETitle + + + + + Called AETitle + + + + + Address + + + + + Port + + + + + Timeout + + + + + Protocol + + + + + Proxy + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui index e48a467849..20803e1cb7 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMStudyItemWidget.ui @@ -13,9 +13,6 @@ Study Item - - - 3 @@ -76,14 +73,6 @@ 0 - - - 12 - - - - - Study ID 1234 --- Date @@ -130,15 +119,12 @@ - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:12pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p></body></html> @@ -150,9 +136,6 @@ p, li { white-space: pre-wrap; } 1 - - - QFrame::NoFrame diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui index f405420237..07820eb749 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMVisualBrowserWidget.ui @@ -20,9 +20,6 @@ :/Icons/patient.svg:/Icons/patient.svg - - - 3 @@ -39,20 +36,58 @@ 2 + + + + color: rgb(0, 0, 0);background-color: rgb(245, 245, 170); + + + QFrame::Box + + + + + + + 0 + 0 + + + + Warning + + + + + + + Update database + + + + + + + Create new database + + + + + + + Select database folder + + + + + + - - - 11 - - - - - - Patients Search + Patients Search false @@ -96,11 +131,6 @@ 0 - - - 11 - - ID @@ -156,11 +186,6 @@ 0 - - - 11 - - Name @@ -216,11 +241,6 @@ 0 - - - 11 - - Study @@ -276,11 +296,6 @@ 0 - - - 11 - - Series @@ -348,11 +363,6 @@ 16777215 - - - 11 - - Modality @@ -384,12 +394,6 @@ Filter by modality - - false - - - - Any @@ -493,11 +497,6 @@ 16777215 - - - 11 - - Date @@ -642,14 +641,6 @@ - - - 11 - - - - - Actions @@ -728,9 +719,6 @@ Qt::StrongFocus - - - No filters have been set and no patients have been found in the local database. Please set at least one filter to query the servers @@ -780,9 +768,6 @@ Please set at least one filter to query the servers 1 - - - QTabWidget::North @@ -790,12 +775,12 @@ Please set at least one filter to query the servers QTabWidget::Rounded - 1 + 0 - 32 - 32 + 24 + 24 @@ -892,14 +877,6 @@ Please set at least one filter to query the servers - - - 11 - - - - - Settings diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index 05bc6473c1..91e08c2952 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -35,7 +35,6 @@ // ctkDICOMWidgets includes #include "ctkDICOMSeriesItemWidget.h" -#include "ctkDICOMStudyItemWidget.h" #include "ctkDICOMPatientItemWidget.h" #include "ui_ctkDICOMPatientItemWidget.h" @@ -74,8 +73,7 @@ class ctkDICOMPatientItemWidgetPrivate: public Ui_ctkDICOMPatientItemWidget QSharedPointer VisualDICOMBrowser; int NumberOfStudiesPerPatient; - int NumberOfSeriesPerRow; - int MinimumThumbnailSize; + ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; QString PatientItem; QString PatientID; @@ -98,8 +96,7 @@ ctkDICOMPatientItemWidgetPrivate::ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatie { this->FilteringDate = ctkDICOMPatientItemWidget::DateType::Any; this->NumberOfStudiesPerPatient = 2; - this->NumberOfSeriesPerRow = 6; - this->MinimumThumbnailSize = 300; + this->ThumbnailSize = ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium; this->PatientItem = ""; this->PatientID = ""; this->FilteringStudyDescription = ""; @@ -125,6 +122,7 @@ void ctkDICOMPatientItemWidgetPrivate::init(QWidget* parentWidget) this->VisualDICOMBrowser = QSharedPointer(parentWidget, skipDelete); + this->PatientNameValueLabel->setWordWrap(true); this->PatientIDValueLabel->setWordWrap(true); this->PatientBirthDateValueLabel->setWordWrap(true); this->PatientSexValueLabel->setWordWrap(true); @@ -237,6 +235,7 @@ void ctkDICOMPatientItemWidgetPrivate::createStudies() QLayout *studiesListWidgetLayout = this->StudiesListWidget->layout(); if (this->PatientItem.isEmpty()) { + this->PatientNameValueLabel->setText(""); this->PatientIDValueLabel->setText(""); this->PatientSexValueLabel->setText(""); this->PatientBirthDateValueLabel->setText(""); @@ -244,6 +243,9 @@ void ctkDICOMPatientItemWidgetPrivate::createStudies() } else { + QString patientName = this->DicomDatabase->fieldForPatient("PatientsName", this->PatientItem); + patientName.replace(R"(^)", R"( )"); + this->PatientNameValueLabel->setText(patientName); this->PatientIDValueLabel->setText(this->DicomDatabase->fieldForPatient("PatientID", this->PatientItem)); this->PatientSexValueLabel->setText(this->DicomDatabase->fieldForPatient("PatientsSex", this->PatientItem)); this->PatientBirthDateValueLabel->setText(this->formatDate(this->DicomDatabase->fieldForPatient("PatientsBirthDate", this->PatientItem))); @@ -437,31 +439,17 @@ int ctkDICOMPatientItemWidget::numberOfStudiesPerPatient() const } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) +void ctkDICOMPatientItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) { Q_D(ctkDICOMPatientItemWidget); - d->NumberOfSeriesPerRow = numberOfSeriesPerRow; + d->ThumbnailSize = thumbnailSize; } //------------------------------------------------------------------------------ -int ctkDICOMPatientItemWidget::numberOfSeriesPerRow() const +ctkDICOMStudyItemWidget::ThumbnailSizeOption ctkDICOMPatientItemWidget::thumbnailSize() const { Q_D(const ctkDICOMPatientItemWidget); - return d->NumberOfSeriesPerRow; -} - -//---------------------------------------------------------------------------- -void ctkDICOMPatientItemWidget::setMinimumThumbnailSize(int minimumThumbnailSize) -{ - Q_D(ctkDICOMPatientItemWidget); - d->MinimumThumbnailSize = minimumThumbnailSize; -} - -//---------------------------------------------------------------------------- -int ctkDICOMPatientItemWidget::minimumThumbnailSize() const -{ - Q_D(const ctkDICOMPatientItemWidget); - return d->MinimumThumbnailSize; + return d->ThumbnailSize; } //---------------------------------------------------------------------------- @@ -619,11 +607,7 @@ void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString &studyItem) } studyItemWidget->setDescription(studyDescription); - studyItemWidget->setNumberOfSeriesPerRow(d->NumberOfSeriesPerRow); - if (this->parentWidget()) - { - studyItemWidget->setThumbnailSize(std::max(int(this->parentWidget()->width() / d->NumberOfSeriesPerRow), d->MinimumThumbnailSize) * 0.94); - } + studyItemWidget->setThumbnailSize(d->ThumbnailSize); studyItemWidget->setFilteringSeriesDescription(d->FilteringSeriesDescription); studyItemWidget->setFilteringModalities(d->FilteringModalities); studyItemWidget->setDicomDatabase(d->DicomDatabase); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index 4083b8ec99..c26f77d1e1 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -30,6 +30,9 @@ #include #include +// CTK includes +#include "ctkDICOMStudyItemWidget.h" + class ctkDICOMPatientItemWidgetPrivate; class ctkDICOMDatabase; @@ -44,8 +47,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget Q_PROPERTY(QString patientItem READ patientItem WRITE setPatientItem); Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); - Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); - Q_PROPERTY(int minimumThumbnailSize READ minimumThumbnailSize WRITE setMinimumThumbnailSize); + Q_PROPERTY(ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize READ thumbnailSize WRITE setThumbnailSize); public: typedef QWidget Superclass; @@ -97,15 +99,10 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); int numberOfStudiesPerPatient() const; - /// Number of series displayed per row - /// 6 by default - void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); - int numberOfSeriesPerRow() const; - - /// Minimum thumbnail size in pixel - /// 300 by default - void setMinimumThumbnailSize(int minimumThumbnailSize); - int minimumThumbnailSize() const; + /// Set the thumbnail size: small, medium, large + /// medium by default + void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); + ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 0ebef04176..acdc27bc48 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -24,6 +24,7 @@ //Qt includes #include #include +#include #include #include #include @@ -32,6 +33,7 @@ // CTK includes #include +#include // ctkDICOMCore includes #include "ctkDICOMDatabase.h" @@ -63,8 +65,10 @@ class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget void drawModalityThumbnail(); void drawThumbnail(const QString& file, int numberOfFrames); void drawTextWithShadow(QPainter *painter, - const QRect &r, - int flags, + const QFont &font, + const int &x, + const int &y, + const Qt::Alignment &alignment, const QString &text); void updateThumbnailProgressBar(); @@ -81,9 +85,10 @@ class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget bool StopJobs; bool RaiseJobsPriority; bool IsCloud; + bool RetrieveFailed; bool IsLoaded; bool IsVisible; - int ThumbnailSize; + int ThumbnailSizePixel; int NumberOfDownloads; QImage ThumbnailImage; bool isThumbnailDocument; @@ -105,12 +110,13 @@ ctkDICOMSeriesItemWidgetPrivate::ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesI this->Modality = ""; this->IsCloud = false; + this->RetrieveFailed = false; this->IsLoaded = false; this->IsVisible = false; this->StopJobs = false; this->RaiseJobsPriority = false; this->isThumbnailDocument = false; - this->ThumbnailSize = 300; + this->ThumbnailSizePixel = 200; this->NumberOfDownloads = 0; this->DicomDatabase = nullptr; @@ -129,6 +135,7 @@ void ctkDICOMSeriesItemWidgetPrivate::init() this->setupUi(q); this->SeriesThumbnail->setTransformationMode(Qt::TransformationMode::SmoothTransformation); + this->SeriesThumbnail->textPushButton()->setElideMode(Qt::ElideRight); } //---------------------------------------------------------------------------- @@ -324,26 +331,30 @@ void ctkDICOMSeriesItemWidgetPrivate::drawModalityThumbnail() return; } - int margin = 10; - int fontSize = 40; + int textSize = floor(this->ThumbnailSizePixel / 7.); + QFont font = this->SeriesThumbnail->font(); + font.setBold(true); + font.setPixelSize(textSize); - QPixmap resultPixmap(this->ThumbnailSize, this->ThumbnailSize); + QPixmap resultPixmap(this->ThumbnailSizePixel, this->ThumbnailSizePixel); resultPixmap.fill(Qt::transparent); ctkDICOMThumbnailGenerator thumbnailGenerator; - thumbnailGenerator.setWidth(this->ThumbnailSize); - thumbnailGenerator.setHeight(this->ThumbnailSize); + thumbnailGenerator.setWidth(this->ThumbnailSizePixel); + thumbnailGenerator.setHeight(this->ThumbnailSizePixel); QImage thumbnailImage; QPainter painter; - thumbnailGenerator.generateBlankThumbnail(thumbnailImage, Qt::white); + QColor backgroundColor = this->SeriesThumbnail->palette().color(QPalette::Normal, QPalette::Window); + thumbnailGenerator.generateBlankThumbnail(thumbnailImage, backgroundColor); resultPixmap = QPixmap::fromImage(thumbnailImage); if (painter.begin(&resultPixmap)) { painter.setRenderHint(QPainter::Antialiasing); QRect rect = resultPixmap.rect(); - painter.setFont(QFont("Arial", fontSize, QFont::Bold)); - this->drawTextWithShadow(&painter, rect.adjusted(margin, margin, margin, margin), Qt::AlignCenter, this->Modality); + int x = int(rect.width() * 0.5); + int y = int(rect.height() * 0.5); + this->drawTextWithShadow(&painter, font, x, y, Qt::AlignCenter, this->Modality); painter.end(); } @@ -359,30 +370,30 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num return; } - int margin = 10; - int fontSize = 12; - if (!this->SeriesThumbnail->text().isEmpty()) - { - margin = 5; - fontSize = 14; - } + int margin = floor(this->ThumbnailSizePixel / 60.); + int iconSize = floor(this->ThumbnailSizePixel / 6.); + int textSize = floor(this->ThumbnailSizePixel / 12.); + QFont font = this->SeriesThumbnail->font(); + font.setBold(true); + font.setPixelSize(textSize); - QPixmap resultPixmap(this->ThumbnailSize, this->ThumbnailSize); + QPixmap resultPixmap(this->ThumbnailSizePixel, this->ThumbnailSizePixel); resultPixmap.fill(Qt::transparent); ctkDICOMThumbnailGenerator thumbnailGenerator; - thumbnailGenerator.setWidth(this->ThumbnailSize); - thumbnailGenerator.setHeight(this->ThumbnailSize); + thumbnailGenerator.setWidth(this->ThumbnailSizePixel); + thumbnailGenerator.setHeight(this->ThumbnailSizePixel); bool thumbnailGenerated = true; bool emptyThumbnailGenerated = false; QPainter painter; - if (this->ThumbnailImage.width() != this->ThumbnailSize) + if (this->ThumbnailImage.width() != this->ThumbnailSizePixel) { if (!thumbnailGenerator.generateThumbnail(file, this->ThumbnailImage)) { thumbnailGenerated = false; emptyThumbnailGenerated = true; this->isThumbnailDocument = true; - thumbnailGenerator.generateBlankThumbnail(this->ThumbnailImage, Qt::white); + QColor backgroundColor = this->SeriesThumbnail->palette().color(QPalette::Normal, QPalette::Window); + thumbnailGenerator.generateBlankThumbnail(this->ThumbnailImage, backgroundColor); resultPixmap = QPixmap::fromImage(this->ThumbnailImage); if (painter.begin(&resultPixmap)) { @@ -400,18 +411,18 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num { painter.setRenderHint(QPainter::Antialiasing); QRect rect = resultPixmap.rect(); - painter.setFont(QFont("Arial", fontSize, QFont::Bold)); - int x = int((rect.width() / 2) - (this->ThumbnailImage.rect().width() / 2)); - int y = int((rect.height() / 2) - (this->ThumbnailImage.rect().height() / 2)); + painter.setFont(font); + int x = int((rect.width() * 0.5) - (this->ThumbnailImage.rect().width() * 0.5)); + int y = int((rect.height() * 0.5) - (this->ThumbnailImage.rect().height() * 0.5)); painter.drawPixmap(x, y, QPixmap::fromImage(this->ThumbnailImage)); - QString topLeft = ctkDICOMSeriesItemWidget::tr("Series: %1\n%2").arg(this->SeriesNumber).arg(this->Modality); - this->drawTextWithShadow(&painter, rect.adjusted(margin, margin, margin, margin), Qt::AlignTop | Qt::AlignLeft, topLeft); - QString bottomLeft = ctkDICOMSeriesItemWidget::tr("N.frames: %1").arg(numberOfFrames); - this->drawTextWithShadow(&painter, rect.adjusted(margin, -margin, margin, -margin), Qt::AlignBottom | Qt::AlignLeft, bottomLeft); + + QString topLeftString = ctkDICOMSeriesItemWidget::tr("Series: %1\n%2").arg(this->SeriesNumber).arg(this->Modality); + this->drawTextWithShadow(&painter, font, margin, margin, Qt::AlignTop | Qt::AlignLeft, topLeftString); QString rows = this->DicomDatabase->instanceValue(this->CentralFrameSOPInstanceUID, "0028,0010"); QString columns = this->DicomDatabase->instanceValue(this->CentralFrameSOPInstanceUID, "0028,0011"); - QString bottomRight = rows + "x" + columns; - this->drawTextWithShadow(&painter, rect.adjusted(-margin, -margin, -margin, -margin), Qt::AlignBottom | Qt::AlignRight, bottomRight); + QString bottomLeftString = rows + "x" + columns + "x" + QString::number(numberOfFrames); + this->drawTextWithShadow(&painter, font, margin, rect.height() - margin, + Qt::AlignBottom | Qt::AlignLeft, bottomLeftString); QSvgRenderer renderer; if (this->IsCloud) @@ -435,7 +446,7 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num } QPoint topRight = rect.topRight(); - QRectF bounds(topRight.x() - 48 - margin, topRight.y() + margin, 48, 48); + QRectF bounds(topRight.x() - iconSize - margin, topRight.y() + margin, iconSize, iconSize); renderer.render(&painter, bounds); painter.end(); } @@ -449,16 +460,67 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num //---------------------------------------------------------------------------- void ctkDICOMSeriesItemWidgetPrivate::drawTextWithShadow(QPainter *painter, - const QRect &r, - int flags, const - QString &text) + const QFont &font, + const int &x, + const int &y, + const Qt::Alignment &alignment, + const QString &text) { - painter->setPen(Qt::darkGray); - painter->drawText(r.adjusted(1, 1, 1, 1), flags, text); - painter->setPen(QColor(Qt::gray)); - painter->drawText(r.adjusted(2, 2, 2, 2), flags, text); - painter->setPen(QPen(QColor(41, 121, 255))); - painter->drawText(r, flags, text); + QColor textColor(60, 164, 255, 225); + QGraphicsDropShadowEffect* dropShadowEffect = new QGraphicsDropShadowEffect; + dropShadowEffect->setXOffset(1); + dropShadowEffect->setYOffset(1); + dropShadowEffect->setBlurRadius(1); + dropShadowEffect->setColor(Qt::gray); + QLabel textLabel; + textLabel.setObjectName("ctkDrawTextWithShadowQLabel"); + QPalette palette = textLabel.palette(); + palette.setColor(QPalette::WindowText, textColor); + textLabel.setPalette(palette); + textLabel.setFont(font); + textLabel.setGraphicsEffect(dropShadowEffect); + textLabel.setText(text); + QPixmap textPixMap = textLabel.grab(); + QRect rect = textPixMap.rect(); + int textWidth = rect.width(); + int textHeight = rect.height(); + + if (alignment == Qt::AlignCenter) + { + painter->drawPixmap(x - textWidth * 0.5, y - textHeight * 0.5, textPixMap); + } + else if (alignment == (Qt::AlignTop | Qt::AlignLeft)) + { + painter->drawPixmap(x, y, textPixMap); + } + else if (alignment == Qt::AlignTop) + { + painter->drawPixmap(x - textWidth * 0.5, y, textPixMap); + } + else if (alignment == (Qt::AlignTop | Qt::AlignRight)) + { + painter->drawPixmap(x - textWidth, y, textPixMap); + } + else if (alignment == (Qt::AlignHCenter | Qt::AlignLeft)) + { + painter->drawPixmap(x, y - textHeight * 0.5, textPixMap); + } + else if (alignment == (Qt::AlignHCenter | Qt::AlignRight)) + { + painter->drawPixmap(x - textWidth, y - textHeight * 0.5, textPixMap); + } + else if (alignment == (Qt::AlignBottom | Qt::AlignLeft)) + { + painter->drawPixmap(x, y - textHeight, textPixMap); + } + else if (alignment == Qt::AlignBottom) + { + painter->drawPixmap(x - textWidth * 0.5, y - textHeight, textPixMap); + } + else if (alignment == (Qt::AlignBottom | Qt::AlignRight)) + { + painter->drawPixmap(x - textWidth, y - textHeight, textPixMap); + } } //---------------------------------------------------------------------------- @@ -485,7 +547,7 @@ void ctkDICOMSeriesItemWidgetPrivate::updateThumbnailProgressBar() { this->drawThumbnail(file, numberOfFrames); } - } + } } //---------------------------------------------------------------------------- @@ -594,6 +656,7 @@ void ctkDICOMSeriesItemWidget::setSeriesDescription(const QString& seriesDescrip { Q_D(ctkDICOMSeriesItemWidget); d->SeriesThumbnail->setText(seriesDescription); + d->SeriesThumbnail->textPushButton()->setToolTip(seriesDescription); } //------------------------------------------------------------------------------ @@ -638,6 +701,20 @@ bool ctkDICOMSeriesItemWidget::isCloud() const return d->IsCloud; } +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidget::setRetrieveFailed(const bool &retrieveFailed) +{ + Q_D(ctkDICOMSeriesItemWidget); + d->RetrieveFailed = retrieveFailed; +} + +//---------------------------------------------------------------------------- +bool ctkDICOMSeriesItemWidget::retrieveFailed() const +{ + Q_D(const ctkDICOMSeriesItemWidget); + return d->RetrieveFailed; +} + //---------------------------------------------------------------------------- bool ctkDICOMSeriesItemWidget::IsLoaded() const { @@ -652,18 +729,18 @@ bool ctkDICOMSeriesItemWidget::IsVisible() const return d->IsVisible; } -//---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::setThumbnailSize(int thumbnailSize) +//------------------------------------------------------------------------------ +void ctkDICOMSeriesItemWidget::setThumbnailSizePixel(const int &thumbnailSizePixel) { Q_D(ctkDICOMSeriesItemWidget); - d->ThumbnailSize = thumbnailSize; + d->ThumbnailSizePixel = thumbnailSizePixel; } //------------------------------------------------------------------------------ -int ctkDICOMSeriesItemWidget::thumbnailSize() const +int ctkDICOMSeriesItemWidget::thumbnailSizePixel() const { Q_D(const ctkDICOMSeriesItemWidget); - return d->ThumbnailSize; + return d->ThumbnailSizePixel; } //---------------------------------------------------------------------------- diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index b01d42de26..81a527d706 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -45,8 +45,9 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget Q_PROPERTY(QString seriesNumber READ seriesNumber WRITE setSeriesNumber); Q_PROPERTY(QString modality READ modality WRITE setModality); Q_PROPERTY(QString seriesDescription READ seriesDescription WRITE setSeriesDescription); - Q_PROPERTY(QString isCloud READ isCloud); - Q_PROPERTY(int thumbnailSize READ thumbnailSize WRITE setThumbnailSize); + Q_PROPERTY(bool isCloud READ isCloud); + Q_PROPERTY(bool retrieveFailed READ retrieveFailed WRITE setRetrieveFailed); + Q_PROPERTY(int thumbnailSizePixel READ thumbnailSizePixel WRITE setThumbnailSizePixel); Q_PROPERTY(bool stopJobs READ stopJobs WRITE setStopJobs); Q_PROPERTY(bool raiseJobsPriority READ raiseJobsPriority WRITE setRaiseJobsPriority); @@ -94,16 +95,20 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget /// Series lives in the server bool isCloud() const; + /// in case the retrieve job failed + void setRetrieveFailed(const bool& retrieveFailed); + bool retrieveFailed() const; + /// Series has been loaded by the parent widget bool IsLoaded() const; /// Series is visible in the parent widget bool IsVisible() const; - /// Series Thumbnail size - /// 300 px by default - void setThumbnailSize(int thumbnailSize); - int thumbnailSize() const; + /// Set the thumbnail size in pixel + /// 200 by default + void setThumbnailSizePixel(const int &thumbnailSizePixel); + int thumbnailSizePixel() const; /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 09047d83d1..4a0fdb8d1b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -278,12 +278,12 @@ void ctkDICOMServerNodeWidget2Private::init() q, SLOT(onTestCurrentServerNode())); QObject::connect(this->RemoveButton, SIGNAL(clicked()), q, SLOT(onRemoveCurrentServerNode())); - this->SaveButton = this->buttonBox->button(QDialogButtonBox::StandardButton::Save); - this->SaveButton->setStyleSheet("text-align:left;"); + this->SaveButton = this->ActionsButtonBox->button(QDialogButtonBox::StandardButton::Save); this->SaveButton->setText(QObject::tr("Apply changes")); - this->RestoreButton = this->buttonBox->button(QDialogButtonBox::StandardButton::Discard); - this->SaveButton->setStyleSheet("text-align:left;"); + this->SaveButton->setIcon(QIcon(":/Icons/save.svg")); + this->RestoreButton = this->ActionsButtonBox->button(QDialogButtonBox::StandardButton::Discard); this->RestoreButton->setText(QObject::tr("Discard changes")); + this->RestoreButton->setIcon(QIcon(":/Icons/cancel.svg")); QObject::connect(this->RestoreButton, SIGNAL(clicked()), q, SLOT(readSettings())); QObject::connect(this->SaveButton, SIGNAL(clicked()), @@ -299,11 +299,11 @@ void ctkDICOMServerNodeWidget2Private::disconnectScheduler() return; } - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QString, QString)), + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), q, SLOT(updateGUIState())); - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QString, QString)), + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), q, SLOT(updateGUIState())); - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), + ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), q, SLOT(updateGUIState())); } @@ -316,11 +316,11 @@ void ctkDICOMServerNodeWidget2Private::connectScheduler() return; } - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobStarted(QString, QString)), + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), q, SLOT(updateGUIState())); - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFinished(QString, QString)), + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), q, SLOT(updateGUIState())); - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), + ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), q, SLOT(updateGUIState())); } diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 69f650b49a..4200f55548 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -24,6 +24,7 @@ //Qt includes #include #include +#include #include // CTK includes @@ -35,7 +36,6 @@ #include "ctkDICOMJobResponseSet.h" // ctkDICOMWidgets includes -#include "ctkDICOMSeriesItemWidget.h" #include "ctkDICOMStudyItemWidget.h" #include "ui_ctkDICOMStudyItemWidget.h" @@ -66,6 +66,10 @@ class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget void init(QWidget* parentWidget); void updateColumnsWidths(); void createSeries(); + int getScreenWidth(); + int getScreenHeight(); + int calculateNumerOfSeriesPerRow(); + int calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); void addEmptySeriesItemWidget(const int& rowIndex, const int& columnIndex); bool isSeriesItemAlreadyAdded(const QString& seriesItem); @@ -77,7 +81,8 @@ class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget QSharedPointer Scheduler; QSharedPointer VisualDICOMBrowser; - int ThumbnailSize; + ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; + int ThumbnailSizePixel; QString PatientID; QString StudyInstanceUID; QString StudyItem; @@ -90,7 +95,8 @@ class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget ctkDICOMStudyItemWidgetPrivate::ctkDICOMStudyItemWidgetPrivate(ctkDICOMStudyItemWidget& obj) : q_ptr(&obj) { - this->ThumbnailSize = 300; + this->ThumbnailSize = ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium; + this->ThumbnailSizePixel = 200; this->FilteringSeriesDescription = ""; this->PatientID = ""; this->StudyInstanceUID = ""; @@ -142,9 +148,9 @@ void ctkDICOMStudyItemWidgetPrivate::init(QWidget* parentWidget) //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidgetPrivate::updateColumnsWidths() { - for (int i = 0; i < this->SeriesListTableWidget->columnCount(); ++i) + for (int columnIndex = 0; columnIndex < this->SeriesListTableWidget->columnCount(); ++columnIndex) { - this->SeriesListTableWidget->setColumnWidth(i, this->ThumbnailSize); + this->SeriesListTableWidget->setColumnWidth(columnIndex, this->ThumbnailSizePixel); } } @@ -228,13 +234,84 @@ void ctkDICOMStudyItemWidgetPrivate::createSeries() { iHeight += this->SeriesListTableWidget->verticalHeader()->sectionSize(rowIndex); } - if (iHeight < this->ThumbnailSize) + if (iHeight < this->ThumbnailSizePixel) { - iHeight = this->ThumbnailSize; + iHeight = this->ThumbnailSizePixel; } iHeight += 25; this->SeriesListTableWidget->setMinimumHeight(iHeight); + } +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidgetPrivate::getScreenWidth() +{ + QList screens = QApplication::screens(); + int width = 1920; + foreach (QScreen* screen, screens) + { + QRect rec = screen->geometry(); + if (rec.width() > width) + { + width = rec.width(); + } } + + return width; +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidgetPrivate::getScreenHeight() +{ + QList screens = QApplication::screens(); + int height = 1080; + foreach (QScreen* screen, screens) + { + QRect rec = screen->geometry(); + if (rec.height() > height) + { + height = rec.height(); + } + } + + return height; +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidgetPrivate::calculateNumerOfSeriesPerRow() +{ + int width = this->getScreenWidth(); + int numberOfSeriesPerRow = 1; + numberOfSeriesPerRow = floor(width / this->ThumbnailSizePixel) - 1; + + return numberOfSeriesPerRow; +} + +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidgetPrivate::calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) +{ + int height = this->getScreenHeight(); + int thumbnailSizeInPixel = 1; + switch (thumbnailSize) + { + case ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small: + { + thumbnailSizeInPixel = floor(height / 7.); + } + break; + case ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium: + { + thumbnailSizeInPixel = floor(height / 5.5); + } + break; + case ctkDICOMStudyItemWidget::ThumbnailSizeOption::Large: + { + thumbnailSizeInPixel = floor(height / 4.); + } + break; + } + + return thumbnailSizeInPixel; } //------------------------------------------------------------------------------ @@ -243,7 +320,7 @@ void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(const int& rowInde { QTableWidgetItem *tableItem = new QTableWidgetItem; tableItem->setFlags(Qt::NoItemFlags); - tableItem->setSizeHint(QSize(this->ThumbnailSize, this->ThumbnailSize)); + tableItem->setSizeHint(QSize(this->ThumbnailSizePixel, this->ThumbnailSizePixel)); this->SeriesListTableWidget->setItem(rowIndex, columnIndex, tableItem); } @@ -388,14 +465,6 @@ bool ctkDICOMStudyItemWidget::collapsed()const return d->StudyItemCollapsibleGroupBox->collapsed(); } -//---------------------------------------------------------------------------- -void ctkDICOMStudyItemWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) -{ - Q_D(ctkDICOMStudyItemWidget); - d->SeriesListTableWidget->setColumnCount(numberOfSeriesPerRow); - d->updateColumnsWidths(); -} - //------------------------------------------------------------------------------ int ctkDICOMStudyItemWidget::numberOfSeriesPerRow() const { @@ -403,21 +472,30 @@ int ctkDICOMStudyItemWidget::numberOfSeriesPerRow() const return d->SeriesListTableWidget->columnCount(); } -//---------------------------------------------------------------------------- -void ctkDICOMStudyItemWidget::setThumbnailSize(int thumbnailSize) +//------------------------------------------------------------------------------ +void ctkDICOMStudyItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) { Q_D(ctkDICOMStudyItemWidget); d->ThumbnailSize = thumbnailSize; + d->ThumbnailSizePixel = d->calculateThumbnailSizeInPixel(d->ThumbnailSize); + d->SeriesListTableWidget->setColumnCount(d->calculateNumerOfSeriesPerRow()); d->updateColumnsWidths(); } //------------------------------------------------------------------------------ -int ctkDICOMStudyItemWidget::thumbnailSize() const +ctkDICOMStudyItemWidget::ThumbnailSizeOption ctkDICOMStudyItemWidget::thumbnailSize() const { Q_D(const ctkDICOMStudyItemWidget); return d->ThumbnailSize; } +//------------------------------------------------------------------------------ +int ctkDICOMStudyItemWidget::thumbnailSizePixel() const +{ + Q_D(const ctkDICOMStudyItemWidget); + return d->ThumbnailSizePixel; +} + //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidget::setSelection(bool selected) { @@ -556,6 +634,30 @@ QTableWidget *ctkDICOMStudyItemWidget::seriesListTableWidget() return d->SeriesListTableWidget; } +//------------------------------------------------------------------------------ +QList ctkDICOMStudyItemWidget::seriesItemWidgetsList() const +{ + Q_D(const ctkDICOMStudyItemWidget); + QList seriesItemWidgetsList; + + for (int row = 0; row < d->SeriesListTableWidget->rowCount(); row++) + { + for (int column = 0 ; column < d->SeriesListTableWidget->columnCount(); column++) + { + ctkDICOMSeriesItemWidget* seriesItemWidget = + qobject_cast(d->SeriesListTableWidget->cellWidget(row, column)); + if (!seriesItemWidget) + { + continue; + } + + seriesItemWidgetsList.append(seriesItemWidget); + } + } + + return seriesItemWidgetsList; +} + //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidget::addSeriesItemWidget(const int& tableIndex, const QString &seriesItem, @@ -579,7 +681,7 @@ void ctkDICOMStudyItemWidget::addSeriesItemWidget(const int& tableIndex, seriesItemWidget->setSeriesNumber(seriesNumber); seriesItemWidget->setModality(modality); seriesItemWidget->setSeriesDescription(seriesDescription); - seriesItemWidget->setThumbnailSize(d->ThumbnailSize); + seriesItemWidget->setThumbnailSizePixel(d->ThumbnailSizePixel); seriesItemWidget->setDicomDatabase(d->DicomDatabase); seriesItemWidget->setScheduler(d->Scheduler); seriesItemWidget->generateInstances(); @@ -589,14 +691,14 @@ void ctkDICOMStudyItemWidget::addSeriesItemWidget(const int& tableIndex, d->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); QTableWidgetItem *tableItem = new QTableWidgetItem; - tableItem->setSizeHint(QSize(d->ThumbnailSize, d->ThumbnailSize)); + tableItem->setSizeHint(QSize(d->ThumbnailSizePixel, d->ThumbnailSizePixel)); int rowIndex = floor(tableIndex / d->SeriesListTableWidget->columnCount()); int columnIndex = tableIndex % d->SeriesListTableWidget->columnCount(); if (columnIndex == 0) { d->SeriesListTableWidget->insertRow(rowIndex); - d->SeriesListTableWidget->setRowHeight(rowIndex, d->ThumbnailSize + 30); + d->SeriesListTableWidget->setRowHeight(rowIndex, d->ThumbnailSizePixel + 30); } d->SeriesListTableWidget->setItem(rowIndex, columnIndex, tableItem); @@ -629,7 +731,7 @@ void ctkDICOMStudyItemWidget::removeSeriesItemWidget(const QString& seriesItem) d->addEmptySeriesItemWidget(row, column); break; } - } + } } //------------------------------------------------------------------------------ @@ -683,7 +785,5 @@ void ctkDICOMStudyItemWidget::updateGUIFromScheduler(QVariant data) //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidget::onStudySelectionClicked(bool toggled) { - Q_D(ctkDICOMStudyItemWidget); - this->setSelection(toggled); } diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index 2b139d6097..eb6d3e3f66 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -30,10 +30,14 @@ #include #include +// ctkDICOMWidgets includes +#include "ctkDICOMSeriesItemWidget.h" + class ctkCollapsibleGroupBox; -class ctkDICOMStudyItemWidgetPrivate; class ctkDICOMDatabase; class ctkDICOMScheduler; +class ctkDICOMSeriesItemWidget; +class ctkDICOMStudyItemWidgetPrivate; class QTableWidget; @@ -41,14 +45,16 @@ class QTableWidget; class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget { Q_OBJECT; + Q_ENUMS(ThumbnailSizeOption) Q_PROPERTY(QString studyItem READ studyItem WRITE setStudyItem); Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); Q_PROPERTY(QString title READ title WRITE setTitle); Q_PROPERTY(QString description READ description WRITE setDescription); Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed); - Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); - Q_PROPERTY(int thumbnailSize READ thumbnailSize WRITE setThumbnailSize); + Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow); + Q_PROPERTY(ThumbnailSizeOption thumbnailSize READ thumbnailSize WRITE setThumbnailSize); + Q_PROPERTY(int thumbnailSizePixel READ thumbnailSizePixel); public: typedef QWidget Superclass; @@ -81,14 +87,22 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget bool collapsed() const; /// Number of series displayed per row - /// 6 by default - void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); int numberOfSeriesPerRow() const; - /// Series Thumbnail size - /// 300 px by default - void setThumbnailSize(int thumbnailSize); - int thumbnailSize() const; + enum ThumbnailSizeOption + { + Small = 0, + Medium, + Large, + }; + + /// Set the thumbnail size: small, medium, large + /// medium by default + void setThumbnailSize(const ThumbnailSizeOption &thumbnailSize); + ThumbnailSizeOption thumbnailSize() const; + + /// Thumbnail size in pixel + int thumbnailSizePixel() const; /// Study is selected void setSelection(bool selected); @@ -127,6 +141,9 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget /// Series list table. Q_INVOKABLE QTableWidget* seriesListTableWidget(); + /// Return all the series item widgets for the study + Q_INVOKABLE QList seriesItemWidgetsList()const; + /// Add/Remove Series item widget Q_INVOKABLE void addSeriesItemWidget(const int& tableIndex, const QString &seriesItem, diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp index fed5766fc9..bc4e7ad52a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp @@ -214,7 +214,7 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(const QString dcmImagePath, c } //------------------------------------------------------------------------------ -void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image, Qt::GlobalColor color) +void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image, QColor color) { Q_D(ctkDICOMThumbnailGenerator); if (image.width() != d->Width || image.height() != d->Height) diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h index 2ea7be7024..f8df192dff 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h @@ -24,6 +24,7 @@ // Qt includes class QImage; +#include // CTK includes #include "ctkDICOMWidgetsExport.h" @@ -56,7 +57,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstr /// Generate a blank thumbnail image (currently a solid gray box of the requested thumbnail size). /// It can be used as a placeholder for invalid images or duringan image is loaded. - Q_INVOKABLE void generateBlankThumbnail(QImage& image, Qt::GlobalColor color = Qt::darkGray); + Q_INVOKABLE void generateBlankThumbnail(QImage& image, QColor color = Qt::darkGray); /// Set thumbnail width void setWidth(int width); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 85e2a539d3..b5735efa3f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -29,12 +29,14 @@ #include #include #include +#include #include #include #include // CTK includes #include +#include #include #include #include @@ -51,7 +53,6 @@ // ctkDICOMWidgets includes #include "ctkDICOMObjectListWidget.h" #include "ctkDICOMVisualBrowserWidget.h" -#include "ctkDICOMStudyItemWidget.h" #include "ctkDICOMSeriesItemWidget.h" #include "ctkDICOMServerNodeWidget2.h" #include "ctkDICOMVisualBrowserWidget.h" @@ -137,10 +138,12 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget void importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); void importFiles(const QStringList& files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); void importOldSettings(); + void showUpdateSchemaDialog(); void updateModalityCheckableComboBox(); void createPatients(); void updateFiltersWarnings(); - void setBackgroundColorToWidget(Qt::GlobalColor color, QWidget* widget); + void setBackgroundColorToFilterWidgets(bool warning = false); + void setBackgroundColorToWidget(QColor color, QWidget* widget); void retrieveSeries(); bool updateServer(ctkDICOMServer* server); void removeAllPatientItemWidgets(); @@ -158,6 +161,9 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget const QMap& filters); QStringList filterSeriesList(const QStringList& patientList, const QMap& filters); + ctkDICOMStudyItemWidget* getCurrentPatientStudyWidgetByUIDs(QString studyInstanceUID); + ctkDICOMSeriesItemWidget* getCurrentPatientSeriesWidgetByUIDs(QString studyInstanceUID, + QString seriesInstanceUID); // Return a sanitized version of the string that is safe to be used // as a filename component. @@ -218,14 +224,15 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget QStringList FilteringModalities; int NumberOfStudiesPerPatient; - int NumberOfSeriesPerRow; - int MinimumThumbnailSize; + ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; bool SendActionVisible; bool DeleteActionVisible; bool IsGUIUpdating; bool IsLoading; ctkDICOMServerNodeWidget2* ServerNodeWidget; + QProgressDialog *UpdateSchemaProgress; + QProgressDialog *ExportProgress; }; CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QString, databaseDirectoryBase, DatabaseDirectoryBase); @@ -256,8 +263,7 @@ ctkDICOMVisualBrowserWidgetPrivate::ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMV this->DatabaseDirectory = ""; this->NumberOfStudiesPerPatient = 2; - this->NumberOfSeriesPerRow = 6; - this->MinimumThumbnailSize = 300; + this->ThumbnailSize = ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium; this->SendActionVisible = false; this->DeleteActionVisible = true; @@ -288,6 +294,9 @@ ctkDICOMVisualBrowserWidgetPrivate::ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMV this->ServerNodeWidget = new ctkDICOMServerNodeWidget2(); this->ServerNodeWidget->setScheduler(this->Scheduler); this->connectScheduler(); + + this->ExportProgress = nullptr; + this->UpdateSchemaProgress = nullptr; } //---------------------------------------------------------------------------- @@ -302,6 +311,14 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() Q_Q(ctkDICOMVisualBrowserWidget); this->setupUi(q); + this->DatabaseDirectoryProblemFrame->hide(); + QObject::connect(this->SelectDatabaseDirectoryButton, SIGNAL(clicked()), + q, SLOT(selectDatabaseDirectory())); + QObject::connect(this->CreateNewDatabaseButton, SIGNAL(clicked()), + q, SLOT(createNewDatabaseDirectory())); + QObject::connect(this->UpdateDatabaseButton, SIGNAL(clicked()), + q, SLOT(updateDatabase())); + this->WarningPushButton->hide(); QObject::connect(this->FilteringPatientIDSearchBox, SIGNAL(textChanged(QString)), q, SLOT(onFilteringPatientIDChanged())); @@ -323,36 +340,23 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() q, SLOT(onFilteringDateComboBoxChanged(int))); QObject::connect(this->QueryPatientPushButton, SIGNAL(clicked()), - q, SLOT(onQueryPatient())); + q, SLOT(onQueryPatients())); this->ServersSettingsCollapsibleGroupBox->layout()->addWidget(this->ServerNodeWidget); - - QSize iconSize(28, 28); - this->PatientsTabWidget->setIconSize(iconSize); this->PatientsTabWidget->clear(); - // setup patients menu on a tool button on the tab bar - QTabBar* tabWidget = this->PatientsTabWidget->tabBar(); - tabWidget->setDocumentMode(true); - tabWidget->setExpanding(true); - + // setup patients menu this->patientsTabMenuToolButton = new QToolButton(q); this->patientsTabMenuToolButton->setObjectName("patientsTabMenuToolButton"); - this->patientsTabMenuToolButton->setIconSize(iconSize); - this->patientsTabMenuToolButton->setFixedHeight(40); this->patientsTabMenuToolButton->setCheckable(false); this->patientsTabMenuToolButton->setChecked(false); this->patientsTabMenuToolButton->setIcon(QIcon(":/Icons/more_vert.svg")); this->patientsTabMenuToolButton->hide(); - this->patientsTabMenuToolButton->setStyleSheet(this->patientsTabMenuToolButton->styleSheet() + - "QToolButton{padding-top: 5px; padding-bottom: 5px}"); - QObject::connect(this->patientsTabMenuToolButton, SIGNAL(clicked()), q, SLOT(onPatientsTabMenuToolButtonClicked())); - this->PatientsTabWidget->setCornerWidget(this->patientsTabMenuToolButton, Qt::TopRightCorner); - + this->PatientsTabWidget->setCornerWidget(this->patientsTabMenuToolButton, Qt::TopLeftCorner); QObject::connect(this->PatientsTabWidget, SIGNAL(currentChanged(int)), q, SLOT(onPatientItemChanged(int))); @@ -429,8 +433,8 @@ void ctkDICOMVisualBrowserWidgetPrivate::connectScheduler() ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), q, SLOT(updateGUIFromScheduler(QVariant))); - ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QString, QString)), - q, SLOT(onTaskFailed(QString, QString))); + ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), + q, SLOT(onTaskFailed(QVariant))); ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progress(int)), q, SLOT(onIndexingProgress(int))); ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressStep(QString)),q, SLOT(onIndexingProgressStep(QString))); ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressDetail(QString)), q, SLOT(onIndexingProgressDetail(QString))); @@ -487,12 +491,45 @@ void ctkDICOMVisualBrowserWidgetPrivate::importOldSettings() QSettings settings; int dontConfirmCopyOnImport = settings.value("MainWindow/DontConfirmCopyOnImport", static_cast(QMessageBox::InvalidRole)).toInt(); if (dontConfirmCopyOnImport == QMessageBox::AcceptRole) - { + { settings.setValue("DICOM/ImportDirectoryMode", static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryCopy)); - } + } settings.remove("MainWindow/DontConfirmCopyOnImport"); } +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::showUpdateSchemaDialog() +{ + Q_Q(ctkDICOMVisualBrowserWidget); + if (this->UpdateSchemaProgress == 0) + { + // + // Set up the Update Schema Progress Dialog + // + this->UpdateSchemaProgress = new QProgressDialog( + ctkDICOMVisualBrowserWidget::tr("DICOM Schema Update"), ctkDICOMVisualBrowserWidget::tr("Cancel"), 0, 100, q, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + + // We don't want the progress dialog to resize itself, so we bypass the label by creating our own + QLabel* progressLabel = new QLabel(ctkDICOMVisualBrowserWidget::tr("Initialization...")); + this->UpdateSchemaProgress->setLabel(progressLabel); + this->UpdateSchemaProgress->setWindowModality(Qt::ApplicationModal); + this->UpdateSchemaProgress->setMinimumDuration(0); + this->UpdateSchemaProgress->setValue(0); + + q->connect(DicomDatabase.data(), SIGNAL(schemaUpdateStarted(int)), + this->UpdateSchemaProgress, SLOT(setMaximum(int))); + q->connect(DicomDatabase.data(), SIGNAL(schemaUpdateProgress(int)), + this->UpdateSchemaProgress, SLOT(setValue(int))); + q->connect(DicomDatabase.data(), SIGNAL(schemaUpdateProgress(QString)), + progressLabel, SLOT(setText(QString))); + + // close the dialog + q->connect(this->DicomDatabase.data(), SIGNAL(schemaUpdated()), + this->UpdateSchemaProgress, SLOT(close())); + } + this->UpdateSchemaProgress->show(); +} + //---------------------------------------------------------------------------- void ctkDICOMVisualBrowserWidgetPrivate::updateModalityCheckableComboBox() { @@ -629,6 +666,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::createPatients() //---------------------------------------------------------------------------- void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() { + Q_Q(ctkDICOMVisualBrowserWidget); if (!this->DicomDatabase) { logger.error("updateFiltersWarnings failed, no DICOM database has been set. \n"); @@ -637,15 +675,15 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() // Loop over all the data in the dicom database and apply the filters. // If there are no series, highlight which are the filters that produce no results - Qt::GlobalColor color = Qt::white; - this->setBackgroundColorToWidget(color, this->FilteringPatientIDSearchBox); - this->setBackgroundColorToWidget(color, this->FilteringPatientNameSearchBox); - this->setBackgroundColorToWidget(color, this->FilteringDateComboBox); - this->setBackgroundColorToWidget(color, this->FilteringStudyDescriptionSearchBox); - this->setBackgroundColorToWidget(color, this->FilteringSeriesDescriptionSearchBox); - this->setBackgroundColorToWidget(color, this->FilteringModalityCheckableComboBox); - - color = Qt::yellow; + this->setBackgroundColorToFilterWidgets(); + + QColor visualDICOMBrowserColor = q->palette().color(QPalette::Normal, q->backgroundRole()); + QColor color = Qt::yellow; + if (visualDICOMBrowserColor.lightnessF() < 0.5) + { + color.setRgb(60, 164, 255); + } + QStringList patientList = this->DicomDatabase->patients(); if (patientList.count() == 0) { @@ -703,7 +741,49 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateFiltersWarnings() } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToWidget(Qt::GlobalColor color, +void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToFilterWidgets(bool warning) +{ + Q_Q(ctkDICOMVisualBrowserWidget); + QColor visualDICOMBrowserColor = q->palette().color(QPalette::Normal, q->backgroundRole()); + if (warning) + { + QColor color = Qt::yellow; + if (visualDICOMBrowserColor.lightnessF() < 0.5) + { + color.setRgb(60, 164, 255); + } + this->setBackgroundColorToWidget(color, this->FilteringPatientIDSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringPatientNameSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringDateComboBox); + this->setBackgroundColorToWidget(color, this->FilteringStudyDescriptionSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringSeriesDescriptionSearchBox); + this->setBackgroundColorToWidget(color, this->FilteringModalityCheckableComboBox); + } + else + { + QColor colorSearchBox(255, 255, 255); + QColor colorButton(239, 239, 239); + if (visualDICOMBrowserColor.lightnessF() < 0.5) + { + colorSearchBox.setRgb(30, 30, 30); + colorButton.setRgb(50, 50, 50); + } + else if (visualDICOMBrowserColor.lightnessF() > 0.95) + { + colorSearchBox.setRgb(255, 255, 255); + colorButton.setRgb(255, 255, 255); + } + this->setBackgroundColorToWidget(colorSearchBox, this->FilteringPatientIDSearchBox); + this->setBackgroundColorToWidget(colorSearchBox, this->FilteringPatientNameSearchBox); + this->setBackgroundColorToWidget(colorButton, this->FilteringDateComboBox); + this->setBackgroundColorToWidget(colorSearchBox, this->FilteringStudyDescriptionSearchBox); + this->setBackgroundColorToWidget(colorSearchBox, this->FilteringSeriesDescriptionSearchBox); + this->setBackgroundColorToWidget(colorButton, this->FilteringModalityCheckableComboBox); + } +} + +//---------------------------------------------------------------------------- +void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToWidget(QColor color, QWidget *widget) { if (!widget) @@ -840,7 +920,6 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() while(wait) { this->Scheduler->waitForDone(300); - wait = false; foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, selectedSeriesWidgetsList) { @@ -849,7 +928,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() continue; } - if (seriesItemWidget->isCloud()) + if (seriesItemWidget->isCloud() && !seriesItemWidget->retrieveFailed()) { wait = true; break; @@ -969,7 +1048,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateSeriesTablesSelection(ctkDICOMSer return; } } - } + } } //---------------------------------------------------------------------------- @@ -1241,6 +1320,54 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringLi return filteredSeriesList; } +//---------------------------------------------------------------------------- +ctkDICOMStudyItemWidget *ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientStudyWidgetByUIDs(QString studyInstanceUID) +{ + ctkDICOMPatientItemWidget *patientItemWidget = + qobject_cast(this->PatientsTabWidget->currentWidget()); + if (!patientItemWidget) + { + return nullptr; + } + + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + { + if (!studyItemWidget || studyItemWidget->studyInstanceUID() != studyInstanceUID) + { + continue; + } + + return studyItemWidget; + } + + return nullptr; +} + +//---------------------------------------------------------------------------- +ctkDICOMSeriesItemWidget *ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientSeriesWidgetByUIDs(QString studyInstanceUID, + QString seriesInstanceUID) +{ + ctkDICOMStudyItemWidget *studyItemWidget = this->getCurrentPatientStudyWidgetByUIDs(studyInstanceUID); + if (!studyItemWidget) + { + return nullptr; + } + + QList seriesItemWidgetsList = studyItemWidget->seriesItemWidgetsList(); + foreach (ctkDICOMSeriesItemWidget *seriesItemWidget, seriesItemWidgetsList) + { + if (!seriesItemWidget || seriesItemWidget->seriesInstanceUID() != seriesInstanceUID) + { + continue; + } + + return seriesItemWidget; + } + + return nullptr; +} + //---------------------------------------------------------------------------- // ctkDICOMVisualBrowserWidget methods @@ -1251,6 +1378,7 @@ ctkDICOMVisualBrowserWidget::ctkDICOMVisualBrowserWidget(QWidget* parentWidget) { Q_D(ctkDICOMVisualBrowserWidget); d->init(); + this->setDCMTKLogLevel(logger.logLevel()); } //---------------------------------------------------------------------------- @@ -1472,6 +1600,47 @@ ctkCollapsibleGroupBox *ctkDICOMVisualBrowserWidget::serverSettingsGroupBox() return d->ServersSettingsCollapsibleGroupBox; } +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel &level) +{ + OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; + if (level == ctkErrorLogLevel::LogLevel::Fatal) + { + dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Critical || + level == ctkErrorLogLevel::LogLevel::Error) + { + dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Warning) + { + dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Info) + { + dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Debug) + { + dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; + } + else if (level == ctkErrorLogLevel::LogLevel::Trace || + level == ctkErrorLogLevel::LogLevel::Status) + { + dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; + } + + OFLog::configure(dcmtkLogLevel); +} + +//------------------------------------------------------------------------------ +dcmtk::log4cplus::LogLevel ctkDICOMVisualBrowserWidget::DCMTKLogLevel() const +{ + OFLogger logger = OFLog::getLogger("CTK"); + return logger.getChainedLogLevel(); +} + //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::setFilteringPatientID(const QString& filteringPatientID) { @@ -1563,7 +1732,7 @@ QStringList ctkDICOMVisualBrowserWidget::filteringModalities() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient) +void ctkDICOMVisualBrowserWidget::setNumberOfStudiesPerPatient(const int &numberOfStudiesPerPatient) { Q_D(ctkDICOMVisualBrowserWidget); d->NumberOfStudiesPerPatient = numberOfStudiesPerPatient; @@ -1577,35 +1746,21 @@ int ctkDICOMVisualBrowserWidget::numberOfStudiesPerPatient() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setNumberOfSeriesPerRow(int numberOfSeriesPerRow) -{ - Q_D(ctkDICOMVisualBrowserWidget); - d->NumberOfSeriesPerRow = numberOfSeriesPerRow; -} - -//------------------------------------------------------------------------------ -int ctkDICOMVisualBrowserWidget::numberOfSeriesPerRow() const -{ - Q_D(const ctkDICOMVisualBrowserWidget); - return d->NumberOfSeriesPerRow; -} - -//------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setMinimumThumbnailSize(int minimumThumbnailSize) +void ctkDICOMVisualBrowserWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) { Q_D(ctkDICOMVisualBrowserWidget); - d->MinimumThumbnailSize = minimumThumbnailSize; + d->ThumbnailSize = thumbnailSize; } //------------------------------------------------------------------------------ -int ctkDICOMVisualBrowserWidget::minimumThumbnailSize() const +ctkDICOMStudyItemWidget::ThumbnailSizeOption ctkDICOMVisualBrowserWidget::thumbnailSize() const { Q_D(const ctkDICOMVisualBrowserWidget); - return d->MinimumThumbnailSize; + return d->ThumbnailSize; } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setSendActionVisible(bool visible) +void ctkDICOMVisualBrowserWidget::setSendActionVisible(const bool &visible) { Q_D(ctkDICOMVisualBrowserWidget); d->SendActionVisible = visible; @@ -1620,7 +1775,7 @@ bool ctkDICOMVisualBrowserWidget::isSendActionVisible() const //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setDeleteActionVisible(bool visible) +void ctkDICOMVisualBrowserWidget::setDeleteActionVisible(const bool &visible) { Q_D(ctkDICOMVisualBrowserWidget); d->DeleteActionVisible = visible; @@ -1653,8 +1808,7 @@ void ctkDICOMVisualBrowserWidget::addPatientItemWidget(const QString& patientIte patientItemWidget->setFilteringDate(d->FilteringDate); patientItemWidget->setFilteringSeriesDescription(d->FilteringSeriesDescription); patientItemWidget->setFilteringModalities(d->FilteringModalities); - patientItemWidget->setMinimumThumbnailSize(d->MinimumThumbnailSize); - patientItemWidget->setNumberOfSeriesPerRow(d->NumberOfSeriesPerRow); + patientItemWidget->setThumbnailSize(d->ThumbnailSize); patientItemWidget->setNumberOfStudiesPerPatient(d->NumberOfStudiesPerPatient); patientItemWidget->setDicomDatabase(d->DicomDatabase); patientItemWidget->setScheduler(d->Scheduler); @@ -1816,9 +1970,17 @@ void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) bool success = true; if (!QDir(absDirectory).exists() - || (!QDir(absDirectory).isEmpty() && !QFile(databaseFileName).exists())) + || (!ctk::isDirEmpty(QDir(absDirectory)) && !QFile(databaseFileName).exists())) { logger.warn(tr("Database folder does not contain ctkDICOM.sql file: ") + absDirectory + "\n"); + d->DatabaseDirectoryProblemFrame->show(); + d->DatabaseDirectoryProblemLabel->setText( + //: %1 is the folder path + tr("No valid DICOM database found in folder %1.").arg(absDirectory) + ); + d->UpdateDatabaseButton->hide(); + d->CreateNewDatabaseButton->show(); + d->SelectDatabaseDirectoryButton->show(); success = false; } @@ -1832,12 +1994,21 @@ void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) } catch (std::exception e) { + Q_UNUSED(e); databaseOpenSuccess = false; } if (!databaseOpenSuccess || d->DicomDatabase->schemaVersionLoaded().isEmpty()) { logger.warn(tr("Database error: %1 \n").arg(d->DicomDatabase->lastError())); d->DicomDatabase->closeDatabase(); + d->DatabaseDirectoryProblemFrame->show(); + d->DatabaseDirectoryProblemLabel->setText( + //: %1 is the folder path + tr("No valid DICOM database found in folder %1.").arg(absDirectory) + ); + d->UpdateDatabaseButton->hide(); + d->CreateNewDatabaseButton->show(); + d->SelectDatabaseDirectoryButton->show(); success = false; } } @@ -1849,10 +2020,23 @@ void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) logger.warn(tr("Database version mismatch: version of selected database = %1, version required = %2 \n") .arg(d->DicomDatabase->schemaVersionLoaded()).arg(d->DicomDatabase->schemaVersion())); d->DicomDatabase->closeDatabase(); + d->DatabaseDirectoryProblemFrame->show(); + d->DatabaseDirectoryProblemLabel->setText( + //: %1 is the folder path + tr("Incompatible DICOM database version found in folder %1.").arg(absDirectory) + ); + d->UpdateDatabaseButton->show(); + d->CreateNewDatabaseButton->show(); + d->SelectDatabaseDirectoryButton->show(); success = false; } } + if (success) + { + d->DatabaseDirectoryProblemFrame->hide(); + } + // Save new database directory in this object and in application settings. d->DatabaseDirectory = absDirectory; if (!d->DatabaseDirectorySettingsKey.isEmpty()) @@ -1862,6 +2046,8 @@ void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) settings.sync(); } + this->onShowPatients(); + // pass DICOM database instance to Import widget emit databaseDirectoryChanged(absDirectory); } @@ -1969,6 +2155,123 @@ void ctkDICOMVisualBrowserWidget::onIndexingComplete(int patientsAdded, int stud d->createPatients(); } +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::selectDatabaseDirectory() +{ + Q_D(const ctkDICOMVisualBrowserWidget); + d->DatabaseDirectoryProblemFrame->hide(); + ctkDirectoryButton directoryButton(this); + directoryButton.setDirectory(d->DatabaseDirectory); + QString dir = directoryButton.browse(); + this->setDatabaseDirectory(dir); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::createNewDatabaseDirectory() +{ + Q_D(ctkDICOMVisualBrowserWidget); + + // Use the current database folder as a basis for the new name + QString baseFolder = this->databaseDirectory(); + if (baseFolder.isEmpty()) + { + baseFolder = d->DefaultDatabaseDirectory; + } + else + { + // only use existing folder name as a basis if it is empty or + // a valid database + if (!ctk::isDirEmpty(QDir(baseFolder))) + { + QString databaseFileName = QDir(baseFolder).filePath("ctkDICOM.sql"); + if (!QFile(databaseFileName).exists()) + { + // current folder is a non-empty and not a DICOM database folder + // create a subfolder for the new DICOM database based on the name + // of default database path + QFileInfo defaultFolderInfo(d->DefaultDatabaseDirectory); + QString defaultSubfolderName = defaultFolderInfo.fileName(); + if (defaultSubfolderName.isEmpty()) + { + defaultSubfolderName = defaultFolderInfo.dir().dirName(); + } + baseFolder += "/" + defaultSubfolderName; + } + } + } + // Remove existing numerical suffix + QString separator = "_"; + bool isSuffixValid = false; + QString suffixStr = baseFolder.split(separator).last(); + int suffixStart = suffixStr.toInt(&isSuffixValid); + if (isSuffixValid) + { + QStringList baseFolderComponents = baseFolder.split(separator); + baseFolderComponents.removeLast(); + baseFolder = baseFolderComponents.join(separator); + } + // Try folder names, starting with the current one, + // incrementing the original numerical suffix. + int attemptsCount = 100; + for (int attempt=0; attemptDatabaseDirectoryProblemFrame->show(); + d->DatabaseDirectoryProblemLabel->setText( + //: %1 is the folder path + tr("Failed to create new database in folder %1.").arg(QDir(baseFolder).absolutePath()) + ); + d->UpdateDatabaseButton->hide(); + d->CreateNewDatabaseButton->show(); + d->SelectDatabaseDirectoryButton->show(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::updateDatabase() +{ + Q_D(ctkDICOMVisualBrowserWidget); + d->DatabaseDirectoryProblemFrame->hide(); + d->showUpdateSchemaDialog(); + QString dir = this->databaseDirectory(); + // open DICOM database on the directory + QString databaseFileName = QDir(dir).filePath("ctkDICOM.sql"); + try + { + d->DicomDatabase->openDatabase(databaseFileName); + } + catch (const std::exception& e) + { + Q_UNUSED(e); + std::cerr << "Database error: " << qPrintable(d->DicomDatabase->lastError()) << "\n"; + d->DicomDatabase->closeDatabase(); + return; + } + d->DicomDatabase->updateSchema(); + // Update GUI + this->setDatabaseDirectory(dir); +} + //------------------------------------------------------------------------------ QStringList ctkDICOMVisualBrowserWidget::fileListForCurrentSelection(ctkDICOMModel::IndexType level, QList selectedWidgets) @@ -2198,7 +2501,46 @@ void ctkDICOMVisualBrowserWidget::onFilteringDateComboBoxChanged(int index) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onQueryPatient() +void ctkDICOMVisualBrowserWidget::onShowPatients() +{ + Q_D(ctkDICOMVisualBrowserWidget); + if (d->IsGUIUpdating) + { + return; + } + + if (!d->DicomDatabase) + { + logger.error("onQueryPatient failed, no DICOM database has been set. \n"); + return; + } + + // Stop any fetching task. + this->onStop(); + + // Clear the UI. + d->removeAllPatientItemWidgets(); + + if (d->DicomDatabase->patients().count() == 0) + { + d->setBackgroundColorToFilterWidgets(true); + + d->WarningPushButton->setText(tr("No patients have been found in the local database.")); + d->WarningPushButton->show(); + d->patientsTabMenuToolButton->hide(); + return; + } + else + { + d->WarningPushButton->hide(); + } + + d->createPatients(); + d->updateFiltersWarnings(); +} + +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onQueryPatients() { Q_D(ctkDICOMVisualBrowserWidget); if (d->IsGUIUpdating) @@ -2226,17 +2568,10 @@ void ctkDICOMVisualBrowserWidget::onQueryPatient() d->FilteringDate == ctkDICOMPatientItemWidget::DateType::Any && d->FilteringModalities.contains("Any"); - if (d->DicomDatabase && - d->DicomDatabase->patients().count() == 0 && + if (d->DicomDatabase->patients().count() == 0 && filtersEmpty) { - QString background = "QWidget { background-color: yellow }"; - d->FilteringPatientIDSearchBox->setStyleSheet(d->FilteringPatientIDSearchBox->styleSheet() + background); - d->FilteringPatientNameSearchBox->setStyleSheet(d->FilteringPatientNameSearchBox->styleSheet() + background); - d->FilteringDateComboBox->setStyleSheet(d->FilteringDateComboBox->styleSheet() + background); - d->FilteringStudyDescriptionSearchBox->setStyleSheet(d->FilteringStudyDescriptionSearchBox->styleSheet() + background); - d->FilteringSeriesDescriptionSearchBox->setStyleSheet(d->FilteringSeriesDescriptionSearchBox->styleSheet() + background); - d->FilteringModalityCheckableComboBox->setStyleSheet(d->FilteringModalityCheckableComboBox->styleSheet() + background); + d->setBackgroundColorToFilterWidgets(true); d->WarningPushButton->setText(tr("No filters have been set and no patients have been found in the local database." "\nPlease set at least one filter to query the servers")); @@ -2324,19 +2659,31 @@ void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onTaskFailed(QString jobUID, QString jobType) +void ctkDICOMVisualBrowserWidget::onTaskFailed(QVariant data) { Q_D(ctkDICOMVisualBrowserWidget); - if (jobType == "ctkDICOMQueryJob") + ctkJobDetail td = data.value(); + + if (td.JobClass == "ctkDICOMQueryJob") { d->updateFiltersWarnings(); d->ProgressFrame->hide(); d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); } - d->WarningPushButton->setText(tr("%1 job failed to fetch the data." - "\nFor more information please open the error report console. \n").arg(jobUID)); - d->WarningPushButton->show(); + if (td.JobClass == "ctkDICOMRetrieveJob") + { + ctkDICOMSeriesItemWidget *seriesItemWidget = + d->getCurrentPatientSeriesWidgetByUIDs(td.StudyInstanceUID, td.SeriesInstanceUID); + if (seriesItemWidget) + { + seriesItemWidget->setRetrieveFailed(true); + } + + d->WarningPushButton->setText(tr("%1 job failed to fetch the data." + "\nFor more information please open the error report console. \n").arg(td.JobUID)); + d->WarningPushButton->show(); + } } //------------------------------------------------------------------------------ @@ -2379,7 +2726,7 @@ void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) QAction *metadataAction = new QAction(metadataString, patientMenu); patientMenu->addAction(metadataAction); - QString deleteString = tr("Delete patient"); + QString deleteString = tr("Delete patient from local database"); QAction *deleteAction = new QAction(deleteString, patientMenu); patientMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); @@ -2472,8 +2819,8 @@ void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) QAction *metadataAction = new QAction(metadataString, studyMenu); studyMenu->addAction(metadataAction); - QString deleteString = numberOfSelectedStudies == 1 ? tr("Delete study") : - tr("Delete %1 studies").arg(numberOfSelectedStudies); + QString deleteString = numberOfSelectedStudies == 1 ? tr("Delete study from local database") : + tr("Delete %1 studies from local database").arg(numberOfSelectedStudies); QAction *deleteAction = new QAction(deleteString, studyMenu); studyMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); @@ -2573,8 +2920,8 @@ void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) QAction *metadataAction = new QAction(metadataString, seriesMenu); seriesMenu->addAction(metadataAction); - QString deleteString = numberOfSelectedSeries == 1 ? tr("Delete series") : - tr("Delete %1 series").arg(numberOfSelectedSeries); + QString deleteString = numberOfSelectedSeries == 1 ? tr("Delete series from local database") : + tr("Delete %1 series from local database").arg(numberOfSelectedSeries); QAction *deleteAction = new QAction(deleteString, seriesMenu); seriesMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); @@ -2648,7 +2995,7 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() } patientMenu->addSeparator(); - QString deleteString = tr("Delete all Patients"); + QString deleteString = tr("Delete all Patients from local database"); QAction *deleteAction = new QAction(deleteString, patientMenu); deleteAction->setIcon(QIcon(":Icons/delete.svg")); patientMenu->addAction(deleteAction); @@ -2746,6 +3093,7 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids } destinationDir += sep; + // create the destination directory if necessary if (!QDir().exists(destinationDir)) { @@ -2763,7 +3111,23 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids } } + // show progress + if (d->ExportProgress == 0) + { + d->ExportProgress = new QProgressDialog(tr("DICOM Export"), tr("Close"), 0, 100, this, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + d->ExportProgress->setWindowModality(Qt::ApplicationModal); + d->ExportProgress->setMinimumDuration(0); + } + QLabel *exportLabel = new QLabel( + //: %1 is the series number + tr("Exporting series %1").arg(seriesNumber) + ); + d->ExportProgress->setLabel(exportLabel); + d->ExportProgress->setValue(0); + int fileNumber = 0; + int numFiles = filesForSeries.size(); + d->ExportProgress->setMaximum(numFiles); foreach (const QString& filePath, filesForSeries) { // File name example: my/destination/folder/000001.dcm @@ -2771,11 +3135,12 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids if (!QFile::exists(filePath)) { + d->ExportProgress->setValue(numFiles); //: %1 is the file path QString errorString = tr("Export source file not found:\n\n%1" "\n\nHalting export.\n\nError may be fixed via Repair.") .arg(filePath); - ctkMessageBox copyErrorMessageBox(this); + ctkMessageBox copyErrorMessageBox; copyErrorMessageBox.setText(errorString); copyErrorMessageBox.setIcon(QMessageBox::Warning); copyErrorMessageBox.exec(); @@ -2783,6 +3148,7 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids } if (QFile::exists(destinationFileName)) { + d->ExportProgress->setValue(numFiles); //: %1 is the destination file name QString errorString = tr("Export destination file already exists:\n\n%1" "\n\nHalting export.") @@ -2797,6 +3163,7 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids bool copyResult = QFile::copy(filePath, destinationFileName); if (!copyResult) { + d->ExportProgress->setValue(numFiles); //: %1 and %2 refers to source and destination file paths QString errorString = tr("Failed to copy\n\n%1\n\nto\n\n%2" "\n\nHalting export.") @@ -2810,8 +3177,10 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids } fileNumber++; + d->ExportProgress->setValue(fileNumber); } - } + d->ExportProgress->setValue(numFiles); + } } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 04664b998a..08d7ec944d 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -31,10 +31,14 @@ #include // CTK includes -#include +#include "ctkDICOMPatientItemWidget.h" +#include "ctkDICOMStudyItemWidget.h" #include "ctkDICOMModel.h" #include "ctkErrorLogLevel.h" +// DCMTK includes +#include + class ctkCollapsibleGroupBox; class ctkDICOMVisualBrowserWidgetPrivate; class ctkDICOMDatabase; @@ -75,8 +79,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget Q_PROPERTY(QString filteringPatientID READ filteringPatientID WRITE setFilteringPatientID); Q_PROPERTY(QString filteringPatientName READ filteringPatientName WRITE setFilteringPatientName); Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); - Q_PROPERTY(int numberOfSeriesPerRow READ numberOfSeriesPerRow WRITE setNumberOfSeriesPerRow); - Q_PROPERTY(int minimumThumbnailSize READ minimumThumbnailSize WRITE setMinimumThumbnailSize); + Q_PROPERTY(ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize READ thumbnailSize WRITE setThumbnailSize); Q_PROPERTY(bool sendActionVisible READ isSendActionVisible WRITE setSendActionVisible) Q_PROPERTY(bool deleteActionVisible READ isDeleteActionVisible WRITE setDeleteActionVisible) Q_PROPERTY(QString storageAETitle READ storageAETitle WRITE setStorageAETitle); @@ -154,6 +157,10 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget Q_INVOKABLE ctkDICOMServerNodeWidget2* serverSettingsWidget(); Q_INVOKABLE ctkCollapsibleGroupBox* serverSettingsGroupBox(); + /// Log level for dcmtk. Default: Error. + Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); + Q_INVOKABLE dcmtk::log4cplus::LogLevel DCMTKLogLevel() const; + /// Query Filters /// Empty by default void setFilteringPatientID(const QString& filteringPatientID); @@ -183,27 +190,22 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// Number of non collapsed studies per patient /// 2 by default - void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); + void setNumberOfStudiesPerPatient(const int &numberOfStudiesPerPatient); int numberOfStudiesPerPatient() const; - /// Number of series displayed per row - /// 6 by default - void setNumberOfSeriesPerRow(int numberOfSeriesPerRow); - int numberOfSeriesPerRow() const; - - /// Minimum thumbnail size in pixel - /// 300 by default - void setMinimumThumbnailSize(int minimumThumbnailSize); - int minimumThumbnailSize() const; + /// Set the thumbnail size: small, medium, large + /// medium by default + void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); + ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; /// Set if send action on right click context menu is available /// false by default - void setSendActionVisible(bool visible); + void setSendActionVisible(const bool &visible); bool isSendActionVisible() const; /// Set if cancel action on right click context menu is available /// true by default - void setDeleteActionVisible(bool visible); + void setDeleteActionVisible(const bool &visible); bool isDeleteActionVisible() const; /// Add/Remove Patient item widget @@ -297,15 +299,26 @@ public Q_SLOTS: void onIndexingProgressDetail(const QString&); void onIndexingComplete(int patientsAdded, int studiesAdded, int seriesAdded, int imagesAdded); + /// Show pop-up window for the user to select database directory + void selectDatabaseDirectory(); + + /// Create new database directory. + /// Current database directory used as a basis. + void createNewDatabaseDirectory(); + + /// Update database in-place to required schema version + void updateDatabase(); + void onFilteringPatientIDChanged(); void onFilteringPatientNameChanged(); void onFilteringStudyDescriptionChanged(); void onFilteringSeriesDescriptionChanged(); void onFilteringModalityCheckableComboBoxChanged(); void onFilteringDateComboBoxChanged(int); - void onQueryPatient(); + void onQueryPatients(); + void onShowPatients(); void updateGUIFromScheduler(QVariant); - void onTaskFailed(QString, QString); + void onTaskFailed(QVariant); void onPatientItemChanged(int); void onClose(); void onLoad(); diff --git a/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui b/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui index abb2bf3810..3e0a3d2d3a 100644 --- a/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui +++ b/Libs/Widgets/Resources/UI/ctkThumbnailLabel.ui @@ -119,14 +119,30 @@ - - - Qt::AlignCenter + + + + 0 + 0 + + + + false + + + true + + + ctkPushButton + QPushButton +
ctkPushButton.h
+
+
diff --git a/Libs/Widgets/ctkDirectoryButton.cpp b/Libs/Widgets/ctkDirectoryButton.cpp index 85ebc1135b..672b0c3db9 100644 --- a/Libs/Widgets/ctkDirectoryButton.cpp +++ b/Libs/Widgets/ctkDirectoryButton.cpp @@ -250,7 +250,7 @@ void ctkDirectoryButton::setAcceptMode(QFileDialog::AcceptMode mode) } //----------------------------------------------------------------------------- -void ctkDirectoryButton::browse() +QString ctkDirectoryButton::browse() { // See https://bugreports.qt-project.org/browse/QTBUG-10244 class ExcludeReadOnlyFilterProxyModel : public QSortFilterProxyModel @@ -308,9 +308,10 @@ void ctkDirectoryButton::browse() // An empty directory means either that the user cancelled the dialog or the selected directory is readonly if (dir.isEmpty()) { - return; + return ""; } this->setDirectory(dir); + return dir; } //----------------------------------------------------------------------------- diff --git a/Libs/Widgets/ctkDirectoryButton.h b/Libs/Widgets/ctkDirectoryButton.h index 6d7dd58d09..2d92dfb36d 100644 --- a/Libs/Widgets/ctkDirectoryButton.h +++ b/Libs/Widgets/ctkDirectoryButton.h @@ -166,7 +166,7 @@ class CTK_WIDGETS_EXPORT ctkDirectoryButton: public QWidget public Q_SLOTS: /// browse() opens a pop up where the user can select a new directory for the /// button. browse() is automatically called when the button is clicked. - void browse(); + QString browse(); Q_SIGNALS: /// directoryChanged is emitted when the current directory changes. diff --git a/Libs/Widgets/ctkThumbnailLabel.cpp b/Libs/Widgets/ctkThumbnailLabel.cpp index 6a42223acd..a53e8dd4fe 100644 --- a/Libs/Widgets/ctkThumbnailLabel.cpp +++ b/Libs/Widgets/ctkThumbnailLabel.cpp @@ -97,23 +97,23 @@ void ctkThumbnailLabelPrivate::updateThumbnail() Q_Q(ctkThumbnailLabel); QSize size = q->size(); - if (this->TextLabel->isVisible()) + if (this->TextPushButton->isVisible()) { if (this->TextPosition & Qt::AlignTop) { - size.setHeight(size.height() - this->TextLabel->height()); + size.setHeight(size.height() - this->TextPushButton->height()); } else if (this->TextPosition & Qt::AlignBottom) { - size.setHeight(size.height() - this->TextLabel->height()); + size.setHeight(size.height() - this->TextPushButton->height()); } else if (this->TextPosition & Qt::AlignLeft) { - size.setWidth(size.width() - this->TextLabel->width()); + size.setWidth(size.width() - this->TextPushButton->width()); } else if (this->TextPosition & Qt::AlignRight) { - size.setWidth(size.width() - this->TextLabel->width()); + size.setWidth(size.width() - this->TextPushButton->width()); } } @@ -148,10 +148,10 @@ ctkThumbnailLabel::~ctkThumbnailLabel() } //---------------------------------------------------------------------------- -QLabel* ctkThumbnailLabel::textLabel() +ctkPushButton* ctkThumbnailLabel::textPushButton() { Q_D(ctkThumbnailLabel); - return d->TextLabel; + return d->TextPushButton; } //---------------------------------------------------------------------------- @@ -180,8 +180,8 @@ void ctkThumbnailLabel::setText(const QString &text) { Q_D(ctkThumbnailLabel); - d->TextLabel->setText(text); - d->TextLabel->setVisible(!text.isEmpty() && + d->TextPushButton->setText(text); + d->TextPushButton->setVisible(!text.isEmpty() && ! (d->TextPosition & Qt::AlignHCenter && d->TextPosition & Qt::AlignVCenter) ); } @@ -190,7 +190,7 @@ void ctkThumbnailLabel::setText(const QString &text) QString ctkThumbnailLabel::text()const { Q_D(const ctkThumbnailLabel); - return d->TextLabel->text(); + return d->TextPushButton->text(); } //---------------------------------------------------------------------------- @@ -201,7 +201,7 @@ void ctkThumbnailLabel::setTextPosition(const Qt::Alignment& position) int textIndex = -1; for (textIndex = 0; textIndex < this->layout()->count(); ++textIndex) { - if (this->layout()->itemAt(textIndex)->widget() == d->TextLabel) + if (this->layout()->itemAt(textIndex)->widget() == d->TextPushButton) { break; } @@ -239,11 +239,11 @@ void ctkThumbnailLabel::setTextPosition(const Qt::Alignment& position) } if (row == 1 && col == 1) { - d->TextLabel->setVisible(false); + d->TextPushButton->setVisible(false); } else { - gridLayout->addWidget(d->TextLabel,row, col); + gridLayout->addWidget(d->TextPushButton,row, col); } } @@ -349,10 +349,10 @@ QColor ctkThumbnailLabel::selectedColor()const QSize ctkThumbnailLabel::minimumSizeHint()const { Q_D(const ctkThumbnailLabel); - if (d->TextLabel->isVisibleTo(const_cast(this)) && - !d->TextLabel->text().isEmpty()) + if (d->TextPushButton->isVisibleTo(const_cast(this)) && + !d->TextPushButton->text().isEmpty()) { - return d->TextLabel->minimumSizeHint(); + return d->TextPushButton->minimumSizeHint(); } return QSize(); } diff --git a/Libs/Widgets/ctkThumbnailLabel.h b/Libs/Widgets/ctkThumbnailLabel.h index 418c13fcf4..584188c8c3 100644 --- a/Libs/Widgets/ctkThumbnailLabel.h +++ b/Libs/Widgets/ctkThumbnailLabel.h @@ -27,6 +27,7 @@ #include "ctkWidgetsExport.h" +class ctkPushButton; class ctkThumbnailLabelPrivate; class QFrame; @@ -70,7 +71,7 @@ class CTK_WIDGETS_EXPORT ctkThumbnailLabel : public QWidget explicit ctkThumbnailLabel(QWidget* parent=0); virtual ~ctkThumbnailLabel(); - Q_INVOKABLE QLabel* textLabel(); + Q_INVOKABLE ctkPushButton* textPushButton(); Q_INVOKABLE QFrame* pixmapFrame(); Q_INVOKABLE QLabel* pixmapLabel(); Q_INVOKABLE QProgressBar* operationProgressBar(); From bd1f8fb3cc7a3a571884264e6033b1d444f85cce Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Fri, 12 Jan 2024 11:38:11 +0100 Subject: [PATCH 04/73] BUG: Fix ctkDICOMSchedulerTest1 test --- Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index 19667143cf..67ef232b3b 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -151,7 +151,7 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { CHECK_INT(instances.count(), numberOfImages); CHECK_INT(files.count(), numberOfImages); - CHECK_INT(urls.count(), numberOfImages); + CHECK_INT(urls.count(), 0); return EXIT_SUCCESS; } From 7eb3a65f88cd549d7f6e154af2ce61bf2179e2a5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Fri, 12 Jan 2024 03:47:48 -0500 Subject: [PATCH 05/73] COMP: Fix -Wcatch-value in ctkDICOMVisualBrowserWidget --- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index b5735efa3f..562ad44bff 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1992,7 +1992,7 @@ void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) d->DicomDatabase->openDatabase(databaseFileName); databaseOpenSuccess = d->DicomDatabase->isOpen(); } - catch (std::exception e) + catch (const std::exception& e) { Q_UNUSED(e); databaseOpenSuccess = false; From 104e2eddc72414860c9bccfc1c44418b8fe44994 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Fri, 12 Jan 2024 03:50:11 -0500 Subject: [PATCH 06/73] COMP: Fix deprecated use of QFileDialog::DirectoryOnly in ctkDICOMVisualBrowserWidget Apply fix similar to 724c1549 ("COMP: Fix deprecated use of QFileDialog::DirectoryOnly in ctkFileDialogTest1", 2023-06-14) --- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 562ad44bff..214dd5ae8d 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -3031,7 +3031,8 @@ void ctkDICOMVisualBrowserWidget::exportSelectedItems(ctkDICOMModel::IndexType l ctkFileDialog* directoryDialog = new ctkFileDialog(); directoryDialog->setOption(QFileDialog::ShowDirsOnly); - directoryDialog->setFileMode(QFileDialog::DirectoryOnly); + directoryDialog->setFileMode(QFileDialog::Directory); + directoryDialog->setOption(QFileDialog::ShowDirsOnly); bool res = directoryDialog->exec(); if (!res) { From d103df2a5770732bfbb0c05f0986dd2d92afce69 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Fri, 12 Jan 2024 03:54:47 -0500 Subject: [PATCH 07/73] COMP: Fix -Wsign-promo in ctkDICOMVisualBrowserWidget Apply fix similar to 757b21d5 ("COMP: Fix -Wsign-promo in Widgets & DICOMWidgets", 2023-04-14) --- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp | 4 ++-- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 4a0fdb8d1b..2f15174ce5 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -168,7 +168,7 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate { state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; } - return model->setData(index, state, Qt::CheckStateRole); + return model->setData(index, static_cast(state), Qt::CheckStateRole); } }; @@ -998,7 +998,7 @@ void ctkDICOMServerNodeWidget2::saveSettings() } } - settings.setValue("DICOM/StorageEnabled", QString::number(this->storageListenerEnabled())); + settings.setValue("DICOM/StorageEnabled", this->storageListenerEnabled()); settings.setValue("DICOM/StorageAETitle", this->storageAETitle()); settings.setValue("DICOM/StoragePort", this->storagePort()); settings.sync(); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 214dd5ae8d..1769f613c2 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -370,8 +370,8 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() // Initialize directoryMode widget QFormLayout *layout = new QFormLayout; QComboBox* importDirectoryModeComboBox = new QComboBox(); - importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Add Link"), ctkDICOMVisualBrowserWidget::ImportDirectoryAddLink); - importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Copy"), ctkDICOMVisualBrowserWidget::ImportDirectoryCopy); + importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Add Link"), static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryAddLink)); + importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Copy"), static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryCopy)); importDirectoryModeComboBox->setToolTip( ctkDICOMVisualBrowserWidget::tr("Indicate if the files should be copied to the local database" " directory or if only links should be created ?")); @@ -382,7 +382,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() // Default values importDirectoryModeComboBox->setCurrentIndex( - importDirectoryModeComboBox->findData(q->importDirectoryMode())); + importDirectoryModeComboBox->findData(static_cast(q->importDirectoryMode()))); //Initialize import widget this->ImportDialog = new ctkFileDialog(); @@ -1947,7 +1947,7 @@ void ctkDICOMVisualBrowserWidget::setImportDirectoryMode(ImportDirectoryMode mod return; // Native dialog does not support modifying or getting widget elements. } QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild(); - comboBox->setCurrentIndex(comboBox->findData(mode)); + comboBox->setCurrentIndex(comboBox->findData(static_cast(mode))); } //------------------------------------------------------------------------------ From 610ac8fe76bf5d39dce24873a490fc00e905871f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Fri, 12 Jan 2024 04:24:32 -0500 Subject: [PATCH 08/73] BUG: Fix -Wint-in-bool-context in ctkDICOMPatientItemWidget This commit addresses the following warning: /path/to/CTK/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp:791:35: warning: enum constant in boolean context [-Wint-in-bool-context] 791 | (Qt::ControlModifier || Qt::ShiftModifier)) | ^~~~~~~~~~~~~ --- Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index 91e08c2952..f2b1cfff1f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -787,8 +787,7 @@ void ctkDICOMPatientItemWidget::onSeriesItemClicked() return; } - if (QApplication::keyboardModifiers() && - (Qt::ControlModifier || Qt::ShiftModifier)) + if (QApplication::keyboardModifiers() & (Qt::ControlModifier | Qt::ShiftModifier)) { return; } From 25c951819174cd32dcdea3697c9ad9503c7b4555 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Fri, 12 Jan 2024 04:59:55 -0500 Subject: [PATCH 09/73] COMP: Fix compilation of tests to account for API changes --- .../Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp | 9 +++------ .../Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp | 6 +++--- .../Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp | 9 +++------ .../Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 9 +++------ 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp index 417c650a2a..ca7924b7c1 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp @@ -45,8 +45,7 @@ int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) CHECK_QSTRING(widget.filteringSeriesDescription(), ""); CHECK_INT(widget.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); CHECK_INT(widget.numberOfStudiesPerPatient(), 2); - CHECK_INT(widget.numberOfSeriesPerRow(), 6); - CHECK_INT(widget.minimumThumbnailSize(), 300); + CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); // Test setting and getting widget.setPatientItem("1"); @@ -61,10 +60,8 @@ int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) CHECK_INT(widget.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); widget.setNumberOfStudiesPerPatient(6); CHECK_INT(widget.numberOfStudiesPerPatient(), 6); - widget.setNumberOfSeriesPerRow(12); - CHECK_INT(widget.numberOfSeriesPerRow(), 12); - widget.setMinimumThumbnailSize(150); - CHECK_INT(widget.minimumThumbnailSize(), 150); + widget.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); + CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); if (argc <= 2 || QString(argv[argc - 1]) != "-I") { diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp index 725487b1dc..c6ef3c8928 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp @@ -51,7 +51,7 @@ int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) CHECK_BOOL(widget.isCloud(), false); CHECK_BOOL(widget.IsLoaded(), false); CHECK_BOOL(widget.IsVisible(), false); - CHECK_INT(widget.thumbnailSize(), 300); + CHECK_INT(widget.thumbnailSizePixel(), 200); // Test setting and getting widget.setSeriesItem("1"); @@ -72,8 +72,8 @@ int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) CHECK_BOOL(widget.stopJobs(), true); widget.setRaiseJobsPriority(true); CHECK_BOOL(widget.raiseJobsPriority(), true); - widget.setThumbnailSize(150); - CHECK_INT(widget.thumbnailSize(), 150); + widget.setThumbnailSizePixel(100); + CHECK_INT(widget.thumbnailSizePixel(), 100); if (argc <= 2 || QString(argv[argc - 1]) != "-I") { diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp index 31f9b2c14b..1918116d8f 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp @@ -47,8 +47,7 @@ int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) CHECK_QSTRING(widget.filteringSeriesDescription(), ""); CHECK_BOOL(widget.collapsed(), false) CHECK_BOOL(widget.selection(), false) - CHECK_INT(widget.numberOfSeriesPerRow(), 6); - CHECK_INT(widget.thumbnailSize(), 300); + CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); // Test setting and getting widget.setStudyItem("1"); @@ -67,10 +66,8 @@ int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) CHECK_BOOL(widget.collapsed(), true); widget.setSelection(true); CHECK_BOOL(widget.selection(), true); - widget.setNumberOfSeriesPerRow(12); - CHECK_INT(widget.numberOfSeriesPerRow(), 12); - widget.setThumbnailSize(150); - CHECK_INT(widget.thumbnailSize(), 150); + widget.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); + CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); if (argc <= 2 || QString(argv[argc - 1]) != "-I") { diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index 50c30fcf71..09b2fa5176 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -55,8 +55,7 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) CHECK_QSTRING(browser.filteringModalities()[0], "Any"); CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); CHECK_INT(browser.numberOfStudiesPerPatient(), 2); - CHECK_INT(browser.numberOfSeriesPerRow(), 6); - CHECK_INT(browser.minimumThumbnailSize(), 300); + CHECK_INT(browser.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); CHECK_BOOL(browser.isSendActionVisible(), false); CHECK_BOOL(browser.isDeleteActionVisible(), true); @@ -133,10 +132,8 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); browser.setNumberOfStudiesPerPatient(6); CHECK_INT(browser.numberOfStudiesPerPatient(), 6); - browser.setNumberOfSeriesPerRow(12); - CHECK_INT(browser.numberOfSeriesPerRow(), 12); - browser.setMinimumThumbnailSize(150); - CHECK_INT(browser.minimumThumbnailSize(), 150); + browser.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); + CHECK_INT(browser.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); browser.setSendActionVisible(true); CHECK_BOOL(browser.isSendActionVisible(), true); browser.setDeleteActionVisible(false); From 5befcd111554e1b11eb438be76d90ec5f36da85a Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Tue, 16 Jan 2024 15:17:03 +0100 Subject: [PATCH 10/73] BUG: Fix logging --- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 8 ++-- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 5 ++- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 8 ++-- Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 6 ++- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 8 ++-- Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 6 ++- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 8 ++-- .../DICOM/Core/ctkDICOMStorageListenerJob.cpp | 2 +- .../Core/ctkDICOMStorageListenerWorker.cpp | 6 ++- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 42 ------------------- .../Widgets/ctkDICOMVisualBrowserWidget.h | 4 -- 11 files changed, 32 insertions(+), 71 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index ccf76d705a..cded03093d 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -51,7 +51,7 @@ QString ctkDICOMInserterJob::loggerReport(const QString &status) const case ctkDICOMJob::DICOMLevels::Patients: return QString("ctkDICOMInserterJob: insert job at patients level %1.\n" "JobUID: %2\n" - "PatientID: %3") + "PatientID: %3\n") .arg(status) .arg(this->jobUID()) .arg(this->patientID()); @@ -59,7 +59,7 @@ QString ctkDICOMInserterJob::loggerReport(const QString &status) const return QString("ctkDICOMInserterJob: insert job at studies level %1.\n" "JobUID: %2\n" "PatientID: %3\n" - "StudyInstanceUID: %4") + "StudyInstanceUID: %4\n") .arg(status) .arg(this->jobUID()) .arg(this->patientID()) @@ -69,7 +69,7 @@ QString ctkDICOMInserterJob::loggerReport(const QString &status) const "JobUID: %2\n" "PatientID: %3\n" "StudyInstanceUID: %4\n" - "SeriesInstanceUID: %5") + "SeriesInstanceUID: %5\n") .arg(status) .arg(this->jobUID()) .arg(this->patientID()) @@ -81,7 +81,7 @@ QString ctkDICOMInserterJob::loggerReport(const QString &status) const "PatientID: %3\n" "StudyInstanceUID: %4\n" "SeriesInstanceUID: %5 \n" - "SOPInstanceUID: %6.") + "SOPInstanceUID: %6\n") .arg(status) .arg(this->jobUID()) .arg(this->patientID()) diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index c86345e657..768e299773 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -98,8 +98,9 @@ void ctkDICOMInserterWorker::run() inserterJob->setStatus(ctkAbstractJob::JobStatus::Running); emit inserterJob->started(); - logger.debug("ctkDICOMInserterWorker : running job on thread id " + - QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + logger.debug(QString("ctkDICOMInserterWorker : running job %1 in thread %2.\n") + .arg(inserterJob->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); QList> jobResponseSets = inserterJob->jobResponseSetsShared(); d->Inserter->addJobResponseSets(jobResponseSets); diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index b233a2db96..3070c337c8 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -137,7 +137,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const case ctkDICOMJob::DICOMLevels::Patients: return QString("ctkDICOMQueryJob: query job at patients level %1.\n" "JobUID: %2\n" - "Server: %3") + "Server: %3\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()); @@ -145,7 +145,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const return QString("ctkDICOMQueryJob: query job at studies level %1.\n" "JobUID: %2\n" "Server: %3\n" - "PatientID: %4") + "PatientID: %4\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) @@ -155,7 +155,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const "JobUID: %2\n" "Server: %3\n" "PatientID: %4\n" - "StudyInstanceUID: %5") + "StudyInstanceUID: %5\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) @@ -167,7 +167,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const "Server: %3\n" "PatientID: %4\n" "StudyInstanceUID: %5\n" - "SeriesInstanceUID: %6") + "SeriesInstanceUID: %6\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index d5ef21df36..0c5ef7b49f 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -146,8 +146,10 @@ void ctkDICOMQueryWorker::run() queryJob->setStatus(ctkAbstractJob::JobStatus::Running); emit queryJob->started(); - logger.debug("ctkDICOMQueryWorker : running job on thread id " + - QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + logger.debug(QString("ctkDICOMQueryWorker : running job %1 in thread %2.\n") + .arg(queryJob->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + switch(queryJob->dicomLevel()) { case ctkDICOMJob::DICOMLevels::Patients: diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 299a97b107..4ad74a7046 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -109,7 +109,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const return QString("ctkDICOMRetrieveJob: retrieve task at patients level %1.\n" "JobUID: %2\n" "Server: %3\n" - "PatientID: %4") + "PatientID: %4\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) @@ -119,7 +119,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const "JobUID: %2\n" "Server: %3\n" "PatientID: %4\n" - "StudyInstanceUID: %5") + "StudyInstanceUID: %5\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) @@ -131,7 +131,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const "Server: %3\n" "PatientID: %4\n" "StudyInstanceUID: %5\n" - "SeriesInstanceUID: %6") + "SeriesInstanceUID: %6\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) @@ -145,7 +145,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const "PatientID: %4\n" "StudyInstanceUID: %5\n" "SeriesInstanceUID: %6\n" - "SOPInstanceUID: %7") + "SOPInstanceUID: %7\n") .arg(status) .arg(this->jobUID()) .arg(this->server()->connectionName()) diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index c391dd49be..f0c8708708 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -159,8 +159,10 @@ void ctkDICOMRetrieveWorker::run() retrieveJob->setStatus(ctkAbstractJob::JobStatus::Running); emit retrieveJob->started(); - logger.debug("ctkDICOMRetrieveWorker : running job on thread id " + - QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + logger.debug(QString("ctkDICOMRetrieveWorker : running job %1 in thread %2.\n") + .arg(retrieveJob->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + switch (server->retrieveProtocol()) { case ctkDICOMServer::CGET: diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 0cdc802a0e..a011efeae6 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -47,7 +47,7 @@ static ctkLogger logger ( "org.commontk.dicom.DICOMJobPool" ); ctkDICOMSchedulerPrivate::ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj) : q_ptr(&obj) { - ctk::setDICOMLogLevel(ctkErrorLogLevel::Info); + ctk::setDICOMLogLevel(ctkErrorLogLevel::Warning); this->DicomDatabase = nullptr; this->ThreadPool = QSharedPointer (new QThreadPool()); @@ -96,7 +96,7 @@ void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) return; } - logger.debug(QString("ctkDICOMScheduler: creating job object %1 of type %2 in thread %3") + logger.debug(QString("ctkDICOMScheduler: creating job object %1 of type %2 in thread %3.\n") .arg(job->jobUID()) .arg(job->className()) .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); @@ -118,7 +118,7 @@ void ctkDICOMSchedulerPrivate::removeJob(QString jobUID) { Q_Q(ctkDICOMScheduler); - logger.debug(QString("ctkDICOMScheduler: deleting job object %1 in thread %2") + logger.debug(QString("ctkDICOMScheduler: deleting job object %1 in thread %2.\n") .arg(jobUID) .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); @@ -1139,7 +1139,7 @@ void ctkDICOMScheduler::onQueueJobsInThreadPool() continue; } - logger.debug(QString("ctkDICOMScheduler: creating worker for job %1 in thread %2") + logger.debug(QString("ctkDICOMScheduler: creating worker for job %1 in thread %2.\n") .arg(job->jobUID()) .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index de3753e2a5..f92bb906ad 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -112,7 +112,7 @@ int ctkDICOMStorageListenerJob::connectionTimeout() const QString ctkDICOMStorageListenerJob::loggerReport(const QString &status) const { return QString("ctkDICOMStorageListenerJob: listener job %1.\n" - "JobUID: %2") + "JobUID: %2\n") .arg(status) .arg(this->jobUID()); } diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index 3198b345dc..7a27ca98f8 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -146,8 +146,10 @@ void ctkDICOMStorageListenerWorker::run() storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Running); emit storageListenerJob->started(); - logger.debug("ctkDICOMStorageListenerWorker : running job on thread id " + - QString::number(reinterpret_cast(QThread::currentThreadId()), 16)); + logger.debug(QString("ctkDICOMStorageListenerWorker : running job %1 in thread %2.\n") + .arg(storageListenerJob->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + if (!d->StorageListener->listen()) { diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 1769f613c2..20bdb774f9 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1378,7 +1378,6 @@ ctkDICOMVisualBrowserWidget::ctkDICOMVisualBrowserWidget(QWidget* parentWidget) { Q_D(ctkDICOMVisualBrowserWidget); d->init(); - this->setDCMTKLogLevel(logger.logLevel()); } //---------------------------------------------------------------------------- @@ -1600,47 +1599,6 @@ ctkCollapsibleGroupBox *ctkDICOMVisualBrowserWidget::serverSettingsGroupBox() return d->ServersSettingsCollapsibleGroupBox; } -//------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel &level) -{ - OFLogger::LogLevel dcmtkLogLevel = OFLogger::OFF_LOG_LEVEL; - if (level == ctkErrorLogLevel::LogLevel::Fatal) - { - dcmtkLogLevel = OFLogger::FATAL_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Critical || - level == ctkErrorLogLevel::LogLevel::Error) - { - dcmtkLogLevel = OFLogger::ERROR_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Warning) - { - dcmtkLogLevel = OFLogger::WARN_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Info) - { - dcmtkLogLevel = OFLogger::INFO_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Debug) - { - dcmtkLogLevel = OFLogger::DEBUG_LOG_LEVEL; - } - else if (level == ctkErrorLogLevel::LogLevel::Trace || - level == ctkErrorLogLevel::LogLevel::Status) - { - dcmtkLogLevel = OFLogger::TRACE_LOG_LEVEL; - } - - OFLog::configure(dcmtkLogLevel); -} - -//------------------------------------------------------------------------------ -dcmtk::log4cplus::LogLevel ctkDICOMVisualBrowserWidget::DCMTKLogLevel() const -{ - OFLogger logger = OFLog::getLogger("CTK"); - return logger.getChainedLogLevel(); -} - //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::setFilteringPatientID(const QString& filteringPatientID) { diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 08d7ec944d..66aff3d5e8 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -157,10 +157,6 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget Q_INVOKABLE ctkDICOMServerNodeWidget2* serverSettingsWidget(); Q_INVOKABLE ctkCollapsibleGroupBox* serverSettingsGroupBox(); - /// Log level for dcmtk. Default: Error. - Q_INVOKABLE void setDCMTKLogLevel(const ctkErrorLogLevel::LogLevel& level); - Q_INVOKABLE dcmtk::log4cplus::LogLevel DCMTKLogLevel() const; - /// Query Filters /// Empty by default void setFilteringPatientID(const QString& filteringPatientID); From a1812700862a49e3539a286f3d0a2b031ed28b2f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 14:44:14 -0500 Subject: [PATCH 11/73] COMP: Update newly introduced ctor/dtor to use "default" keyword if empty --- Libs/Core/ctkAbstractScheduler.cpp | 4 +--- Libs/Core/ctkAbstractWorker.cpp | 4 +--- Libs/DICOM/Core/ctkDICOMEcho.cpp | 12 ++---------- Libs/DICOM/Core/ctkDICOMInserter.cpp | 11 ++--------- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 4 +--- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMJob.cpp | 4 +--- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 4 +--- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMServer.cpp | 11 ++--------- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 9 ++------- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp | 8 ++------ Libs/DICOM/Core/ctkDICOMWorker.cpp | 12 ++++-------- 16 files changed, 29 insertions(+), 94 deletions(-) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index cb5b8b27fb..f3b9e25b63 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -30,6 +30,4 @@ ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) } // -------------------------------------------------------------------------- -ctkAbstractScheduler::~ctkAbstractScheduler() -{ -} +ctkAbstractScheduler::~ctkAbstractScheduler() = default; diff --git a/Libs/Core/ctkAbstractWorker.cpp b/Libs/Core/ctkAbstractWorker.cpp index 2dc0396dae..c15f2ee090 100644 --- a/Libs/Core/ctkAbstractWorker.cpp +++ b/Libs/Core/ctkAbstractWorker.cpp @@ -34,9 +34,7 @@ ctkAbstractWorker::ctkAbstractWorker() } //---------------------------------------------------------------------------- -ctkAbstractWorker::~ctkAbstractWorker() -{ -} +ctkAbstractWorker::~ctkAbstractWorker() = default; //---------------------------------------------------------------------------- static void skipDelete(QObject* obj) diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp index 544289aa71..5bfd592d7f 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.cpp +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -47,7 +47,7 @@ class ctkDICOMEchoPrivate { public: ctkDICOMEchoPrivate(); - ~ctkDICOMEchoPrivate(); + ~ctkDICOMEchoPrivate() = default; QString ConnectionName; QString CallingAETitle; @@ -73,11 +73,6 @@ ctkDICOMEchoPrivate::ctkDICOMEchoPrivate() this->SCU.setConnectionTimeout(3); } -//------------------------------------------------------------------------------ -ctkDICOMEchoPrivate::~ctkDICOMEchoPrivate() -{ -} - //------------------------------------------------------------------------------ // ctkDICOMEcho methods @@ -92,11 +87,8 @@ ctkDICOMEcho::ctkDICOMEcho(QObject* parentObject) } //------------------------------------------------------------------------------ -ctkDICOMEcho::~ctkDICOMEcho() -{ -} +ctkDICOMEcho::~ctkDICOMEcho() = default; -/// Set methods for connectivity //------------------------------------------------------------------------------ void ctkDICOMEcho::setConnectionName(const QString& connectionName) { diff --git a/Libs/DICOM/Core/ctkDICOMInserter.cpp b/Libs/DICOM/Core/ctkDICOMInserter.cpp index f4267a98fc..4c5580ce3e 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserter.cpp @@ -37,7 +37,7 @@ class ctkDICOMInserterPrivate { public: ctkDICOMInserterPrivate(); - ~ctkDICOMInserterPrivate(); + ~ctkDICOMInserterPrivate() = default; bool Canceled; QString DatabaseFilename; @@ -54,11 +54,6 @@ ctkDICOMInserterPrivate::ctkDICOMInserterPrivate() this->Canceled = false; } -//------------------------------------------------------------------------------ -ctkDICOMInserterPrivate::~ctkDICOMInserterPrivate() -{ -} - //------------------------------------------------------------------------------ // ctkDICOMInserter methods @@ -70,9 +65,7 @@ ctkDICOMInserter::ctkDICOMInserter(QObject* parentObject) } //------------------------------------------------------------------------------ -ctkDICOMInserter::~ctkDICOMInserter() -{ -} +ctkDICOMInserter::~ctkDICOMInserter() = default; //------------------------------------------------------------------------------ void ctkDICOMInserter::setDatabaseFilename(const QString &databaseFilename) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index cded03093d..e3a1fe0a11 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -39,9 +39,7 @@ ctkDICOMInserterJob::ctkDICOMInserterJob() } //------------------------------------------------------------------------------ -ctkDICOMInserterJob::~ctkDICOMInserterJob() -{ -} +ctkDICOMInserterJob::~ctkDICOMInserterJob() = default; //------------------------------------------------------------------------------ QString ctkDICOMInserterJob::loggerReport(const QString &status) const diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 768e299773..2bbaee26be 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -45,9 +45,7 @@ ctkDICOMInserterWorkerPrivate::ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWor } //------------------------------------------------------------------------------ -ctkDICOMInserterWorkerPrivate::~ctkDICOMInserterWorkerPrivate() -{ -} +ctkDICOMInserterWorkerPrivate::~ctkDICOMInserterWorkerPrivate() = default; //------------------------------------------------------------------------------ // ctkDICOMInserterWorker methods @@ -65,9 +63,7 @@ ctkDICOMInserterWorker::ctkDICOMInserterWorker(ctkDICOMInserterWorkerPrivate* pi } //------------------------------------------------------------------------------ -ctkDICOMInserterWorker::~ctkDICOMInserterWorker() -{ -} +ctkDICOMInserterWorker::~ctkDICOMInserterWorker() = default; //---------------------------------------------------------------------------- void ctkDICOMInserterWorker::cancel() diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 6ca0adbdc2..de71e42e92 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -42,9 +42,7 @@ ctkDICOMJob::ctkDICOMJob() } //------------------------------------------------------------------------------ -ctkDICOMJob::~ctkDICOMJob() -{ -} +ctkDICOMJob::~ctkDICOMJob() = default; //------------------------------------------------------------------------------ void ctkDICOMJob::setDICOMLevel(const DICOMLevels& dicomLevel) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index f31124311c..118f3ee234 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -93,9 +93,7 @@ ctkDICOMJobResponseSet::ctkDICOMJobResponseSet(QObject* parent) } //------------------------------------------------------------------------------ -ctkDICOMJobResponseSet::~ctkDICOMJobResponseSet() -{ -} +ctkDICOMJobResponseSet::~ctkDICOMJobResponseSet() = default; //---------------------------------------------------------------------------- void ctkDICOMJobResponseSet::setFilePath(const QString &filePath) diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index 3070c337c8..0c9254dc89 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -41,9 +41,7 @@ ctkDICOMQueryJobPrivate::ctkDICOMQueryJobPrivate(ctkDICOMQueryJob* object) } //------------------------------------------------------------------------------ -ctkDICOMQueryJobPrivate::~ctkDICOMQueryJobPrivate() -{ -} +ctkDICOMQueryJobPrivate::~ctkDICOMQueryJobPrivate() = default; //------------------------------------------------------------------------------ // ctkDICOMQueryJob methods @@ -61,9 +59,7 @@ ctkDICOMQueryJob::ctkDICOMQueryJob(ctkDICOMQueryJobPrivate* pimpl) } //------------------------------------------------------------------------------ -ctkDICOMQueryJob::~ctkDICOMQueryJob() -{ -} +ctkDICOMQueryJob::~ctkDICOMQueryJob() = default; //---------------------------------------------------------------------------- void ctkDICOMQueryJob::setFilters(const QMap &filters) diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index 0c5ef7b49f..7eba428699 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -41,9 +41,7 @@ ctkDICOMQueryWorkerPrivate::ctkDICOMQueryWorkerPrivate(ctkDICOMQueryWorker* obje } //------------------------------------------------------------------------------ -ctkDICOMQueryWorkerPrivate::~ctkDICOMQueryWorkerPrivate() -{ -} +ctkDICOMQueryWorkerPrivate::~ctkDICOMQueryWorkerPrivate() = default; //------------------------------------------------------------------------------ void ctkDICOMQueryWorkerPrivate::setQueryParameters() @@ -89,9 +87,7 @@ ctkDICOMQueryWorker::ctkDICOMQueryWorker(ctkDICOMQueryWorkerPrivate* pimpl) } //------------------------------------------------------------------------------ -ctkDICOMQueryWorker::~ctkDICOMQueryWorker() -{ -} +ctkDICOMQueryWorker::~ctkDICOMQueryWorker() = default; //---------------------------------------------------------------------------- void ctkDICOMQueryWorker::cancel() diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 4ad74a7046..e7aa2a1404 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -40,9 +40,7 @@ ctkDICOMRetrieveJobPrivate::ctkDICOMRetrieveJobPrivate(ctkDICOMRetrieveJob* obje } //------------------------------------------------------------------------------ -ctkDICOMRetrieveJobPrivate::~ctkDICOMRetrieveJobPrivate() -{ -} +ctkDICOMRetrieveJobPrivate::~ctkDICOMRetrieveJobPrivate() = default; //------------------------------------------------------------------------------ // ctkDICOMRetrieveJob methods @@ -60,9 +58,7 @@ ctkDICOMRetrieveJob::ctkDICOMRetrieveJob(ctkDICOMRetrieveJobPrivate* pimpl) } //------------------------------------------------------------------------------ -ctkDICOMRetrieveJob::~ctkDICOMRetrieveJob() -{ -} +ctkDICOMRetrieveJob::~ctkDICOMRetrieveJob() = default; //---------------------------------------------------------------------------- static void skipDelete(QObject* obj) diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index 3bff55dab9..7232b3e62f 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -40,7 +40,7 @@ class ctkDICOMServerPrivate: public QObject public: ctkDICOMServerPrivate(ctkDICOMServer& obj); - ~ctkDICOMServerPrivate(); + ~ctkDICOMServerPrivate() = default; QString ConnectionName; bool QueryRetrieveEnabled; @@ -77,11 +77,6 @@ ctkDICOMServerPrivate::ctkDICOMServerPrivate(ctkDICOMServer& obj) this->ProxyServer = nullptr; } -//------------------------------------------------------------------------------ -ctkDICOMServerPrivate::~ctkDICOMServerPrivate() -{ -} - //------------------------------------------------------------------------------ // ctkDICOMServer methods @@ -93,9 +88,7 @@ ctkDICOMServer::ctkDICOMServer(QObject* parent) } //------------------------------------------------------------------------------ -ctkDICOMServer::~ctkDICOMServer() -{ -} +ctkDICOMServer::~ctkDICOMServer() = default; //------------------------------------------------------------------------------ void ctkDICOMServer::setConnectionName(const QString& connectionName) diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index 1e272a6c5d..d6dca38aca 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -45,7 +45,7 @@ class ctkDICOMStorageListenerSCUPrivate : public DcmStorageSCP { this->listener = 0; }; - ~ctkDICOMStorageListenerSCUPrivate() {}; + ~ctkDICOMStorageListenerSCUPrivate() = default; virtual OFCondition acceptAssociations() { @@ -156,7 +156,7 @@ class ctkDICOMStorageListenerPrivate { public: ctkDICOMStorageListenerPrivate(); - ~ctkDICOMStorageListenerPrivate(); + ~ctkDICOMStorageListenerPrivate() = default; QString findFile(const QStringList& nameFilters, const QString& subDir)const; QString defaultConfigFile() const; @@ -189,11 +189,6 @@ ctkDICOMStorageListenerPrivate::ctkDICOMStorageListenerPrivate() this->SCU.setVerbosePCMode(false); } -//------------------------------------------------------------------------------ -ctkDICOMStorageListenerPrivate::~ctkDICOMStorageListenerPrivate() -{ -} - //------------------------------------------------------------------------------ QString ctkDICOMStorageListenerPrivate::defaultConfigFile() const { diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index f92bb906ad..9fb6d2c2b5 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -41,9 +41,7 @@ ctkDICOMStorageListenerJobPrivate::ctkDICOMStorageListenerJobPrivate(ctkDICOMSto } //------------------------------------------------------------------------------ -ctkDICOMStorageListenerJobPrivate::~ctkDICOMStorageListenerJobPrivate() -{ -} +ctkDICOMStorageListenerJobPrivate::~ctkDICOMStorageListenerJobPrivate() = default; //------------------------------------------------------------------------------ // ctkDICOMStorageListenerJob methods @@ -62,9 +60,7 @@ ctkDICOMStorageListenerJob::ctkDICOMStorageListenerJob(ctkDICOMStorageListenerJo } //------------------------------------------------------------------------------ -ctkDICOMStorageListenerJob::~ctkDICOMStorageListenerJob() -{ -} +ctkDICOMStorageListenerJob::~ctkDICOMStorageListenerJob() = default; //---------------------------------------------------------------------------- void ctkDICOMStorageListenerJob::setPort(const int port) diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index 7a27ca98f8..5942f86d80 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -44,9 +44,7 @@ ctkDICOMStorageListenerWorkerPrivate::ctkDICOMStorageListenerWorkerPrivate(ctkDI } //------------------------------------------------------------------------------ -ctkDICOMStorageListenerWorkerPrivate::~ctkDICOMStorageListenerWorkerPrivate() -{ -} +ctkDICOMStorageListenerWorkerPrivate::~ctkDICOMStorageListenerWorkerPrivate() = default; //------------------------------------------------------------------------------ void ctkDICOMStorageListenerWorkerPrivate::setStorageListenerParameters() @@ -103,9 +101,7 @@ ctkDICOMStorageListenerWorker::ctkDICOMStorageListenerWorker(ctkDICOMStorageList } //------------------------------------------------------------------------------ -ctkDICOMStorageListenerWorker::~ctkDICOMStorageListenerWorker() -{ -} +ctkDICOMStorageListenerWorker::~ctkDICOMStorageListenerWorker() = default; //---------------------------------------------------------------------------- void ctkDICOMStorageListenerWorker::cancel() diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp index 0272f94048..b6dd093f06 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMWorker.cpp @@ -25,15 +25,11 @@ #include "ctkDICOMScheduler.h" #include "ctkDICOMWorker.h" -// -------------------------------------------------------------------------- -ctkDICOMWorker::ctkDICOMWorker() -{ -} +//------------------------------------------------------------------------------ +ctkDICOMWorker::ctkDICOMWorker() = default; -//---------------------------------------------------------------------------- -ctkDICOMWorker::~ctkDICOMWorker() -{ -} +//------------------------------------------------------------------------------ +ctkDICOMWorker::~ctkDICOMWorker() = default; //---------------------------------------------------------------------------- void ctkDICOMWorker::startNextJob() From 4643cbd264ba8518d3301ca17fdbfd86b112aa96 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 15:14:25 -0500 Subject: [PATCH 12/73] COMP: Update newly introduced "extendable" private dtor to be virtual --- Libs/DICOM/Core/ctkDICOMInserterWorker_p.h | 2 +- Libs/DICOM/Core/ctkDICOMQueryJob_p.h | 2 +- Libs/DICOM/Core/ctkDICOMQueryWorker_p.h | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h | 2 +- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h index 9b1c138298..bab96ff9cf 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h @@ -39,7 +39,7 @@ class ctkDICOMInserterWorkerPrivate : public QObject public: ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWorker* object); - ~ctkDICOMInserterWorkerPrivate(); + virtual ~ctkDICOMInserterWorkerPrivate(); QSharedPointer Inserter; }; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob_p.h b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h index 60178c0ebd..3d9a60a8fb 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob_p.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h @@ -38,7 +38,7 @@ class ctkDICOMQueryJobPrivate : public QObject public: ctkDICOMQueryJobPrivate(ctkDICOMQueryJob* object); - ~ctkDICOMQueryJobPrivate(); + virtual ~ctkDICOMQueryJobPrivate(); QSharedPointer Server; QMap Filters; diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h index 146a7c3a8b..f11830fe3a 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h @@ -39,7 +39,7 @@ class ctkDICOMQueryWorkerPrivate : public QObject public: ctkDICOMQueryWorkerPrivate(ctkDICOMQueryWorker* object); - ~ctkDICOMQueryWorkerPrivate(); + virtual ~ctkDICOMQueryWorkerPrivate(); void setQueryParameters(); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h index 58351b5a2e..7a3b91b732 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h @@ -38,7 +38,7 @@ class ctkDICOMRetrieveJobPrivate : public QObject public: ctkDICOMRetrieveJobPrivate(ctkDICOMRetrieveJob* object); - ~ctkDICOMRetrieveJobPrivate(); + virtual ~ctkDICOMRetrieveJobPrivate(); QSharedPointer Server; }; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h index a124dc5967..9f1c5ca0b9 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h @@ -39,7 +39,7 @@ class ctkDICOMRetrieveWorkerPrivate : public QObject public: ctkDICOMRetrieveWorkerPrivate(ctkDICOMRetrieveWorker* object); - ~ctkDICOMRetrieveWorkerPrivate(); + virtual ~ctkDICOMRetrieveWorkerPrivate(); void setRetrieveParameters(); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index bcdcfc9af7..5142306bd6 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -48,7 +48,7 @@ class ctkDICOMSchedulerPrivate : public QObject public: ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj); - ~ctkDICOMSchedulerPrivate(); + virtual ~ctkDICOMSchedulerPrivate(); int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); void insertJob(QSharedPointer job); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h index 4e4513fa37..00edd4512b 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob_p.h @@ -38,7 +38,7 @@ class ctkDICOMStorageListenerJobPrivate : public QObject public: ctkDICOMStorageListenerJobPrivate(ctkDICOMStorageListenerJob* object); - ~ctkDICOMStorageListenerJobPrivate(); + virtual ~ctkDICOMStorageListenerJobPrivate(); QString AETitle; int Port; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h index 6dea97c5e9..e15daaf4c7 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker_p.h @@ -39,7 +39,7 @@ class ctkDICOMStorageListenerWorkerPrivate : public QObject public: ctkDICOMStorageListenerWorkerPrivate(ctkDICOMStorageListenerWorker* object); - ~ctkDICOMStorageListenerWorkerPrivate(); + virtual ~ctkDICOMStorageListenerWorkerPrivate(); void init(); void setStorageListenerParameters(); From 0ce2b98c71f8c31b0bb80acb71c45c2fbfd4c2c0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 15:40:41 -0500 Subject: [PATCH 13/73] DOC: Group docstrings of newly introduced methods --- Libs/Core/ctkAbstractJob.h | 16 ++++++++ Libs/Core/ctkAbstractWorker.h | 4 ++ Libs/DICOM/Core/ctkDICOMEcho.h | 23 +++++++++-- Libs/DICOM/Core/ctkDICOMInserter.h | 6 +++ Libs/DICOM/Core/ctkDICOMInserterJob.h | 6 +++ Libs/DICOM/Core/ctkDICOMInserterWorker.h | 4 ++ Libs/DICOM/Core/ctkDICOMJob.h | 12 ++++++ Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 22 +++++++++++ Libs/DICOM/Core/ctkDICOMQuery.cpp | 1 - Libs/DICOM/Core/ctkDICOMQuery.h | 29 ++++++++++++-- Libs/DICOM/Core/ctkDICOMQueryJob.h | 7 +++- Libs/DICOM/Core/ctkDICOMQueryWorker.h | 4 ++ Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 1 - Libs/DICOM/Core/ctkDICOMRetrieve.h | 32 ++++++++++++++-- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 2 + Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 4 ++ Libs/DICOM/Core/ctkDICOMScheduler.h | 27 ++++++++++++- Libs/DICOM/Core/ctkDICOMServer.h | 38 ++++++++++++++++++- Libs/DICOM/Core/ctkDICOMStorageListener.h | 12 +++++- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 6 +++ .../Core/ctkDICOMStorageListenerWorker.h | 4 ++ .../DICOM/Widgets/ctkDICOMPatientItemWidget.h | 21 ++++++++++ Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 22 +++++++++++ .../DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 6 +++ Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 21 ++++++++++ .../Widgets/ctkDICOMVisualBrowserWidget.h | 2 + 26 files changed, 313 insertions(+), 19 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index 055ae3b111..066dcb3247 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -53,13 +53,16 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject explicit ctkAbstractJob(); virtual ~ctkAbstractJob(); + ///@{ /// Job UID QString jobUID() const; virtual void setJobUID(const QString& jobUID); + ///@} /// Class name QString className() const; + ///@{ /// Status enum JobStatus { Initialized = 0, @@ -70,34 +73,47 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject }; JobStatus status() const; virtual void setStatus(const JobStatus& status); + ///@} + ///@{ /// Persistent bool isPersistent() const; void setIsPersistent(const bool& persistent); + ///@} + ///@{ /// Number of retries: current counter of how many times /// the task has been relunched on fails int retryCounter() const; void setRetryCounter(const int& retryCounter); + ///@} + ///@{ /// Set the maximum concurrent jobs per job type. /// Default value is 20. int maximumConcurrentJobsPerType() const; void setMaximumConcurrentJobsPerType(const int& maximumConcurrentJobsPerType); + ///@} + ///@{ /// Maximum number of retries that the Job pool will try on each failed Job /// default: 3 int maximumNumberOfRetry() const; void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + ///@} + ///@{ /// Retry delay in millisec /// default: 100 msec int retryDelay() const; void setRetryDelay(const int& retryDelay); + ///@} + ///@{ /// Priority QThread::Priority priority() const; void setPriority(const QThread::Priority& priority); + ///@} /// Generate worker for job Q_INVOKABLE virtual ctkAbstractWorker* createWorker() = 0; diff --git a/Libs/Core/ctkAbstractWorker.h b/Libs/Core/ctkAbstractWorker.h index 7d6be0d498..1e26a87936 100644 --- a/Libs/Core/ctkAbstractWorker.h +++ b/Libs/Core/ctkAbstractWorker.h @@ -50,17 +50,21 @@ class CTK_CORE_EXPORT ctkAbstractWorker : public QObject, public QRunnable /// Cancel worker virtual void cancel() = 0; + ///@{ /// Job Q_INVOKABLE ctkAbstractJob* job() const; QSharedPointer jobShared() const; Q_INVOKABLE void setJob(ctkAbstractJob& job); void setJob(QSharedPointer job); + ///@} + ///@{ /// Scheduler Q_INVOKABLE ctkAbstractScheduler* scheduler() const; QSharedPointer schedulerShared() const; Q_INVOKABLE void setScheduler(ctkAbstractScheduler& scheduler); void setScheduler(QSharedPointer scheduler); + ///@} protected: QSharedPointer Job; diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h index 3c5e9bbd8d..c795d8a27e 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.h +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -50,27 +50,42 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMEcho : public QObject explicit ctkDICOMEcho(QObject* parent = 0); virtual ~ctkDICOMEcho(); - /// Set methods for connectivity. - /// Empty by default + ///@{ + /// Name identifying the server void setConnectionName(const QString& connectionName); QString connectionName() const; + ///@} + + ///@{ + /// Set methods for connectivity. /// Empty by default void setCallingAETitle(const QString& callingAETitle); QString callingAETitle()const; - /// Empty by default + void setCalledAETitle(const QString& calledAETitle); QString calledAETitle() const; + ///@} + + ///@{ + /// Peer hostname being connected to /// Empty by default void setHost(const QString& host); QString host() const; + ///@} + + ///@{ /// Specify a port for the packet headers. /// \a port ranges from 0 to 65535. /// 80 by default. void setPort(int port); int port() const; - /// connection timeout, default 3 sec. + ///@} + + ///@{ + /// Connection timeout, default 3 sec. void setConnectionTimeout(const int timeout); int connectionTimeout() const; + ///@} /// Echo connection. bool echo(); diff --git a/Libs/DICOM/Core/ctkDICOMInserter.h b/Libs/DICOM/Core/ctkDICOMInserter.h index 6a0c74a9b5..58e268a0ed 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.h +++ b/Libs/DICOM/Core/ctkDICOMInserter.h @@ -48,17 +48,23 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserter : public QObject explicit ctkDICOMInserter(QObject* parent = 0); virtual ~ctkDICOMInserter(); + ///@{ /// Database Filename void setDatabaseFilename(const QString& databaseFilename); QString databaseFilename() const; + ///@} + ///@{ /// Database TagsToPrecache void setTagsToPrecache(const QStringList& tagsToPrecache); QStringList tagsToPrecache() const; + ///@} + ///@{ /// Database TagsToPrecache void setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage); QStringList tagsToExcludeFromStorage() const; + ///@} /// operation is canceled? Q_INVOKABLE bool wasCanceled(); diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index ec243e9cfa..ffd7b13556 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -51,17 +51,23 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const; + ///@{ /// Database Filename void setDatabaseFilename(const QString& databaseFilename); QString databaseFilename() const; + ///}@ + ///@{ /// Database TagsToPrecache void setTagsToPrecache(const QStringList& tagsToPrecache); QStringList tagsToPrecache() const; + ///}@ + ///@{ /// Database TagsToPrecache void setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage); QStringList tagsToExcludeFromStorage() const; + ///}@ /// Create a copy of the object Q_INVOKABLE ctkDICOMJob* generateCopy() const; diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 19469b9644..20316c8e7e 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -54,13 +54,17 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkDICOMWorker /// Cancel worker void cancel(); + ///@{ /// Job Q_INVOKABLE void setJob(ctkAbstractJob& job); void setJob(QSharedPointer job); + ///@} + ///@{ /// Inserter QSharedPointer inserterShared() const; Q_INVOKABLE ctkDICOMInserter* inserter() const; + ///@} protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index bb01357639..e6e7ef4640 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -60,32 +60,44 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob Instances }; + ///@{ /// DICOM Level void setDICOMLevel(const DICOMLevels& dicomLevel); DICOMLevels dicomLevel() const; + ///@} + ///@{ /// Patient ID void setPatientID(const QString& patientID); QString patientID() const; + ///@} + ///@{ /// Study instance UID void setStudyInstanceUID(const QString& studyInstanceUID); QString studyInstanceUID() const; + ///@} + ///@{ /// Series instance UID void setSeriesInstanceUID(const QString& seriesInstanceUID); QString seriesInstanceUID() const; + ///@} + ///@{ /// SOP instance UID void setSOPInstanceUID(const QString& sopInstanceUID); QString sopInstanceUID() const; + ///@} + ///@{ /// Access the list of responses. Q_INVOKABLE QList jobResponseSets() const; QList> jobResponseSetsShared() const; Q_INVOKABLE void setJobResponseSets(QList jobResponseSets); void setJobResponseSets(QList> jobResponseSets); void copyJobResponseSets(QList> jobResponseSets); + ///@} /// Logger report string formatting for specific task Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index d35c46221b..f3f0310e6e 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -56,20 +56,27 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject explicit ctkDICOMJobResponseSet(QObject* parent = 0); virtual ~ctkDICOMJobResponseSet(); + ///@{ /// File Path void setFilePath(const QString& filePath); QString filePath() const; + ///@} + ///@{ /// Copy File /// false as default void setCopyFile(const bool& copyFile); bool copyFile() const; + ///@} + ///@{ /// Overwrite existing dataset /// false as default void setOverwriteExistingDataset(const bool& overwriteExistingDataset); bool overwriteExistingDataset() const; + ///@} + ///@{ /// Job type enum JobType { None = 0, @@ -84,31 +91,45 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject }; void setTypeOfJob(const JobType& typeOfJob); JobType typeOfJob() const; + ///@} + ///@{ /// Task UID void setJobUID(const QString& jobUID); QString jobUID() const; + ///@} + ///@{ /// Patient ID void setPatientID(const QString& patientID); QString patientID() const; + ///@} + ///@{ /// Study instance UID void setStudyInstanceUID(const QString& studyInstanceUID); QString studyInstanceUID() const; + ///@} + ///@{ /// Series instance UID void setSeriesInstanceUID(const QString& seriesInstanceUID); QString seriesInstanceUID() const; + ///@} + ///@{ /// SOP instance UID void setSOPInstanceUID(const QString& sopInstanceUID); QString sopInstanceUID() const; + ///@} + ///@{ /// Connection name void setConnectionName(const QString& connectionName); QString connectionName() const; + ///@} + ///@{ /// DCM datasets Q_INVOKABLE void setDataset(DcmItem* dcmItem, bool takeOwnership = true); Q_INVOKABLE ctkDICOMItem* dataset() const; @@ -116,6 +137,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_INVOKABLE void setDatasets(QMap dcmItems, bool takeOwnership = true); Q_INVOKABLE QMap datasets() const; QMap> datasetsShared() const; + ///@} /// Copy object Q_INVOKABLE void deepCopy(ctkDICOMJobResponseSet* node); diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index ad53f2de5d..c1bed4a9ec 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -159,7 +159,6 @@ ctkDICOMQuery::~ctkDICOMQuery() { } -/// Set methods for connectivity //------------------------------------------------------------------------------ void ctkDICOMQuery::setConnectionName(const QString& connectionName) { diff --git a/Libs/DICOM/Core/ctkDICOMQuery.h b/Libs/DICOM/Core/ctkDICOMQuery.h index 041c2a82ca..bcda2bec7a 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.h +++ b/Libs/DICOM/Core/ctkDICOMQuery.h @@ -55,33 +55,51 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject explicit ctkDICOMQuery(QObject* parent = 0); virtual ~ctkDICOMQuery(); - /// Set methods for connectivity. - /// Empty by default + ///@{ + /// Name identifying the server void setConnectionName(const QString& connectionName); QString connectionName() const; + ///@} + + ///@{ + /// Methods for connectivity. /// Empty by default void setCallingAETitle(const QString& callingAETitle); QString callingAETitle()const; - /// Empty by default + void setCalledAETitle(const QString& calledAETitle); QString calledAETitle() const; + ///@} + + ///@{ + /// Peer hostname being connected to /// Empty by default void setHost(const QString& host); QString host() const; + ///@} + + ///@{ /// Specify a port for the packet headers. /// \a port ranges from 0 to 65535. /// 0 by default. void setPort(int port); int port() const; + ///@} + + ///@{ /// connection timeout, default 10 sec. void setConnectionTimeout(const int timeout); int connectionTimeout() const; + ///@} + + ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. void setMaximumPatientsQuery(const int maximumPatientsQuery); int maximumPatientsQuery(); + ///@} - /// + ///@{ /// Filters are keyword/value pairs as generated by /// the ctkDICOMWidgets in a human readable (and editable) /// format. The Query is responsible for converting these @@ -100,6 +118,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject /// No filter (empty) by default. Q_INVOKABLE void setFilters(const QMap&); Q_INVOKABLE QMap filters()const; + ///@} /// operation is canceled? Q_INVOKABLE bool wasCanceled(); @@ -136,12 +155,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject const QString& studyInstanceUID, const QString& seriesInstanceUID); + ///@{ /// Access the list of datasets from the last queryPatients, queryStudies, /// querySeries and queryInstances methods. Q_INVOKABLE QList jobResponseSets() const; QList> jobResponseSetsShared() const; void setJobUID(const QString& jobUID); QString jobUID() const; + ///@} Q_SIGNALS: /// Signal is emitted inside the query() function. It ranges from 0 to 100. diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index 95c3720b5e..e3f7de40a8 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -48,7 +48,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob explicit ctkDICOMQueryJob(); virtual ~ctkDICOMQueryJob(); - /// + ///@{ /// Filters are keyword/value pairs as generated by /// the ctkDICOMWidgets in a human readable (and editable) /// format. The Query is responsible for converting these @@ -67,17 +67,22 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob /// No filter (empty) by default. Q_INVOKABLE void setFilters(const QMap &filters); Q_INVOKABLE QMap filters()const; + ///@} + ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. void setMaximumPatientsQuery(const int maximumPatientsQuery); int maximumPatientsQuery(); + ///@} + ///@{ /// Server Q_INVOKABLE ctkDICOMServer* server() const; QSharedPointer serverShared() const; Q_INVOKABLE void setServer(ctkDICOMServer& server); void setServer(QSharedPointer server); + ///@} /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const; diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index 394562bfa7..82df5e1fb5 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -53,13 +53,17 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkDICOMWorker /// Cancel worker void cancel(); + ///@{ /// Job Q_INVOKABLE void setJob(ctkAbstractJob& job); void setJob(QSharedPointer job); + ///@} + ///@{ /// Querier QSharedPointer querierShared() const; Q_INVOKABLE ctkDICOMQuery* querier() const; + ///@} protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index 303996167c..cd386990e1 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -699,7 +699,6 @@ ctkDICOMRetrieve::~ctkDICOMRetrieve() { } -/// Set methods for connectivity //------------------------------------------------------------------------------ void ctkDICOMRetrieve::setConnectionName(const QString &connectionName) { diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.h b/Libs/DICOM/Core/ctkDICOMRetrieve.h index a82545bb0e..0c0fc0e8cb 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.h @@ -56,47 +56,72 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieve : public QObject explicit ctkDICOMRetrieve(QObject* parent = 0); virtual ~ctkDICOMRetrieve(); - /// Set methods for connectivity - /// name identifying the server + ///@{ + /// Name identifying the server void setConnectionName(const QString& connectionName); QString connectionName() const; + ///@} + + ///@{ /// CTK_AE - the AE string by which the peer host might /// recognize your request void setCallingAETitle(const QString& callingAETitle); QString callingAETitle() const; + ///@} + + ///@{ /// CTK_AE - the AE of the service of peer host that you are calling /// which tells the host what you are requesting void setCalledAETitle(const QString& calledAETitle); QString calledAETitle() const; - /// peer hostname being connected to + ///@} + + ///@{ + /// Peer hostname being connected to void setHost(const QString& host); QString host() const; + ///@} + + ///@{ /// [0, 65365] port on peer host - e.g. 11112 void setPort(int port); int port() const; + ///@} + + ///@{ /// Typically CTK_STORE or similar - needs to be something that the /// peer host knows about and is able to move data into /// Only used when calling moveSeries or moveStudy void setMoveDestinationAETitle(const QString& moveDestinationAETitle); QString moveDestinationAETitle() const; + ///@} + + ///@{ /// prefer to keep using the existing association to peer host when doing /// multiple requests (default true) void setKeepAssociationOpen(const bool keepOpen); bool keepAssociationOpen() const; + ///@} + + ///@{ /// connection timeout, default 3 sec. void setConnectionTimeout(const int timeout); int connectionTimeout() const; + ///@} /// operation is canceled? Q_INVOKABLE bool wasCanceled(); + ///@{ /// where to insert new data sets obtained via get (must be set for /// get to succeed) Q_INVOKABLE void setDatabase(ctkDICOMDatabase& dicomDatabase); void setDatabase(QSharedPointer dicomDatabase); Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; QSharedPointer dicomDatabaseShared() const; + ///@} + ///@{ /// Access the list of datasets from the last operation. Q_INVOKABLE QList jobResponseSets() const; QList> jobResponseSetsShared() const; @@ -105,6 +130,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieve : public QObject void removeJobResponseSet(QSharedPointer jobResponseSet); Q_INVOKABLE void setJobUID(const QString& jobUID); Q_INVOKABLE QString jobUID() const; + ///@} /// Patient ID from from the last operation. QString patientID() const; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 94ceeba7f0..1a1ba33a84 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -45,11 +45,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob explicit ctkDICOMRetrieveJob(); virtual ~ctkDICOMRetrieveJob(); + ///@{ /// Server Q_INVOKABLE ctkDICOMServer* server() const; QSharedPointer serverShared() const; Q_INVOKABLE void setServer(ctkDICOMServer& server); void setServer(QSharedPointer server); + ///@} /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h index 15a8ea60c9..1b29c1c324 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -51,13 +51,17 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkDICOMWorker /// Cancel worker void cancel(); + ///@{ /// Job Q_INVOKABLE void setJob(ctkAbstractJob& job); void setJob(QSharedPointer job); + ///@} + ///@{ /// Retriever QSharedPointer retrieverShared() const; Q_INVOKABLE ctkDICOMRetrieve* retriever() const; + ///@} protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 135f0d52bb..6b8463d959 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -103,25 +103,28 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler const QString &AETitle, QThread::Priority priority = QThread::LowPriority); + ///@{ /// Insert results from a job void insertJobResponseSet(QSharedPointer jobResponseSet, QThread::Priority priority = QThread::HighPriority); void insertJobResponseSets(QList> jobResponseSets, QThread::Priority priority = QThread::HighPriority); - + ///@} /// Return the Dicom Database. Q_INVOKABLE ctkDICOMDatabase* dicomDatabase() const; /// Return Dicom Database as a shared pointer /// (not Python-wrappable). QSharedPointer dicomDatabaseShared() const; + /// Set the Dicom Database. Q_INVOKABLE void setDicomDatabase(ctkDICOMDatabase& dicomDatabase); /// Set the Dicom Database as a shared pointer /// (not Python-wrappable). void setDicomDatabase(QSharedPointer dicomDatabase); - /// + + ///@{ /// Filters are keyword/value pairs as generated by /// the ctkDICOMWidgets in a human readable (and editable) /// format. The Query is responsible for converting these @@ -140,7 +143,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler /// No filter (empty) by default. Q_INVOKABLE void setFilters(const QMap &filters); Q_INVOKABLE QMap filters()const; + ///@} + ///@{ /// Servers Q_INVOKABLE int getNumberOfServers(); Q_INVOKABLE int getNumberOfQueryRetrieveServers(); @@ -154,7 +159,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE void removeAllServers(); Q_INVOKABLE QString getServerNameFromIndex(int id); Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + ///@} + ///@{ /// Jobs managment Q_INVOKABLE int numberOfJobs(); Q_INVOKABLE int numberOfPersistentJobs(); @@ -176,33 +183,49 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler const QStringList& sopInstanceUIDs = {}); Q_INVOKABLE void raiseJobsPriorityForSeries(const QStringList& selectedSeriesInstanceUIDs, QThread::Priority priority = QThread::HighestPriority); + ///@} + ///@{ /// Maximum number of concurrent QThreads spawned by the threadPool in the Job pool /// default: 20 int maximumThreadCount() const; void setMaximumThreadCount(const int& maximumThreadCount); + ///@} + + ///@{ /// Maximum number of retries that the Job pool will try on each failed Job /// default: 3 int maximumNumberOfRetry() const; void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + ///@} + + ///@{ /// Retry delay in millisec /// default: 100 msec int retryDelay() const; void setRetryDelay(const int& retryDelay); + ///@} + + ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. void setMaximumPatientsQuery(const int maximumPatientsQuery); int maximumPatientsQuery(); + ///@} + ///@{ /// Return the listener Job. Q_INVOKABLE ctkDICOMStorageListenerJob* listenerJob(); Q_INVOKABLE bool isStorageListenerActive(); + ///@} + ///@{ /// Return the threadPool. Q_INVOKABLE QThreadPool* threadPool() const; /// Return threadPool as a shared pointer /// (not Python-wrappable). QSharedPointer threadPoolShared() const; + ///@} /// Utility method to transform/pass informations between threads by qt signals Q_INVOKABLE QVariant jobToDetail(ctkDICOMJob* job); diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h index 19fa552a65..382e1882a9 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.h +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -52,33 +52,54 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject explicit ctkDICOMServer(QObject* parent = 0); virtual ~ctkDICOMServer(); - /// Set methods for connectivity + ///@{ + /// Name identifying the server void setConnectionName(const QString& connectionName); QString connectionName() const; + ///}@ + + ///@{ /// Query/Retrieve operations /// true as default void setQueryRetrieveEnabled(bool queryRetrieveEnabled); bool queryRetrieveEnabled() const; + ///}@ + + ///@{ /// Storage operations /// true as default void setStorageEnabled(bool storageEnabled); bool storageEnabled() const; + ///}@ + + ///@{ /// CTK_AE - the AE string by which the peer host might /// recognize your request void setCallingAETitle(const QString& callingAETitle); QString callingAETitle() const; + ///}@ + + ///@{ /// CTK_AE - the AE of the service of peer host that you are calling /// which tells the host what you are requesting void setCalledAETitle(const QString& calledAETitle); QString calledAETitle() const; - /// peer hostname being connected to + ///}@ + + ///@{ + /// Peer hostname being connected to void setHost(const QString& host); QString host() const; + ///}@ + + ///@{ /// [0, 65365] port on peer host /// 80 as default void setPort(int port); int port() const; + ///}@ + ///@{ /// Protocol for retrieval of query results. /// CGET by default enum RetrieveProtocol @@ -91,23 +112,36 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject RetrieveProtocol retrieveProtocol() const; Q_INVOKABLE void setRetrieveProtocolAsString(QString protocolString); Q_INVOKABLE QString retrieveProtocolAsString() const; + ///}@ + + ///@{ /// Typically CTK_STORE or similar - needs to be something that the /// peer host knows about and is able to move data into /// Only used when calling moveSeries or moveStudy void setMoveDestinationAETitle(const QString& moveDestinationAETitle); QString moveDestinationAETitle() const; + ///}@ + + ///@{ /// prefer to keep using the existing association to peer host when doing /// multiple requests (default true) void setKeepAssociationOpen(const bool keepOpen); bool keepAssociationOpen(); + ///}@ + + ///@{ /// connection timeout in seconds, default 10 s. void setConnectionTimeout(const int timeout); int connectionTimeout(); + ///}@ + + ///@{ /// proxy server Q_INVOKABLE ctkDICOMServer* proxyServer() const; QSharedPointer proxyServerShared() const; Q_INVOKABLE void setProxyServer(ctkDICOMServer& proxyServer); void setProxyServer(QSharedPointer proxyServer); + ///}@ protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index 61a4fa83d0..c840451eca 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -49,19 +49,28 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject explicit ctkDICOMStorageListener(QObject* parent = 0); virtual ~ctkDICOMStorageListener(); + ///@{ /// Storage AE title /// "CTKSTORE" by default void setAETitle(const QString& AETitle); QString AETitle() const; + ///@} + + ///@{ /// Storage port /// 11112 by default void setPort(int port); int port() const; - /// connection timeout + ///@} + + ///@{ + /// Connection timeout /// 1 sec by default void setConnectionTimeout(const int timeout); int connectionTimeout(); + ///@} + ///@{ /// Access the list of datasets from the last operation. Q_INVOKABLE QList jobResponseSets() const; QList> jobResponseSetsShared() const; @@ -70,6 +79,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject void removeJobResponseSet(QSharedPointer jobResponseSet); Q_INVOKABLE void setJobUID(const QString& jobUID); Q_INVOKABLE QString jobUID() const; + ///@} /// Start listen connection. bool listen(); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index f572161219..3d4cd6f3aa 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -47,17 +47,23 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob explicit ctkDICOMStorageListenerJob(); virtual ~ctkDICOMStorageListenerJob(); + ///@{ /// Port, default: 11112 void setPort(const int port); int port() const; + ///@} + ///@{ /// AETitle, default: CTKSTORE void setAETitle(const QString& AETitle); QString AETitle() const; + ///@} + ///@{ /// Connection timeout, default 1 sec. void setConnectionTimeout(const int timeout); int connectionTimeout() const; + ///@} /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h index f79d718136..7e6eefd3eb 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -52,13 +52,17 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkDICOMWorke /// Cancel worker void cancel(); + ///@{ /// Job Q_INVOKABLE void setJob(ctkAbstractJob& job); void setJob(QSharedPointer job); + ///@} + ///@{ /// Retriever QSharedPointer storageListenerShared() const; Q_INVOKABLE ctkDICOMStorageListener* storageListener() const; + ///@} public slots: void onInsertJobDetail(); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index c26f77d1e1..62e9f97a95 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -54,18 +54,25 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget explicit ctkDICOMPatientItemWidget(QWidget* parent = nullptr); virtual ~ctkDICOMPatientItemWidget(); + ///@{ /// Patient item void setPatientItem(const QString& patientItem); QString patientItem() const; + ///@} + ///@{ /// Patient ID void setPatientID(const QString& patientID); QString patientID() const; + ///@} + ///@{ /// Query Filters /// Empty by default void setFilteringStudyDescription(const QString& filteringStudyDescription); QString filteringStudyDescription() const; + ///@} + /// Date filtering enum enum DateType { @@ -77,6 +84,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget LastYear }; + ///@{ /// Available values: /// Any, /// Today, @@ -87,22 +95,33 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget /// Any by default. void setFilteringDate(const DateType& filteringDate); DateType filteringDate() const; + ///@} + + ///@{ /// Empty by default void setFilteringSeriesDescription(const QString& filteringSeriesDescription); QString filteringSeriesDescription() const; + ///@} + + ///@{ /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default void setFilteringModalities(const QStringList& filteringModalities); QStringList filteringModalities() const; + ///@} + ///@{ /// Number of non collapsed studies per patient /// 2 by default void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); int numberOfStudiesPerPatient() const; + ///@} + ///@{ /// Set the thumbnail size: small, medium, large /// medium by default void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; + ///@} /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; @@ -132,9 +151,11 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget /// Return number of days from filtering date attribute Q_INVOKABLE static int getNDaysFromFilteringDate(ctkDICOMPatientItemWidget::DateType filteringDate); + ///@{ /// Add/Remove study item widgets Q_INVOKABLE void addStudyItemWidget(const QString &studyItem); Q_INVOKABLE void removeStudyItemWidget(const QString &studyItem); + ///@} /// Set selection for all studies/series Q_INVOKABLE void setSelection(bool selected); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index 81a527d706..bac32a2fdf 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -56,48 +56,68 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget explicit ctkDICOMSeriesItemWidget(QWidget* parent = nullptr); virtual ~ctkDICOMSeriesItemWidget(); + ///@{ /// Series Item void setSeriesItem(const QString& seriesItem); QString seriesItem() const; + ///@} + ///@{ /// Patient ID void setPatientID(const QString& patientID); QString patientID() const; + ///@} + ///@{ /// Study instance UID void setStudyInstanceUID(const QString& studyInstanceUID); QString studyInstanceUID() const; + ///@} + ///@{ /// Series instance UID void setSeriesInstanceUID(const QString& seriesInstanceUID); QString seriesInstanceUID() const; + ///@} + ///@{ /// Series Number void setSeriesNumber(const QString& seriesNumber); QString seriesNumber() const; + ///@} + ///@{ /// Modality void setModality(const QString& modality); QString modality() const; + ///@} + ///@{ /// Series Description void setSeriesDescription(const QString& seriesDescription); QString seriesDescription() const; + ///@} + ///@{ /// Stop Series widget to run new jobs void setStopJobs(const bool& stopJobs); bool stopJobs() const; + ///@} + ///@{ /// Set high priority to all jobs run from the Series widget void setRaiseJobsPriority(const bool& raiseJobsPriority); bool raiseJobsPriority() const; + ///@} /// Series lives in the server bool isCloud() const; + ///@{ /// in case the retrieve job failed void setRetrieveFailed(const bool& retrieveFailed); bool retrieveFailed() const; + ///@} /// Series has been loaded by the parent widget bool IsLoaded() const; @@ -105,10 +125,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget /// Series is visible in the parent widget bool IsVisible() const; + ///@{ /// Set the thumbnail size in pixel /// 200 by default void setThumbnailSizePixel(const int &thumbnailSizePixel); int thumbnailSizePixel() const; + ///@} /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index 16bac14ded..3cd5a1b4df 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -50,20 +50,26 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget explicit ctkDICOMServerNodeWidget2(QWidget* parent=0); virtual ~ctkDICOMServerNodeWidget2(); + ///@{ /// Storage listener is enabled /// false by default void setStorageListenerEnabled(const bool enabled); bool storageListenerEnabled() const; + ///@} + ///@{ /// Storage AE title /// "CTKSTORE" by default void setStorageAETitle(const QString& storageAETitle); QString storageAETitle() const; + ///@} + ///@{ /// Storage port /// 11112 by default void setStoragePort(const int storagePort); int storagePort() const; + ///@} /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index eb6d3e3f66..6870839ca7 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -61,30 +61,42 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget explicit ctkDICOMStudyItemWidget(QWidget* parent = nullptr); virtual ~ctkDICOMStudyItemWidget(); + ///@{ /// Study item void setStudyItem(const QString& studyItem); QString studyItem() const; + ///@} + ///@{ /// Patient ID void setPatientID(const QString& patientID); QString patientID() const; + ///@} + ///@{ /// Study instance UID void setStudyInstanceUID(const QString& studyInstanceUID); QString studyInstanceUID() const; + ///@} + ///@{ /// Study title void setTitle(const QString& title); QString title() const; + ///@} + ///@{ /// Study Description void setDescription(const QString& description); QString description() const; + ///@} + ///@{ /// Study GroupBox collapsed /// False by default void setCollapsed(bool collapsed); bool collapsed() const; + ///@} /// Number of series displayed per row int numberOfSeriesPerRow() const; @@ -96,25 +108,34 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget Large, }; + ///@{ /// Set the thumbnail size: small, medium, large /// medium by default void setThumbnailSize(const ThumbnailSizeOption &thumbnailSize); ThumbnailSizeOption thumbnailSize() const; + ///@} /// Thumbnail size in pixel int thumbnailSizePixel() const; + ///@{ /// Study is selected void setSelection(bool selected); bool selection() const; + ///@} + ///@{ /// Query Filters /// Empty by default void setFilteringSeriesDescription(const QString& filteringSeriesDescription); QString filteringSeriesDescription() const; + ///@} + + ///@{ /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default void setFilteringModalities(const QStringList& filteringModalities); QStringList filteringModalities() const; + ///@} /// Return the scheduler. Q_INVOKABLE ctkDICOMScheduler* scheduler() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 66aff3d5e8..b9823abb82 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -356,6 +356,7 @@ public Q_SLOTS: void exportSeries(QString dirPath, QStringList uids); protected Q_SLOTS: + ///@{ /// \brief Import directories /// /// This is used when user selected one or multiple @@ -364,6 +365,7 @@ protected Q_SLOTS: /// \sa importDirectories(QString directory, int mode) void onImportDirectoriesSelected(QStringList directories); void onImportDirectoryComboBoxCurrentIndexChanged(int index); + ///@} /// Called when a right mouse click is made on a tab of the patient tab widget void showPatientContextMenu(const QPoint& point); From 91a6fc60eccc9c6c98ccdbb58e8fcae36b46719a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 18:08:08 -0500 Subject: [PATCH 14/73] COMP: Fix constness of newly introduced DICOM methods accepting POD type --- Libs/Core/ctkAbstractJob.cpp | 12 ++++++------ Libs/Core/ctkAbstractJob.h | 12 ++++++------ Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h | 10 +++++----- Libs/DICOM/Core/ctkDICOMEcho.cpp | 2 +- Libs/DICOM/Core/ctkDICOMEcho.h | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 6 +++--- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 6 +++--- Libs/DICOM/Core/ctkDICOMQuery.cpp | 6 +++--- Libs/DICOM/Core/ctkDICOMQuery.h | 4 ++-- Libs/DICOM/Core/ctkDICOMServer.cpp | 4 ++-- Libs/DICOM/Core/ctkDICOMServer.h | 4 ++-- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListener.h | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 2 +- 15 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.cpp b/Libs/Core/ctkAbstractJob.cpp index 2a1fefaace..c30039fb18 100644 --- a/Libs/Core/ctkAbstractJob.cpp +++ b/Libs/Core/ctkAbstractJob.cpp @@ -73,7 +73,7 @@ ctkAbstractJob::JobStatus ctkAbstractJob::status() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setStatus(const JobStatus &status) +void ctkAbstractJob::setStatus(JobStatus status) { this->Status = status; } @@ -85,7 +85,7 @@ bool ctkAbstractJob::isPersistent() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setIsPersistent(const bool &persistent) +void ctkAbstractJob::setIsPersistent(bool persistent) { this->Persistent = persistent; } @@ -97,7 +97,7 @@ int ctkAbstractJob::retryCounter() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setRetryCounter(const int& retryCounter) +void ctkAbstractJob::setRetryCounter(int retryCounter) { this->RetryCounter = retryCounter; } @@ -109,7 +109,7 @@ int ctkAbstractJob::maximumConcurrentJobsPerType() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setMaximumConcurrentJobsPerType(const int &maximumConcurrentJobsPerType) +void ctkAbstractJob::setMaximumConcurrentJobsPerType(int maximumConcurrentJobsPerType) { this->MaximumConcurrentJobsPerType = maximumConcurrentJobsPerType; } @@ -121,7 +121,7 @@ int ctkAbstractJob::maximumNumberOfRetry() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) +void ctkAbstractJob::setMaximumNumberOfRetry(int maximumNumberOfRetry) { this->MaximumNumberOfRetry = maximumNumberOfRetry; } @@ -133,7 +133,7 @@ int ctkAbstractJob::retryDelay() const } //---------------------------------------------------------------------------- -void ctkAbstractJob::setRetryDelay(const int &retryDelay) +void ctkAbstractJob::setRetryDelay(int retryDelay) { this->RetryDelay = retryDelay; } diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index 066dcb3247..bbc14e15f2 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -72,41 +72,41 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject Finished, }; JobStatus status() const; - virtual void setStatus(const JobStatus& status); + virtual void setStatus(JobStatus status); ///@} ///@{ /// Persistent bool isPersistent() const; - void setIsPersistent(const bool& persistent); + void setIsPersistent(bool persistent); ///@} ///@{ /// Number of retries: current counter of how many times /// the task has been relunched on fails int retryCounter() const; - void setRetryCounter(const int& retryCounter); + void setRetryCounter(int retryCounter); ///@} ///@{ /// Set the maximum concurrent jobs per job type. /// Default value is 20. int maximumConcurrentJobsPerType() const; - void setMaximumConcurrentJobsPerType(const int& maximumConcurrentJobsPerType); + void setMaximumConcurrentJobsPerType(int maximumConcurrentJobsPerType); ///@} ///@{ /// Maximum number of retries that the Job pool will try on each failed Job /// default: 3 int maximumNumberOfRetry() const; - void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + void setMaximumNumberOfRetry(int maximumNumberOfRetry); ///@} ///@{ /// Retry delay in millisec /// default: 100 msec int retryDelay() const; - void setRetryDelay(const int& retryDelay); + void setRetryDelay(int retryDelay); ///@} ///@{ diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index 9b6b61aa75..3d61351dc9 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -57,7 +57,7 @@ public slots: return new ctkJobDetail(); } - void setTypeOfJob(ctkJobDetail* td, const ctkDICOMJobResponseSet::JobType& jobType) + void setTypeOfJob(ctkJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) { td->TypeOfJob = jobType; } @@ -120,7 +120,7 @@ public slots: return td->ConnectionName; } - void setNumberOfDataSets(ctkJobDetail* td, const int& numberOfDataSets) + void setNumberOfDataSets(ctkJobDetail* td, int numberOfDataSets) { td->NumberOfDataSets = numberOfDataSets; } @@ -137,17 +137,17 @@ public slots: ts->setFilePath(filePath); } - void setCopyFile(ctkDICOMJobResponseSet* ts, const bool& copyFile) + void setCopyFile(ctkDICOMJobResponseSet* ts, bool copyFile) { ts->setCopyFile(copyFile); } - void setOverwriteExistingDataset(ctkDICOMJobResponseSet* ts, const bool& overwriteExistingDataset) + void setOverwriteExistingDataset(ctkDICOMJobResponseSet* ts, bool overwriteExistingDataset) { ts->setOverwriteExistingDataset(overwriteExistingDataset); } - void setTypeOfJob(ctkDICOMJobResponseSet* ts, const ctkDICOMJobResponseSet::JobType& TypeOfJob) + void setTypeOfJob(ctkDICOMJobResponseSet* ts, ctkDICOMJobResponseSet::JobType TypeOfJob) { ts->setTypeOfJob(TypeOfJob); } diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp index 5bfd592d7f..b111e17d84 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.cpp +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -160,7 +160,7 @@ int ctkDICOMEcho::port() const } //----------------------------------------------------------------------------- -void ctkDICOMEcho::setConnectionTimeout(const int timeout) +void ctkDICOMEcho::setConnectionTimeout(int timeout) { Q_D(ctkDICOMEcho); d->SCU.setACSETimeout(timeout); diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h index c795d8a27e..3298774c22 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.h +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -83,7 +83,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMEcho : public QObject ///@{ /// Connection timeout, default 3 sec. - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout() const; ///@} diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 118f3ee234..af0a71a1de 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -125,7 +125,7 @@ QString ctkDICOMJobResponseSet::filePath() const } //---------------------------------------------------------------------------- -void ctkDICOMJobResponseSet::setCopyFile(const bool ©File) +void ctkDICOMJobResponseSet::setCopyFile(bool copyFile) { Q_D(ctkDICOMJobResponseSet); d->CopyFile = copyFile; @@ -139,7 +139,7 @@ bool ctkDICOMJobResponseSet::copyFile() const } //---------------------------------------------------------------------------- -void ctkDICOMJobResponseSet::setOverwriteExistingDataset(const bool &overwriteExistingDataset) +void ctkDICOMJobResponseSet::setOverwriteExistingDataset(bool overwriteExistingDataset) { Q_D(ctkDICOMJobResponseSet); d->OverwriteExistingDataset = overwriteExistingDataset; @@ -153,7 +153,7 @@ bool ctkDICOMJobResponseSet::overwriteExistingDataset() const } //---------------------------------------------------------------------------- -void ctkDICOMJobResponseSet::setTypeOfJob(const ctkDICOMJobResponseSet::JobType &typeOfJob) +void ctkDICOMJobResponseSet::setTypeOfJob(ctkDICOMJobResponseSet::JobType typeOfJob) { Q_D(ctkDICOMJobResponseSet); d->TypeOfJob = typeOfJob; diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index f3f0310e6e..7b6189bc07 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -65,14 +65,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject ///@{ /// Copy File /// false as default - void setCopyFile(const bool& copyFile); + void setCopyFile(bool copyFile); bool copyFile() const; ///@} ///@{ /// Overwrite existing dataset /// false as default - void setOverwriteExistingDataset(const bool& overwriteExistingDataset); + void setOverwriteExistingDataset(bool overwriteExistingDataset); bool overwriteExistingDataset() const; ///@} @@ -89,7 +89,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject RetrieveSOPInstance, StoreSOPInstance }; - void setTypeOfJob(const JobType& typeOfJob); + void setTypeOfJob(JobType typeOfJob); JobType typeOfJob() const; ///@} diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index c1bed4a9ec..eee621369a 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -230,7 +230,7 @@ int ctkDICOMQuery::port()const } //----------------------------------------------------------------------------- -void ctkDICOMQuery::setConnectionTimeout(const int timeout) +void ctkDICOMQuery::setConnectionTimeout(int timeout) { Q_D(ctkDICOMQuery); d->SCU.setACSETimeout(timeout); @@ -245,7 +245,7 @@ int ctkDICOMQuery::connectionTimeout() const } //------------------------------------------------------------------------------ -void ctkDICOMQuery::setMaximumPatientsQuery(const int maximumPatientsQuery) +void ctkDICOMQuery::setMaximumPatientsQuery(int maximumPatientsQuery) { Q_D(ctkDICOMQuery); d->MaximumPatientsQuery = maximumPatientsQuery; @@ -266,7 +266,7 @@ bool ctkDICOMQuery::wasCanceled() } //------------------------------------------------------------------------------ -void ctkDICOMQuery::setFilters( const QMap& filters ) +void ctkDICOMQuery::setFilters(const QMap& filters) { Q_D(ctkDICOMQuery); d->Filters = filters; diff --git a/Libs/DICOM/Core/ctkDICOMQuery.h b/Libs/DICOM/Core/ctkDICOMQuery.h index bcda2bec7a..3199922a91 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.h +++ b/Libs/DICOM/Core/ctkDICOMQuery.h @@ -88,14 +88,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject ///@{ /// connection timeout, default 10 sec. - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout() const; ///@} ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. - void setMaximumPatientsQuery(const int maximumPatientsQuery); + void setMaximumPatientsQuery(int maximumPatientsQuery); int maximumPatientsQuery(); ///@} diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index 7232b3e62f..636645582d 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -262,7 +262,7 @@ QString ctkDICOMServer::moveDestinationAETitle()const } //------------------------------------------------------------------------------ -void ctkDICOMServer::setKeepAssociationOpen(const bool keepOpen) +void ctkDICOMServer::setKeepAssociationOpen(bool keepOpen) { Q_D(ctkDICOMServer); d->KeepAssociationOpen = keepOpen; @@ -276,7 +276,7 @@ bool ctkDICOMServer::keepAssociationOpen() } //----------------------------------------------------------------------------- -void ctkDICOMServer::setConnectionTimeout(const int timeout) +void ctkDICOMServer::setConnectionTimeout(int timeout) { Q_D(ctkDICOMServer); d->ConnectionTimeout = timeout; diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h index 382e1882a9..624cf8a2f8 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.h +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -125,13 +125,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject ///@{ /// prefer to keep using the existing association to peer host when doing /// multiple requests (default true) - void setKeepAssociationOpen(const bool keepOpen); + void setKeepAssociationOpen(bool keepOpen); bool keepAssociationOpen(); ///}@ ///@{ /// connection timeout in seconds, default 10 s. - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout(); ///}@ diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index d6dca38aca..55112d6723 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -327,7 +327,7 @@ int ctkDICOMStorageListener::port()const } //----------------------------------------------------------------------------- -void ctkDICOMStorageListener::setConnectionTimeout(const int timeout) +void ctkDICOMStorageListener::setConnectionTimeout(int timeout) { Q_D(ctkDICOMStorageListener); d->SCU.setACSETimeout(timeout); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index c840451eca..b11eda7e0d 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -66,7 +66,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject ///@{ /// Connection timeout /// 1 sec by default - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout(); ///@} diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index 9fb6d2c2b5..ed938341e4 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -91,7 +91,7 @@ QString ctkDICOMStorageListenerJob::AETitle() const } //---------------------------------------------------------------------------- -void ctkDICOMStorageListenerJob::setConnectionTimeout(const int timeout) +void ctkDICOMStorageListenerJob::setConnectionTimeout(int timeout) { Q_D(ctkDICOMStorageListenerJob); d->ConnectionTimeout = timeout; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 3d4cd6f3aa..f0982d68bc 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -61,7 +61,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob ///@{ /// Connection timeout, default 1 sec. - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout() const; ///@} From 983edf357483e8ce5bcb5a2d7bd219063a9aa722 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 18:18:25 -0500 Subject: [PATCH 15/73] COMP: Declare as "override" newly introduced virtual DICOM methods --- Libs/DICOM/Core/ctkDICOMInserterJob.h | 6 +++--- Libs/DICOM/Core/ctkDICOMInserterWorker.h | 4 ++-- Libs/DICOM/Core/ctkDICOMQueryJob.h | 6 +++--- Libs/DICOM/Core/ctkDICOMQueryWorker.h | 4 ++-- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 6 +++--- Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 4 ++-- Libs/DICOM/Core/ctkDICOMScheduler.h | 8 ++++---- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 6 +++--- Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index ffd7b13556..1e37df24dc 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -49,7 +49,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob virtual ~ctkDICOMInserterJob(); /// Logger report string formatting for specific task - Q_INVOKABLE QString loggerReport(const QString& status) const; + Q_INVOKABLE QString loggerReport(const QString& status) const override; ///@{ /// Database Filename @@ -70,10 +70,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob ///}@ /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const; + Q_INVOKABLE ctkDICOMJob* generateCopy() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker(); + Q_INVOKABLE ctkDICOMWorker* createWorker() override; protected: QString DatabaseFilename; diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 20316c8e7e..5eabd5545b 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -49,10 +49,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkDICOMWorker virtual ~ctkDICOMInserterWorker(); /// Execute worker - void run(); + void run() override; /// Cancel worker - void cancel(); + void cancel() override; ///@{ /// Job diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index e3f7de40a8..a426c8fa9e 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -85,13 +85,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob ///@} /// Logger report string formatting for specific task - Q_INVOKABLE QString loggerReport(const QString& status) const; + Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const; + Q_INVOKABLE ctkDICOMJob* generateCopy() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker(); + Q_INVOKABLE ctkDICOMWorker* createWorker() override; protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index 82df5e1fb5..25d7a41606 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -48,10 +48,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkDICOMWorker virtual ~ctkDICOMQueryWorker(); /// Execute worker - void run(); + void run() override; /// Cancel worker - void cancel(); + void cancel() override; ///@{ /// Job diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 1a1ba33a84..15ccd559e0 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -54,13 +54,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob ///@} /// Logger report string formatting for specific task - Q_INVOKABLE QString loggerReport(const QString& status) const; + Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const; + Q_INVOKABLE ctkDICOMJob* generateCopy() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker(); + Q_INVOKABLE ctkDICOMWorker* createWorker() override; protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h index 1b29c1c324..cfa8a8f97d 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -46,10 +46,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkDICOMWorker virtual ~ctkDICOMRetrieveWorker(); /// Execute worker - void run(); + void run() override; /// Cancel worker - void cancel(); + void cancel() override; ///@{ /// Job diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 6b8463d959..57016aa169 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -235,10 +235,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler void progressJobDetail(QVariant data); public Q_SLOTS: - void onJobStarted(); - void onJobCanceled(); - void onJobFailed(); - void onJobFinished(); + void onJobStarted() override; + void onJobCanceled() override; + void onJobFailed() override; + void onJobFinished() override; void onQueueJobsInThreadPool(); protected: diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index f0982d68bc..ae160aaa61 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -66,13 +66,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob ///@} /// Logger report string formatting for specific task - Q_INVOKABLE QString loggerReport(const QString& status) const; + Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const; + Q_INVOKABLE ctkDICOMJob* generateCopy() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker(); + Q_INVOKABLE ctkDICOMWorker* createWorker() override; protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h index 7e6eefd3eb..307aab153f 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -47,10 +47,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkDICOMWorke virtual ~ctkDICOMStorageListenerWorker(); /// Execute worker - void run(); + void run() override; /// Cancel worker - void cancel(); + void cancel() override; ///@{ /// Job From 18b8f169333cd844caaab5fa91d7f514a1837246 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 18:40:40 -0500 Subject: [PATCH 16/73] COMP: Fix constness of ctkDICOMScheduler method accepting QString param --- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 14 +++++++------- Libs/DICOM/Core/ctkDICOMScheduler.h | 8 ++++---- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index a011efeae6..7d75215c91 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -114,7 +114,7 @@ void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) } //------------------------------------------------------------------------------ -void ctkDICOMSchedulerPrivate::removeJob(QString jobUID) +void ctkDICOMSchedulerPrivate::removeJob(const QString& jobUID) { Q_Q(ctkDICOMScheduler); @@ -722,14 +722,14 @@ void ctkDICOMScheduler::addJob(ctkDICOMJob *job) } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::deleteJob(QString jobUID) +void ctkDICOMScheduler::deleteJob(const QString& jobUID) { Q_D(ctkDICOMScheduler); d->removeJob(jobUID); } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::deleteWorker(QString jobUID) +void ctkDICOMScheduler::deleteWorker(const QString& jobUID) { Q_D(ctkDICOMScheduler); @@ -743,22 +743,22 @@ void ctkDICOMScheduler::deleteWorker(QString jobUID) } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMScheduler::getJobSharedByUID(QString JobUID) +QSharedPointer ctkDICOMScheduler::getJobSharedByUID(const QString& jobUID) { Q_D(ctkDICOMScheduler); QMutexLocker ml(&d->mMutex); - QMap>::iterator it = d->JobsQueue.find(JobUID); + QMap>::iterator it = d->JobsQueue.find(jobUID); if (it == d->JobsQueue.end()) { return nullptr; } - return d->JobsQueue.value(JobUID); + return d->JobsQueue.value(jobUID); } //---------------------------------------------------------------------------- -ctkDICOMJob *ctkDICOMScheduler::getJobByUID(QString jobUID) +ctkDICOMJob *ctkDICOMScheduler::getJobByUID(const QString& jobUID) { QSharedPointer job = this->getJobSharedByUID(jobUID); if (!job) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 57016aa169..f25a9979e3 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -166,10 +166,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE int numberOfJobs(); Q_INVOKABLE int numberOfPersistentJobs(); Q_INVOKABLE void addJob(ctkDICOMJob* job); - Q_INVOKABLE void deleteJob(QString jobUID); - Q_INVOKABLE void deleteWorker(QString jobUID); - QSharedPointer getJobSharedByUID(QString jobUID); - Q_INVOKABLE ctkDICOMJob* getJobByUID(QString jobUID); + Q_INVOKABLE void deleteJob(const QString& jobUID); + Q_INVOKABLE void deleteWorker(const QString& jobUID); + QSharedPointer getJobSharedByUID(const QString& jobUID); + Q_INVOKABLE ctkDICOMJob* getJobByUID(const QString& jobUID); Q_INVOKABLE void waitForFinish(); Q_INVOKABLE void waitForDone(int msec); Q_INVOKABLE void waitForFinishByUIDs(const QStringList& patientIDs = {}, diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index 5142306bd6..aff83a5caa 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -52,7 +52,7 @@ class ctkDICOMSchedulerPrivate : public QObject int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); void insertJob(QSharedPointer job); - void removeJob(QString jobUID); + void removeJob(const QString& jobUID); QString generateUniqueJobUID(); ctkDICOMServer* getServerFromProxyServersByConnectionName(const QString&); From 02ad81567c1e93ff65a4eed8fe90905df43127b6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 18:22:54 -0500 Subject: [PATCH 17/73] COMP: Remove re-declaration of virtual pure methods in DICOM worker --- Libs/DICOM/Core/ctkDICOMWorker.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMWorker.h b/Libs/DICOM/Core/ctkDICOMWorker.h index ddc491cf27..1839bbe813 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.h +++ b/Libs/DICOM/Core/ctkDICOMWorker.h @@ -45,12 +45,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMWorker : public ctkAbstractWorker explicit ctkDICOMWorker(); virtual ~ctkDICOMWorker(); - /// Execute worker - virtual void run() = 0; - - /// Cancel worker - virtual void cancel() = 0; - public slots: void startNextJob(); void onJobCanceled(); From c7dc6b8dc74d16e6e797e98eba28af580d8852c2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 19:44:51 -0500 Subject: [PATCH 18/73] STYLE: Rename DICOM property from "TypeOfJob" to "jobType" --- .../Cpp/ctkDICOMJobResponseSetTest1.cpp | 6 ++-- .../Core/ctkDICOMCorePythonQtDecorators.h | 12 +++---- Libs/DICOM/Core/ctkDICOMDatabase.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 16 +++++----- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 10 +++--- Libs/DICOM/Core/ctkDICOMQuery.cpp | 8 ++--- Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 12 +++---- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 2 +- .../Widgets/ctkDICOMPatientItemWidget.cpp | 2 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 32 +++++++++---------- .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 2 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 6 ++-- 12 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp index 1872387084..43e6a5611c 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp @@ -43,7 +43,7 @@ int ctkDICOMJobResponseSetTest1(int argc, char * argv []) { CHECK_QSTRING(jobResponseSet.filePath(), ""); CHECK_BOOL(jobResponseSet.copyFile(), false); CHECK_BOOL(jobResponseSet.overwriteExistingDataset(), false); - CHECK_INT(jobResponseSet.typeOfJob(), ctkDICOMJobResponseSet::JobType::None); + CHECK_INT(jobResponseSet.jobType(), ctkDICOMJobResponseSet::JobType::None); CHECK_QSTRING(jobResponseSet.jobUID(), ""); CHECK_QSTRING(jobResponseSet.patientID(), ""); CHECK_QSTRING(jobResponseSet.studyInstanceUID(), ""); @@ -58,8 +58,8 @@ int ctkDICOMJobResponseSetTest1(int argc, char * argv []) { CHECK_BOOL(jobResponseSet.copyFile(), true); jobResponseSet.setOverwriteExistingDataset(true); CHECK_BOOL(jobResponseSet.overwriteExistingDataset(), true); - jobResponseSet.setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); - CHECK_INT(jobResponseSet.typeOfJob(), ctkDICOMJobResponseSet::JobType::RetrieveStudy); + jobResponseSet.setJobType(ctkDICOMJobResponseSet::JobType::RetrieveStudy); + CHECK_INT(jobResponseSet.jobType(), ctkDICOMJobResponseSet::JobType::RetrieveStudy); jobResponseSet.setJobUID("JobUID"); CHECK_QSTRING(jobResponseSet.jobUID(), "JobUID"); jobResponseSet.setPatientID("patientID"); diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index 3d61351dc9..de1f743131 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -57,13 +57,13 @@ public slots: return new ctkJobDetail(); } - void setTypeOfJob(ctkJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) + void setJobType(ctkJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) { - td->TypeOfJob = jobType; + td->JobType = jobType; } - ctkDICOMJobResponseSet::JobType TypeOfJob(ctkJobDetail* td) + ctkDICOMJobResponseSet::JobType jobType(ctkJobDetail* td) { - return td->TypeOfJob; + return td->JobType; } void setJobUID(ctkJobDetail* td, const QString& jobUID) @@ -147,9 +147,9 @@ public slots: ts->setOverwriteExistingDataset(overwriteExistingDataset); } - void setTypeOfJob(ctkDICOMJobResponseSet* ts, ctkDICOMJobResponseSet::JobType TypeOfJob) + void setJobType(ctkDICOMJobResponseSet* ts, ctkDICOMJobResponseSet::JobType jobType) { - ts->setTypeOfJob(TypeOfJob); + ts->setJobType(jobType); } void setJobUID(ctkDICOMJobResponseSet* ts, const QString& jobUID) diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index d7d39d90a5..3fc897c7ef 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -2549,7 +2549,7 @@ void ctkDICOMDatabase::insert(QList> jobR QDir databaseDirectory(this->databaseDirectory()); foreach (QSharedPointer jobResponseSet, jobResponseSets) { - ctkDICOMJobResponseSet::JobType typeOfTask = jobResponseSet->typeOfJob(); + ctkDICOMJobResponseSet::JobType typeOfTask = jobResponseSet->jobType(); QString filePath = jobResponseSet->filePath(); QString url; bool generateThumbnail = false; // thumbnail will be generated when needed, don't slow down import with that diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index af0a71a1de..178b0b12ad 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -47,7 +47,7 @@ class ctkDICOMJobResponseSetPrivate: public QObject bool CopyFile; bool OverwriteExistingDataset; - ctkDICOMJobResponseSet::JobType TypeOfJob; + ctkDICOMJobResponseSet::JobType JobType; QString JobUID; QString PatientID; QString StudyInstanceUID; @@ -64,7 +64,7 @@ class ctkDICOMJobResponseSetPrivate: public QObject ctkDICOMJobResponseSetPrivate::ctkDICOMJobResponseSetPrivate(ctkDICOMJobResponseSet& obj) : q_ptr(&obj) { - this->TypeOfJob = ctkDICOMJobResponseSet::JobType::None; + this->JobType = ctkDICOMJobResponseSet::JobType::None; this->JobUID = ""; this->PatientID = ""; this->StudyInstanceUID = ""; @@ -153,17 +153,17 @@ bool ctkDICOMJobResponseSet::overwriteExistingDataset() const } //---------------------------------------------------------------------------- -void ctkDICOMJobResponseSet::setTypeOfJob(ctkDICOMJobResponseSet::JobType typeOfJob) +void ctkDICOMJobResponseSet::setJobType(ctkDICOMJobResponseSet::JobType jobType) { Q_D(ctkDICOMJobResponseSet); - d->TypeOfJob = typeOfJob; + d->JobType = jobType; } //------------------------------------------------------------------------------ -ctkDICOMJobResponseSet::JobType ctkDICOMJobResponseSet::typeOfJob() const +ctkDICOMJobResponseSet::JobType ctkDICOMJobResponseSet::jobType() const { Q_D(const ctkDICOMJobResponseSet); - return d->TypeOfJob; + return d->JobType; } //------------------------------------------------------------------------------ @@ -350,7 +350,7 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) this->setFilePath(node->filePath()); this->setCopyFile(node->copyFile()); this->setOverwriteExistingDataset(node->overwriteExistingDataset()); - this->setTypeOfJob(node->typeOfJob()); + this->setJobType(node->jobType()); this->setJobUID(node->jobUID()); this->setPatientID(node->patientID()); this->setStudyInstanceUID(node->studyInstanceUID()); @@ -394,7 +394,7 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) QVariant ctkDICOMJobResponseSet::jobResponseSetToDetail() { ctkJobDetail td; - td.TypeOfJob = this->typeOfJob(); + td.JobType = this->jobType(); td.JobUID = this->jobUID(); td.PatientID = this->patientID(); td.StudyInstanceUID = this->studyInstanceUID(); diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 7b6189bc07..858902ef90 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -44,7 +44,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_PROPERTY(QString filePath READ filePath WRITE setFilePath); Q_PROPERTY(bool copyFile READ copyFile WRITE setCopyFile); Q_PROPERTY(bool overwriteExistingDataset READ overwriteExistingDataset WRITE setOverwriteExistingDataset); - Q_PROPERTY(JobType typeOfJob READ typeOfJob WRITE setTypeOfJob); + Q_PROPERTY(JobType jobType READ jobType WRITE setJobType); Q_PROPERTY(QString jobUID READ jobUID WRITE setJobUID); Q_PROPERTY(QString patientID READ patientID WRITE setPatientID); Q_PROPERTY(QString studyInstanceUID READ studyInstanceUID WRITE setStudyInstanceUID); @@ -89,8 +89,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject RetrieveSOPInstance, StoreSOPInstance }; - void setTypeOfJob(JobType typeOfJob); - JobType typeOfJob() const; + void setJobType(JobType jobType); + JobType jobType() const; ///@} ///@{ @@ -155,7 +155,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject struct CTK_DICOM_CORE_EXPORT ctkJobDetail { explicit ctkJobDetail(): - TypeOfJob(ctkDICOMJobResponseSet::JobType::None), + JobType(ctkDICOMJobResponseSet::JobType::None), JobClass(""), DICOMLevel(ctkDICOMJob::DICOMLevels::Patients), JobUID(""), @@ -167,7 +167,7 @@ struct CTK_DICOM_CORE_EXPORT ctkJobDetail { NumberOfDataSets(0){} virtual ~ctkJobDetail(){}; - ctkDICOMJobResponseSet::JobType TypeOfJob; + ctkDICOMJobResponseSet::JobType JobType; QString JobClass; ctkDICOMJob::DICOMLevels DICOMLevel; QString JobUID; diff --git a/Libs/DICOM/Core/ctkDICOMQuery.cpp b/Libs/DICOM/Core/ctkDICOMQuery.cpp index eee621369a..42b453121b 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.cpp +++ b/Libs/DICOM/Core/ctkDICOMQuery.cpp @@ -593,7 +593,7 @@ bool ctkDICOMQuery::queryPatients() QSharedPointer JobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); - JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryPatients); + JobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::QueryPatients); JobResponseSet->setConnectionName(d->ConnectionName); JobResponseSet->setJobUID(d->JobUID); @@ -723,7 +723,7 @@ bool ctkDICOMQuery::queryStudies(const QString& patientID) QSharedPointer JobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); - JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryStudies); + JobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::QueryStudies); JobResponseSet->setPatientID(patientID.toStdString().c_str()); JobResponseSet->setConnectionName(d->ConnectionName); JobResponseSet->setJobUID(d->JobUID); @@ -844,7 +844,7 @@ bool ctkDICOMQuery::querySeries(const QString& patientID, QSharedPointer JobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); - JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QuerySeries); + JobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::QuerySeries); JobResponseSet->setPatientID(patientID.toStdString().c_str()); JobResponseSet->setStudyInstanceUID(studyInstanceUID.toStdString().c_str()); JobResponseSet->setConnectionName(d->ConnectionName); @@ -964,7 +964,7 @@ bool ctkDICOMQuery::queryInstances(const QString& patientID, QSharedPointer JobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); - JobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::QueryInstances); + JobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::QueryInstances); JobResponseSet->setPatientID(patientID.toStdString().c_str()); JobResponseSet->setStudyInstanceUID(studyInstanceUID.toStdString().c_str()); JobResponseSet->setSeriesInstanceUID(seriesInstanceUID.toStdString().c_str()); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index cd386990e1..4295cc3f5b 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -102,15 +102,15 @@ class ctkDICOMRetrieveSCUPrivate : public DcmSCU QSharedPointer(new ctkDICOMJobResponseSet); if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSOPInstance) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); } else if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSeries); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveSeries); } else if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveStudy) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveStudy); } jobResponseSet->setPatientID(this->retrieve->patientID()); jobResponseSet->setStudyInstanceUID(this->retrieve->studyInstanceUID()); @@ -502,15 +502,15 @@ bool ctkDICOMRetrievePrivate::move(const QString& patientID, QSharedPointer(new ctkDICOMJobResponseSet); if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSOPInstance) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance); } else if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveSeries); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveSeries); } else if (q->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveStudy) { - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::RetrieveStudy); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::RetrieveStudy); } jobResponseSet->setStudyInstanceUID(q->studyInstanceUID()); jobResponseSet->setSeriesInstanceUID(q->seriesInstanceUID()); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index 55112d6723..7fe8c585af 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -111,7 +111,7 @@ class ctkDICOMStorageListenerSCUPrivate : public DcmStorageSCP { QSharedPointer jobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); - jobResponseSet->setTypeOfJob(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); jobResponseSet->setStudyInstanceUID(studyUID.c_str()); jobResponseSet->setSeriesInstanceUID(seriesUID.c_str()); jobResponseSet->setSOPInstanceUID(instanceUID.c_str()); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index f2b1cfff1f..6dcebd4849 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -699,7 +699,7 @@ void ctkDICOMPatientItemWidget::updateGUIFromScheduler(QVariant data) } if (td.JobUID.isEmpty() || - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryStudies || + td.JobType != ctkDICOMJobResponseSet::JobType::QueryStudies || td.PatientID != d->PatientID) { return; diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index acdc27bc48..4f1a8c6b51 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -197,12 +197,12 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) return; } - ctkDICOMJobResponseSet::JobType typeOfJob = ctkDICOMJobResponseSet::JobType::None; + ctkDICOMJobResponseSet::JobType jobType = ctkDICOMJobResponseSet::JobType::None; QString jobSopInstanceUID; if (!td.JobUID.isEmpty()) { jobSopInstanceUID = td.SOPInstanceUID; - typeOfJob = td.TypeOfJob; + jobType = td.JobType; } QStringList instancesList = this->DicomDatabase->instancesForSeries(this->SeriesInstanceUID); @@ -283,8 +283,8 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) // Get file for thumbnail if (file.isEmpty() && this->IsCloud && - (typeOfJob == ctkDICOMJobResponseSet::JobType::None || - typeOfJob == ctkDICOMJobResponseSet::JobType::QueryInstances)) + (jobType == ctkDICOMJobResponseSet::JobType::None || + jobType == ctkDICOMJobResponseSet::JobType::QueryInstances)) { this->Scheduler->retrieveSOPInstance(this->PatientID, this->StudyInstanceUID, @@ -299,10 +299,10 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) if (numberOfFrames > 1 && this->IsCloud && ((jobSopInstanceUID == this->CentralFrameSOPInstanceUID && - (typeOfJob == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || - typeOfJob == ctkDICOMJobResponseSet::JobType::StoreSOPInstance)) || - (typeOfJob == ctkDICOMJobResponseSet::JobType::None || - typeOfJob == ctkDICOMJobResponseSet::JobType::QueryInstances))) + (jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance)) || + (jobType == ctkDICOMJobResponseSet::JobType::None || + jobType == ctkDICOMJobResponseSet::JobType::QueryInstances))) { this->Scheduler->retrieveSeries(this->PatientID, this->StudyInstanceUID, @@ -314,8 +314,8 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); if ((jobSopInstanceUID.isEmpty() || jobSopInstanceUID == this->CentralFrameSOPInstanceUID || - typeOfJob == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || - typeOfJob == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) && + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) && !file.isEmpty()) { this->drawThumbnail(file, numberOfFrames); @@ -871,10 +871,10 @@ void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(QVariant data) ctkJobDetail td = data.value(); if (td.JobUID.isEmpty() || - (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryInstances && - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSeries && - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance&& - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || + (td.JobType != ctkDICOMJobResponseSet::JobType::QueryInstances && + td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && + td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance&& + td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || td.StudyInstanceUID != d->StudyInstanceUID || td.SeriesInstanceUID != d->SeriesInstanceUID) { @@ -891,8 +891,8 @@ void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(QVariant data) ctkJobDetail td = data.value(); if (td.JobUID.isEmpty() || - (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::RetrieveSeries && - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || + (td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && + td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || td.StudyInstanceUID != d->StudyInstanceUID || td.SeriesInstanceUID != d->SeriesInstanceUID) { diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 4200f55548..46a02a30c3 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -772,7 +772,7 @@ void ctkDICOMStudyItemWidget::updateGUIFromScheduler(QVariant data) } if (td.JobUID.isEmpty() || - td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QuerySeries || + td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries || td.PatientID != d->PatientID || td.StudyInstanceUID != d->StudyInstanceUID) { diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 20bdb774f9..904c895a7a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -2595,13 +2595,13 @@ void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) d->updateFiltersWarnings(); return; } - else if (td.TypeOfJob == ctkDICOMJobResponseSet::JobType::QueryStudies || - td.TypeOfJob == ctkDICOMJobResponseSet::JobType::QuerySeries) + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies || + td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries) { d->updateFiltersWarnings(); return; } - else if (td.TypeOfJob != ctkDICOMJobResponseSet::JobType::QueryPatients) + else if (td.JobType != ctkDICOMJobResponseSet::JobType::QueryPatients) { return; } From 94d29fb192ff1b3b32a2ff5e5d0a1334367e84f7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 19:47:06 -0500 Subject: [PATCH 19/73] STYLE: Rename DICOMDatabase ivar from "typeOfTask" to "jobType" --- Libs/DICOM/Core/ctkDICOMDatabase.cpp | 50 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index 3fc897c7ef..70cf79c938 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -2549,7 +2549,7 @@ void ctkDICOMDatabase::insert(QList> jobR QDir databaseDirectory(this->databaseDirectory()); foreach (QSharedPointer jobResponseSet, jobResponseSets) { - ctkDICOMJobResponseSet::JobType typeOfTask = jobResponseSet->jobType(); + ctkDICOMJobResponseSet::JobType jobType = jobResponseSet->jobType(); QString filePath = jobResponseSet->filePath(); QString url; bool generateThumbnail = false; // thumbnail will be generated when needed, don't slow down import with that @@ -2572,16 +2572,16 @@ void ctkDICOMDatabase::insert(QList> jobR if (patientID.isEmpty()) { - if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryPatients) + if (jobType == ctkDICOMJobResponseSet::JobType::QueryPatients) { patientID = key; } - else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryStudies || - typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + else if (jobType == ctkDICOMJobResponseSet::JobType::QueryStudies || + jobType == ctkDICOMJobResponseSet::JobType::QuerySeries || + jobType == ctkDICOMJobResponseSet::JobType::QueryInstances || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { patientID = jobResponseSet->patientID(); } @@ -2591,15 +2591,15 @@ void ctkDICOMDatabase::insert(QList> jobR if (studyInstanceUID.isEmpty()) { - if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryStudies) + if (jobType == ctkDICOMJobResponseSet::JobType::QueryStudies) { studyInstanceUID = key; } - else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + else if (jobType == ctkDICOMJobResponseSet::JobType::QuerySeries || + jobType == ctkDICOMJobResponseSet::JobType::QueryInstances || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { studyInstanceUID = jobResponseSet->studyInstanceUID(); } @@ -2616,13 +2616,13 @@ void ctkDICOMDatabase::insert(QList> jobR if (seriesInstanceUID.isEmpty()) { - if (typeOfTask == ctkDICOMJobResponseSet::JobType::QuerySeries) + if (jobType == ctkDICOMJobResponseSet::JobType::QuerySeries) { seriesInstanceUID = key; } - else if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) + else if (jobType == ctkDICOMJobResponseSet::JobType::QueryInstances || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { seriesInstanceUID = jobResponseSet->seriesInstanceUID(); } @@ -2632,14 +2632,14 @@ void ctkDICOMDatabase::insert(QList> jobR if (sopInstanceUID.isEmpty()) { - if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances) + if (jobType == ctkDICOMJobResponseSet::JobType::QueryInstances) { sopInstanceUID = key; } - else if (typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveStudy || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSeries || - typeOfTask == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || - typeOfTask == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) + else if (jobType == ctkDICOMJobResponseSet::JobType::RetrieveStudy || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || + jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) { sopInstanceUID = jobResponseSet->sopInstanceUID(); } @@ -2659,13 +2659,13 @@ void ctkDICOMDatabase::insert(QList> jobR continue; } - if (studyInstanceUID.isEmpty() && typeOfTask != ctkDICOMJobResponseSet::JobType::QueryPatients) + if (studyInstanceUID.isEmpty() && jobType != ctkDICOMJobResponseSet::JobType::QueryPatients) { logger.error("ctkDICOMDatabase::insert: dataset has no studyInstanceUID"); continue; } - if (typeOfTask == ctkDICOMJobResponseSet::JobType::QueryInstances) + if (jobType == ctkDICOMJobResponseSet::JobType::QueryInstances) { url = "dimse+ctk://" + jobResponseSet->connectionName(); if (!studyInstanceUID.isEmpty()) From 06d13054d456b51c9102c7467c3902abc0ac4997 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 21:31:54 -0500 Subject: [PATCH 20/73] COMP: Rename "ctkJobDetail" to "ctkDICOMJobDetail" --- .../Core/ctkDICOMCorePythonQtDecorators.h | 40 +++++++++---------- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 8 ++-- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 2 +- .../Widgets/ctkDICOMPatientItemWidget.cpp | 2 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 10 ++--- .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 2 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 4 +- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index de1f743131..0220714eb7 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -44,87 +44,87 @@ class ctkDICOMCorePythonQtDecorators : public QObject ctkDICOMCorePythonQtDecorators() { - PythonQt::self()->registerCPPClass("ctkJobDetail", 0, "CTKDICOMCore"); + PythonQt::self()->registerCPPClass("ctkDICOMJobDetail", 0, "CTKDICOMCore"); } public slots: //---------------------------------------------------------------------------- - // ctkJobDetail + // ctkDICOMJobDetail //---------------------------------------------------------------------------- - ctkJobDetail* new_ctkJobDetail() + ctkDICOMJobDetail* new_ctkDICOMJobDetail() { - return new ctkJobDetail(); + return new ctkDICOMJobDetail(); } - void setJobType(ctkJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) + void setJobType(ctkDICOMJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) { td->JobType = jobType; } - ctkDICOMJobResponseSet::JobType jobType(ctkJobDetail* td) + ctkDICOMJobResponseSet::JobType jobType(ctkDICOMJobDetail* td) { return td->JobType; } - void setJobUID(ctkJobDetail* td, const QString& jobUID) + void setJobUID(ctkDICOMJobDetail* td, const QString& jobUID) { td->JobUID = jobUID; } - QString JobUID(ctkJobDetail* td) + QString JobUID(ctkDICOMJobDetail* td) { return td->JobUID; } - void setPatientID(ctkJobDetail* td, const QString& patientID) + void setPatientID(ctkDICOMJobDetail* td, const QString& patientID) { td->PatientID = patientID; } - QString patientID(ctkJobDetail* td) + QString patientID(ctkDICOMJobDetail* td) { return td->PatientID; } - void setStudyInstanceUID(ctkJobDetail* td, const QString& studyInstanceUID) + void setStudyInstanceUID(ctkDICOMJobDetail* td, const QString& studyInstanceUID) { td->StudyInstanceUID = studyInstanceUID; } - QString studyInstanceUID(ctkJobDetail* td) + QString studyInstanceUID(ctkDICOMJobDetail* td) { return td->StudyInstanceUID; } - void setSeriesInstanceUID(ctkJobDetail* td, const QString& seriesInstanceUID) + void setSeriesInstanceUID(ctkDICOMJobDetail* td, const QString& seriesInstanceUID) { td->SeriesInstanceUID = seriesInstanceUID; } - QString seriesInstanceUID(ctkJobDetail* td) + QString seriesInstanceUID(ctkDICOMJobDetail* td) { return td->SeriesInstanceUID; } - void setSOPInstanceUID(ctkJobDetail* td, const QString& sopInstanceUID) + void setSOPInstanceUID(ctkDICOMJobDetail* td, const QString& sopInstanceUID) { td->SOPInstanceUID = sopInstanceUID; } - QString sopInstanceUID(ctkJobDetail* td) + QString sopInstanceUID(ctkDICOMJobDetail* td) { return td->SOPInstanceUID; } - void setConnectionName(ctkJobDetail* td, const QString& connectionName) + void setConnectionName(ctkDICOMJobDetail* td, const QString& connectionName) { td->ConnectionName = connectionName; } - QString connectionName(ctkJobDetail* td) + QString connectionName(ctkDICOMJobDetail* td) { return td->ConnectionName; } - void setNumberOfDataSets(ctkJobDetail* td, int numberOfDataSets) + void setNumberOfDataSets(ctkDICOMJobDetail* td, int numberOfDataSets) { td->NumberOfDataSets = numberOfDataSets; } - int numberOfDataSets(ctkJobDetail* td) + int numberOfDataSets(ctkDICOMJobDetail* td) { return td->NumberOfDataSets; } diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 178b0b12ad..e5ab7fa680 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -393,7 +393,7 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) //------------------------------------------------------------------------------ QVariant ctkDICOMJobResponseSet::jobResponseSetToDetail() { - ctkJobDetail td; + ctkDICOMJobDetail td; td.JobType = this->jobType(); td.JobUID = this->jobUID(); td.PatientID = this->patientID(); diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 858902ef90..99d17ab1e6 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -153,8 +153,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_DISABLE_COPY(ctkDICOMJobResponseSet); }; -struct CTK_DICOM_CORE_EXPORT ctkJobDetail { - explicit ctkJobDetail(): +struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail { + explicit ctkDICOMJobDetail(): JobType(ctkDICOMJobResponseSet::JobType::None), JobClass(""), DICOMLevel(ctkDICOMJob::DICOMLevels::Patients), @@ -165,7 +165,7 @@ struct CTK_DICOM_CORE_EXPORT ctkJobDetail { SOPInstanceUID(""), ConnectionName(""), NumberOfDataSets(0){} - virtual ~ctkJobDetail(){}; + virtual ~ctkDICOMJobDetail(){}; ctkDICOMJobResponseSet::JobType JobType; QString JobClass; @@ -177,5 +177,5 @@ struct CTK_DICOM_CORE_EXPORT ctkJobDetail { QString SOPInstanceUID; QString ConnectionName; int NumberOfDataSets; -}; Q_DECLARE_METATYPE(ctkJobDetail); +}; Q_DECLARE_METATYPE(ctkDICOMJobDetail); #endif diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 7d75215c91..516693b876 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -1017,7 +1017,7 @@ QSharedPointer ctkDICOMScheduler::threadPoolShared() const //---------------------------------------------------------------------------- QVariant ctkDICOMScheduler::jobToDetail(ctkDICOMJob* job) { - ctkJobDetail td; + ctkDICOMJobDetail td; td.JobClass = job->className(); td.DICOMLevel = job->dicomLevel(); td.JobUID = job->jobUID(); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index 6dcebd4849..b373257ee1 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -692,7 +692,7 @@ void ctkDICOMPatientItemWidget::updateGUIFromScheduler(QVariant data) { Q_D(ctkDICOMPatientItemWidget); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobUID.isEmpty()) { d->createStudies(); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 4f1a8c6b51..b46c6fb482 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -61,7 +61,7 @@ class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget void init(); QString getDICOMCenterFrameFromInstances(QStringList instancesList); - void createThumbnail(ctkJobDetail td); + void createThumbnail(ctkDICOMJobDetail td); void drawModalityThumbnail(); void drawThumbnail(const QString& file, int numberOfFrames); void drawTextWithShadow(QPainter *painter, @@ -189,7 +189,7 @@ QString ctkDICOMSeriesItemWidgetPrivate::getDICOMCenterFrameFromInstances(QStrin } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkJobDetail td) +void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkDICOMJobDetail td) { if (!this->DicomDatabase) { @@ -849,7 +849,7 @@ void ctkDICOMSeriesItemWidget::generateInstances() return; } - ctkJobDetail td; + ctkDICOMJobDetail td; d->createThumbnail(td); QStringList instancesList = d->DicomDatabase->instancesForSeries(d->SeriesInstanceUID); if (!d->StopJobs && @@ -869,7 +869,7 @@ void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(QVariant data) { Q_D(ctkDICOMSeriesItemWidget); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobUID.isEmpty() || (td.JobType != ctkDICOMJobResponseSet::JobType::QueryInstances && td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && @@ -889,7 +889,7 @@ void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(QVariant data) { Q_D(ctkDICOMSeriesItemWidget); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobUID.isEmpty() || (td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 46a02a30c3..3240c9ae45 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -765,7 +765,7 @@ void ctkDICOMStudyItemWidget::updateGUIFromScheduler(QVariant data) { Q_D(ctkDICOMStudyItemWidget); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobUID.isEmpty()) { d->createSeries(); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 904c895a7a..92cc69781d 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -2589,7 +2589,7 @@ void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) d->ProgressFrame->hide(); d->QueryPatientPushButton->setIcon(QIcon(":/Icons/query.svg")); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobUID.isEmpty()) { d->updateFiltersWarnings(); @@ -2620,7 +2620,7 @@ void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) void ctkDICOMVisualBrowserWidget::onTaskFailed(QVariant data) { Q_D(ctkDICOMVisualBrowserWidget); - ctkJobDetail td = data.value(); + ctkDICOMJobDetail td = data.value(); if (td.JobClass == "ctkDICOMQueryJob") { From 90e889cbda259cb14bac2a885dfe2dc8274bf744 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 21:44:50 -0500 Subject: [PATCH 21/73] COMP: Update DICOM scheduler API to use QString for server connection name --- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 10 +++------- Libs/DICOM/Core/ctkDICOMScheduler.h | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 516693b876..e18b09aac4 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -526,7 +526,7 @@ ctkDICOMServer* ctkDICOMScheduler::getNthServer(int id) } //---------------------------------------------------------------------------- -ctkDICOMServer* ctkDICOMScheduler::getServer(const char *connectionName) +ctkDICOMServer* ctkDICOMScheduler::getServer(const QString& connectionName) { Q_D(ctkDICOMScheduler); ctkDICOMServer* server = this->getNthServer(this->getServerIndexFromName(connectionName)); @@ -553,7 +553,7 @@ void ctkDICOMScheduler::addServer(QSharedPointer server) } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::removeServer(const char *connectionName) +void ctkDICOMScheduler::removeServer(const QString& connectionName) { this->removeNthServer(this->getServerIndexFromName(connectionName)); } @@ -596,13 +596,9 @@ QString ctkDICOMScheduler::getServerNameFromIndex(int id) } //---------------------------------------------------------------------------- -int ctkDICOMScheduler::getServerIndexFromName(const char *connectionName) +int ctkDICOMScheduler::getServerIndexFromName(const QString& connectionName) { Q_D(ctkDICOMScheduler); - if (!connectionName) - { - return -1; - } for(int serverIndex = 0; serverIndex < d->Servers.size(); ++serverIndex) { QSharedPointer server = d->Servers.at(serverIndex); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index f25a9979e3..590a63f23e 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -151,14 +151,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE int getNumberOfQueryRetrieveServers(); Q_INVOKABLE int getNumberOfStorageServers(); Q_INVOKABLE ctkDICOMServer* getNthServer(int id); - Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE ctkDICOMServer* getServer(const QString& connectionName); Q_INVOKABLE void addServer(ctkDICOMServer& server); void addServer(QSharedPointer server); - Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeServer(const QString& connectionName); Q_INVOKABLE void removeNthServer(int id); Q_INVOKABLE void removeAllServers(); Q_INVOKABLE QString getServerNameFromIndex(int id); - Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + Q_INVOKABLE int getServerIndexFromName(const QString& connectionName); ///@} ///@{ From 4c84f02f0f7a9f0a2828a7e0b3e5e01bc56b368d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 22:15:55 -0500 Subject: [PATCH 22/73] COMP: Fix ctkDICOMJobDetail wrapping renaming JobUID -> jobUID, adding jobClass --- Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index 0220714eb7..28b46c3098 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -57,6 +57,15 @@ public slots: return new ctkDICOMJobDetail(); } + void setJobClass(ctkDICOMJobDetail* td, const QString& jobClass) + { + td->JobClass = jobClass; + } + QString jobClass(ctkDICOMJobDetail* td) + { + return td->JobClass; + } + void setJobType(ctkDICOMJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) { td->JobType = jobType; @@ -70,7 +79,7 @@ public slots: { td->JobUID = jobUID; } - QString JobUID(ctkDICOMJobDetail* td) + QString jobUID(ctkDICOMJobDetail* td) { return td->JobUID; } From b228ae46cd3e03694c1174334a7fdf72bce6af22 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 23:15:25 -0500 Subject: [PATCH 23/73] COMP: Remove invalid declaration from ctkDICOMScheduler.cpp --- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index e18b09aac4..3c197a9e05 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -632,8 +632,6 @@ void ctkDICOMScheduler::waitForDone(int msec) QCoreApplication::processEvents(); d->ThreadPool->waitForDone(msec); } - -Q_INVOKABLE void waitForFinish(); void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, const QStringList &studyInstanceUIDs, const QStringList &seriesInstanceUIDs, From fbda0f1d9c71018e81b04fc49afc261a73d44a7d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 02:51:38 -0500 Subject: [PATCH 24/73] COMP: Fix clazy-range-loop-detach warning in ctkDICOMJobResponseSet --- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index e5ab7fa680..170f5dd304 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -294,7 +294,7 @@ QSharedPointer ctkDICOMJobResponseSet::datasetShared() const void ctkDICOMJobResponseSet::setDatasets(QMap dcmItems, bool takeOwnership) { Q_D(ctkDICOMJobResponseSet); - for(QString key : dcmItems.keys()) + for(const QString& key : dcmItems.keys()) { DcmItem *dcmItem = dcmItems.value(key); if (!dcmItem) @@ -316,7 +316,7 @@ QMap ctkDICOMJobResponseSet::datasets() const Q_D(const ctkDICOMJobResponseSet); QMap datasets; - for(QString key : d->Datasets.keys()) + for(const QString& key : d->Datasets.keys()) { QSharedPointer dcmItem = d->Datasets.value(key); if (!dcmItem) @@ -361,7 +361,7 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) d->Datasets.clear(); QMap nodeDatasets = node->datasets(); - for(QString key : nodeDatasets.keys()) + for(const QString& key : nodeDatasets.keys()) { ctkDICOMItem* nodeDataset = nodeDatasets.value(key); if (!nodeDataset) From df9faf8b562823911e503848a43fea3e4c7beb5e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 23:43:50 -0500 Subject: [PATCH 25/73] COMP: Explicitly exclude abstract job/scheduler/worker from wrapping --- Libs/Core/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Libs/Core/CMakeLists.txt b/Libs/Core/CMakeLists.txt index f88a2bfb28..8375a1d0fe 100644 --- a/Libs/Core/CMakeLists.txt +++ b/Libs/Core/CMakeLists.txt @@ -131,6 +131,14 @@ set(KIT_MOC_SRCS ctkWorkflowTransitions.h ) +# Abstract class should not be wrapped ! +set_source_files_properties( + ctkAbstractJob.h + ctkAbstractScheduler.h + ctkAbstractWorker.h + WRAP_EXCLUDE + ) + # UI files set(KIT_UI_FORMS ) From 316980c9e96b65cc54184b9557a36223b5842a18 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 19:31:21 -0500 Subject: [PATCH 26/73] COMP: Manage jobs and workers as abstract objects Remove redefinition of "ctkDICOMJob::createWorker" pure virtual method relying on the fact ctkDICOMJob and ctkAbtractJob are covariant (see [1]) and managed jobs and workers as abstract object anticipating the integration of ctkAbstractSchedulerPrivate holding the collection of worker and job. Move abstract definition of "loggerReport" from ctkDICOMJob to ctkAbtractJob Add "jobToDetail" virtual function to ctkAbstractScheduler [1]: https://en.wikipedia.org/wiki/Covariant_return_type --- Libs/Core/ctkAbstractJob.h | 3 + Libs/Core/ctkAbstractScheduler.cpp | 19 ++++ Libs/Core/ctkAbstractScheduler.h | 4 + Libs/DICOM/Core/ctkDICOMJob.h | 6 -- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 129 ++++++++++++++++---------- Libs/DICOM/Core/ctkDICOMScheduler.h | 11 ++- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 12 +-- 7 files changed, 118 insertions(+), 66 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index bbc14e15f2..a3c143fee7 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -118,6 +118,9 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject /// Generate worker for job Q_INVOKABLE virtual ctkAbstractWorker* createWorker() = 0; + /// Logger report string formatting for specific job + Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; + Q_SIGNALS: void started(); void finished(); diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index f3b9e25b63..20e9d79335 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -21,8 +21,13 @@ =========================================================================*/ +// CTK includes +#include "ctkAbstractJob.h" #include "ctkAbstractScheduler.h" +//--------------------------------------------------------------------------- +// ctkAbstractScheduler methods + // -------------------------------------------------------------------------- ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) : QObject(parent) @@ -31,3 +36,17 @@ ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) // -------------------------------------------------------------------------- ctkAbstractScheduler::~ctkAbstractScheduler() = default; + +//---------------------------------------------------------------------------- +QVariant ctkAbstractScheduler::jobToDetail(ctkAbstractJob* job) +{ + if (!job) + { + return QVariant(); + } + + QVariant data; + data.setValue(job->jobUID()); + + return data; +} diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index fbc6de7301..233052576d 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -30,6 +30,7 @@ // CTK includes #include "ctkCoreExport.h" +class ctkAbstractJob; //------------------------------------------------------------------------------ /// \ingroup Core @@ -40,6 +41,9 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject explicit ctkAbstractScheduler(QObject* parent = 0); virtual ~ctkAbstractScheduler(); + /// Utility method to transform/pass informations between threads by qt signals + Q_INVOKABLE virtual QVariant jobToDetail(ctkAbstractJob* job); + Q_SIGNALS: void jobStarted(QVariant data); void jobFinished(QVariant data); diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index e6e7ef4640..1095b8cfb6 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -99,15 +99,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob void copyJobResponseSets(QList> jobResponseSets); ///@} - /// Logger report string formatting for specific task - Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; - /// Create a copy of the object Q_INVOKABLE virtual ctkDICOMJob* generateCopy() const = 0; - /// Generate worker for job - Q_INVOKABLE virtual ctkDICOMWorker* createWorker() = 0; - Q_SIGNALS: void progressJobDetail(QVariant); void finishedJobDetail(QVariant); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 3c197a9e05..c371077af4 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -31,7 +31,6 @@ #include "ctkDICOMServer.h" #include "ctkDICOMStorageListenerJob.h" #include "ctkDICOMUtil.h" -#include "ctkDICOMWorker.h" #include "ctkLogger.h" // dcmtk includes @@ -65,10 +64,10 @@ ctkDICOMSchedulerPrivate::~ctkDICOMSchedulerPrivate() } //------------------------------------------------------------------------------ -int ctkDICOMSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) +int ctkDICOMSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) { int count = 0; - foreach (QSharedPointer queuedJob, this->JobsQueue) + foreach (QSharedPointer queuedJob, this->JobsQueue) { if (queuedJob->jobUID() == job->jobUID()) { @@ -87,7 +86,7 @@ int ctkDICOMSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedP } //------------------------------------------------------------------------------ -void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) +void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) { Q_Q(ctkDICOMScheduler); @@ -122,7 +121,7 @@ void ctkDICOMSchedulerPrivate::removeJob(const QString& jobUID) .arg(jobUID) .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); - QSharedPointer job = this->JobsQueue.value(jobUID); + QSharedPointer job = this->JobsQueue.value(jobUID); if (!job) { return; @@ -654,7 +653,7 @@ void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, d->ThreadPool->waitForDone(300); wait = false; - foreach (QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (!job) { @@ -665,11 +664,16 @@ void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, { continue; } + ctkDICOMJob* dicomJob = qobject_cast(job.data()); + if (!dicomJob) + { + continue; + } - if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || - (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || - (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || - (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + if ((!dicomJob->patientID().isEmpty() && patientIDs.contains(dicomJob->patientID())) || + (!dicomJob->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(dicomJob->studyInstanceUID())) || + (!dicomJob->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(dicomJob->seriesInstanceUID())) || + (!dicomJob->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(dicomJob->sopInstanceUID()))) { if (job->status() != ctkAbstractJob::JobStatus::Finished) { @@ -695,7 +699,7 @@ int ctkDICOMScheduler::numberOfPersistentJobs() Q_D(ctkDICOMScheduler); int cont = 0; QMutexLocker ml(&d->mMutex); - foreach (QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (job->isPersistent()) { @@ -707,11 +711,11 @@ int ctkDICOMScheduler::numberOfPersistentJobs() } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::addJob(ctkDICOMJob *job) +void ctkDICOMScheduler::addJob(ctkAbstractJob *job) { Q_D(ctkDICOMScheduler); - QSharedPointer jobShared = QSharedPointer(job); + QSharedPointer jobShared = QSharedPointer(job); d->insertJob(jobShared); } @@ -727,7 +731,7 @@ void ctkDICOMScheduler::deleteWorker(const QString& jobUID) { Q_D(ctkDICOMScheduler); - QMap>::iterator it = d->Workers.find(jobUID); + QMap>::iterator it = d->Workers.find(jobUID); if (it == d->Workers.end()) { return; @@ -737,12 +741,12 @@ void ctkDICOMScheduler::deleteWorker(const QString& jobUID) } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMScheduler::getJobSharedByUID(const QString& jobUID) +QSharedPointer ctkDICOMScheduler::getJobSharedByUID(const QString& jobUID) { Q_D(ctkDICOMScheduler); QMutexLocker ml(&d->mMutex); - QMap>::iterator it = d->JobsQueue.find(jobUID); + QMap>::iterator it = d->JobsQueue.find(jobUID); if (it == d->JobsQueue.end()) { return nullptr; @@ -752,9 +756,9 @@ QSharedPointer ctkDICOMScheduler::getJobSharedByUID(const QString& } //---------------------------------------------------------------------------- -ctkDICOMJob *ctkDICOMScheduler::getJobByUID(const QString& jobUID) +ctkAbstractJob *ctkDICOMScheduler::getJobByUID(const QString& jobUID) { - QSharedPointer job = this->getJobSharedByUID(jobUID); + QSharedPointer job = this->getJobSharedByUID(jobUID); if (!job) { return nullptr; @@ -771,7 +775,7 @@ void ctkDICOMScheduler::stopAllJobs(bool stopPersistentJobs) QMutexLocker ml(&d->mMutex); // Stops jobs without a worker (in waiting) - foreach (QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (job->isPersistent() && !stopPersistentJobs) { @@ -788,7 +792,7 @@ void ctkDICOMScheduler::stopAllJobs(bool stopPersistentJobs) } // Stops queued and running jobs - foreach (QSharedPointer worker, d->Workers) + foreach (QSharedPointer worker, d->Workers) { QSharedPointer job = worker->jobShared(); if (job->isPersistent() && !stopPersistentJobs) @@ -826,7 +830,7 @@ void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, QMutexLocker ml(&d->mMutex); // Stops jobs without a worker (in waiting) - foreach (QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (!job) { @@ -843,10 +847,17 @@ void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, continue; } - if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || - (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || - (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || - (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + ctkDICOMJob* dicomJob = qobject_cast(job.data()); + if (!dicomJob) + { + qCritical() << Q_FUNC_INFO << " failed: unexpected type of job"; + continue; + } + + if ((!dicomJob->patientID().isEmpty() && patientIDs.contains(dicomJob->patientID())) || + (!dicomJob->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(dicomJob->studyInstanceUID())) || + (!dicomJob->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(dicomJob->seriesInstanceUID())) || + (!dicomJob->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(dicomJob->sopInstanceUID()))) { job->setStatus(ctkAbstractJob::JobStatus::Stopped); this->deleteJob(job->jobUID()); @@ -854,10 +865,10 @@ void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, } // Stops queued and running jobs - foreach (QSharedPointer worker, d->Workers) + foreach (QSharedPointer worker, d->Workers) { - QSharedPointer job = - qobject_cast>(worker->jobShared()); + QSharedPointer job = + qobject_cast>(worker->jobShared()); if (!job) { continue; @@ -868,10 +879,17 @@ void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, continue; } - if ((!job->patientID().isEmpty() && patientIDs.contains(job->patientID())) || - (!job->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(job->studyInstanceUID())) || - (!job->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(job->seriesInstanceUID())) || - (!job->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(job->sopInstanceUID()))) + ctkDICOMJob* dicomJob = qobject_cast(job.data()); + if (!dicomJob) + { + qCritical() << Q_FUNC_INFO << " failed: unexpected type of job"; + continue; + } + + if ((!dicomJob->patientID().isEmpty() && patientIDs.contains(dicomJob->patientID())) || + (!dicomJob->studyInstanceUID().isEmpty() && studyInstanceUIDs.contains(dicomJob->studyInstanceUID())) || + (!dicomJob->seriesInstanceUID().isEmpty() && seriesInstanceUIDs.contains(dicomJob->seriesInstanceUID())) || + (!dicomJob->sopInstanceUID().isEmpty() && sopInstanceUIDs.contains(dicomJob->sopInstanceUID()))) { job->setStatus(ctkAbstractJob::JobStatus::Stopped); worker->cancel(); @@ -891,14 +909,21 @@ void ctkDICOMScheduler::raiseJobsPriorityForSeries(const QStringList& selectedSe } QMutexLocker ml(&d->mMutex); - foreach (QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (job->isPersistent()) { continue; } - if (!selectedSeriesInstanceUIDs.contains(job->seriesInstanceUID())) + ctkDICOMJob* dicomJob = qobject_cast(job.data()); + if (!dicomJob) + { + qCritical() << Q_FUNC_INFO << " failed: unexpected type of job"; + continue; + } + + if (!selectedSeriesInstanceUIDs.contains(dicomJob->seriesInstanceUID())) { priority = QThread::Priority::LowPriority; } @@ -969,7 +994,7 @@ ctkDICOMStorageListenerJob *ctkDICOMScheduler::listenerJob() { Q_D(ctkDICOMScheduler); QMutexLocker ml(&d->mMutex); - foreach(QSharedPointer job, d->JobsQueue) + foreach(QSharedPointer job, d->JobsQueue) { QSharedPointer listenerJob = qobject_cast>(job); @@ -1009,16 +1034,23 @@ QSharedPointer ctkDICOMScheduler::threadPoolShared() const } //---------------------------------------------------------------------------- -QVariant ctkDICOMScheduler::jobToDetail(ctkDICOMJob* job) +QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) { + ctkDICOMJob* dicomJob = qobject_cast(job); + if (!dicomJob) + { + qCritical() << Q_FUNC_INFO << " failed: unexpected type of job"; + return QVariant(); + } + ctkDICOMJobDetail td; td.JobClass = job->className(); - td.DICOMLevel = job->dicomLevel(); td.JobUID = job->jobUID(); - td.PatientID = job->patientID(); - td.StudyInstanceUID = job->studyInstanceUID(); - td.SeriesInstanceUID = job->seriesInstanceUID(); - td.SOPInstanceUID = job->sopInstanceUID(); + td.DICOMLevel = dicomJob->dicomLevel(); + td.PatientID = dicomJob->patientID(); + td.StudyInstanceUID = dicomJob->studyInstanceUID(); + td.SeriesInstanceUID = dicomJob->seriesInstanceUID(); + td.SOPInstanceUID = dicomJob->sopInstanceUID(); ctkDICOMQueryJob* queryJob = qobject_cast(job); if (queryJob && queryJob->server()) { @@ -1039,7 +1071,7 @@ QVariant ctkDICOMScheduler::jobToDetail(ctkDICOMJob* job) //---------------------------------------------------------------------------- void ctkDICOMScheduler::onJobStarted() { - ctkDICOMJob* job = qobject_cast(this->sender()); + ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) { return; @@ -1052,12 +1084,11 @@ void ctkDICOMScheduler::onJobStarted() //---------------------------------------------------------------------------- void ctkDICOMScheduler::onJobCanceled() { - ctkDICOMJob* job = qobject_cast(this->sender()); + ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) { return; } - logger.debug(job->loggerReport("canceled")); emit this->jobCanceled(this->jobToDetail(job)); } @@ -1065,7 +1096,7 @@ void ctkDICOMScheduler::onJobCanceled() //---------------------------------------------------------------------------- void ctkDICOMScheduler::onJobFailed() { - ctkDICOMJob* job = qobject_cast(this->sender()); + ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) { return; @@ -1084,7 +1115,7 @@ void ctkDICOMScheduler::onJobFailed() //---------------------------------------------------------------------------- void ctkDICOMScheduler::onJobFinished() { - ctkDICOMJob* job = qobject_cast(this->sender()); + ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) { return; @@ -1115,7 +1146,7 @@ void ctkDICOMScheduler::onQueueJobsInThreadPool() QMutexLocker ml(&d->mMutex); foreach (QThread::Priority priority, threadPriorityEnums) { - foreach(QSharedPointer job, d->JobsQueue) + foreach(QSharedPointer job, d->JobsQueue) { if (job->priority() != priority) { @@ -1139,8 +1170,8 @@ void ctkDICOMScheduler::onQueueJobsInThreadPool() job->setStatus(ctkAbstractJob::JobStatus::Queued); - QSharedPointer worker = - QSharedPointer(job->createWorker()); + QSharedPointer worker = + QSharedPointer(job->createWorker()); worker->setScheduler(*this); d->Workers.insert(job->jobUID(), worker); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 590a63f23e..781f6083f9 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -35,6 +35,7 @@ #include "ctkDICOMCoreExport.h" #include "ctkDICOMDatabase.h" +class ctkAbstractJob; class ctkDICOMJob; class ctkDICOMIndexer; class ctkDICOMSchedulerPrivate; @@ -165,11 +166,11 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler /// Jobs managment Q_INVOKABLE int numberOfJobs(); Q_INVOKABLE int numberOfPersistentJobs(); - Q_INVOKABLE void addJob(ctkDICOMJob* job); + Q_INVOKABLE void addJob(ctkAbstractJob* job); Q_INVOKABLE void deleteJob(const QString& jobUID); Q_INVOKABLE void deleteWorker(const QString& jobUID); - QSharedPointer getJobSharedByUID(const QString& jobUID); - Q_INVOKABLE ctkDICOMJob* getJobByUID(const QString& jobUID); + QSharedPointer getJobSharedByUID(const QString& jobUID); + Q_INVOKABLE ctkAbstractJob* getJobByUID(const QString& jobUID); Q_INVOKABLE void waitForFinish(); Q_INVOKABLE void waitForDone(int msec); Q_INVOKABLE void waitForFinishByUIDs(const QStringList& patientIDs = {}, @@ -227,8 +228,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler QSharedPointer threadPoolShared() const; ///@} - /// Utility method to transform/pass informations between threads by qt signals - Q_INVOKABLE QVariant jobToDetail(ctkDICOMJob* job); + /// Utility method to transform/pass informations between threads by Qt signals + Q_INVOKABLE QVariant jobToDetail(ctkAbstractJob* job) override; Q_SIGNALS: void queueJobs(); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index aff83a5caa..fe3722665d 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -27,8 +27,8 @@ // ctkDICOMCore includes #include "ctkDICOMScheduler.h" -class ctkDICOMWorker; -class ctkDICOMJob; +class ctkAbstractWorker; +class ctkAbstractJob; struct ThumbnailUID { @@ -50,8 +50,8 @@ class ctkDICOMSchedulerPrivate : public QObject ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj); virtual ~ctkDICOMSchedulerPrivate(); - int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); - void insertJob(QSharedPointer job); + int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); + void insertJob(QSharedPointer job); void removeJob(const QString& jobUID); QString generateUniqueJobUID(); ctkDICOMServer* getServerFromProxyServersByConnectionName(const QString&); @@ -59,8 +59,8 @@ class ctkDICOMSchedulerPrivate : public QObject QSharedPointer DicomDatabase; QSharedPointer ThreadPool; QList> Servers; - QMap> JobsQueue; - QMap> Workers; + QMap> JobsQueue; + QMap> Workers; QMap Filters; QMutex mMutex; int RetryDelay; From c53acee56c0760317edeb8f4c2d01f5f877d0761 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 23:14:16 -0500 Subject: [PATCH 27/73] COMP: Relocate onJob from DICOM to Abstract scheduler --- Libs/Core/ctkAbstractScheduler.cpp | 67 +++++++++++++++++++++++++++ Libs/Core/ctkAbstractScheduler.h | 14 ++++-- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 67 ++------------------------- Libs/DICOM/Core/ctkDICOMScheduler.h | 8 +--- 4 files changed, 82 insertions(+), 74 deletions(-) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index 20e9d79335..0ed8c51842 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -24,6 +24,10 @@ // CTK includes #include "ctkAbstractJob.h" #include "ctkAbstractScheduler.h" +#include "ctkLogger.h" + +static ctkLogger logger ("org.commontk.core.AbstractScheduler"); + //--------------------------------------------------------------------------- // ctkAbstractScheduler methods @@ -50,3 +54,66 @@ QVariant ctkAbstractScheduler::jobToDetail(ctkAbstractJob* job) return data; } + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::onJobStarted() +{ + ctkAbstractJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("started")); + emit this->jobStarted(this->jobToDetail(job)); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::onJobCanceled() +{ + ctkAbstractJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + logger.debug(job->loggerReport("canceled")); + emit this->jobCanceled(this->jobToDetail(job)); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::onJobFailed() +{ + ctkAbstractJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("failed")); + + QVariant data = this->jobToDetail(job); + QString jobUID = job->jobUID(); + this->deleteWorker(jobUID); + this->deleteJob(jobUID); + + emit this->jobFailed(data); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::onJobFinished() +{ + ctkAbstractJob* job = qobject_cast(this->sender()); + if (!job) + { + return; + } + + logger.debug(job->loggerReport("finished")); + + QVariant data = this->jobToDetail(job); + QString jobUID = job->jobUID(); + this->deleteWorker(jobUID); + this->deleteJob(jobUID); + + emit this->jobFinished(data); +} diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index 233052576d..e253b35734 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -41,6 +41,12 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject explicit ctkAbstractScheduler(QObject* parent = 0); virtual ~ctkAbstractScheduler(); + ///@{ + /// Jobs managment + Q_INVOKABLE virtual void deleteJob(const QString& jobUID) = 0; + Q_INVOKABLE virtual void deleteWorker(const QString& jobUID) = 0; + ///@} + /// Utility method to transform/pass informations between threads by qt signals Q_INVOKABLE virtual QVariant jobToDetail(ctkAbstractJob* job); @@ -51,10 +57,10 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject void jobFailed(QVariant data); public Q_SLOTS: - virtual void onJobStarted() = 0; - virtual void onJobFinished() = 0; - virtual void onJobCanceled() = 0; - virtual void onJobFailed() = 0; + virtual void onJobStarted(); + virtual void onJobFinished(); + virtual void onJobCanceled(); + virtual void onJobFailed(); private: Q_DISABLE_COPY(ctkAbstractScheduler) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index c371077af4..c21e965d56 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -21,6 +21,9 @@ =========================================================================*/ +// ctkCore includes +#include "ctkLogger.h" + // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" #include "ctkDICOMJobResponseSet.h" @@ -31,7 +34,6 @@ #include "ctkDICOMServer.h" #include "ctkDICOMStorageListenerJob.h" #include "ctkDICOMUtil.h" -#include "ctkLogger.h" // dcmtk includes #include @@ -1068,69 +1070,6 @@ QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) return data; } -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::onJobStarted() -{ - ctkAbstractJob* job = qobject_cast(this->sender()); - if (!job) - { - return; - } - - logger.debug(job->loggerReport("started")); - emit this->jobStarted(this->jobToDetail(job)); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::onJobCanceled() -{ - ctkAbstractJob* job = qobject_cast(this->sender()); - if (!job) - { - return; - } - logger.debug(job->loggerReport("canceled")); - emit this->jobCanceled(this->jobToDetail(job)); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::onJobFailed() -{ - ctkAbstractJob* job = qobject_cast(this->sender()); - if (!job) - { - return; - } - - logger.debug(job->loggerReport("failed")); - - QVariant data = this->jobToDetail(job); - QString jobUID = job->jobUID(); - this->deleteWorker(jobUID); - this->deleteJob(jobUID); - - emit this->jobFailed(data); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::onJobFinished() -{ - ctkAbstractJob* job = qobject_cast(this->sender()); - if (!job) - { - return; - } - - logger.debug(job->loggerReport("finished")); - - QVariant data = this->jobToDetail(job); - QString jobUID = job->jobUID(); - this->deleteWorker(jobUID); - this->deleteJob(jobUID); - - emit this->jobFinished(data); -} - //---------------------------------------------------------------------------- void ctkDICOMScheduler::onQueueJobsInThreadPool() { diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 781f6083f9..e930488a45 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -167,8 +167,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE int numberOfJobs(); Q_INVOKABLE int numberOfPersistentJobs(); Q_INVOKABLE void addJob(ctkAbstractJob* job); - Q_INVOKABLE void deleteJob(const QString& jobUID); - Q_INVOKABLE void deleteWorker(const QString& jobUID); + Q_INVOKABLE void deleteJob(const QString& jobUID) override; + Q_INVOKABLE void deleteWorker(const QString& jobUID) override; QSharedPointer getJobSharedByUID(const QString& jobUID); Q_INVOKABLE ctkAbstractJob* getJobByUID(const QString& jobUID); Q_INVOKABLE void waitForFinish(); @@ -236,10 +236,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler void progressJobDetail(QVariant data); public Q_SLOTS: - void onJobStarted() override; - void onJobCanceled() override; - void onJobFailed() override; - void onJobFinished() override; void onQueueJobsInThreadPool(); protected: From 37725646278a1a976765098ec45af31c3c6b985e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 16 Jan 2024 23:57:15 -0500 Subject: [PATCH 28/73] COMP: Relocate Workers and JobsQueue map to abstract scheduler --- Libs/Core/CMakeLists.txt | 3 + Libs/Core/ctkAbstractScheduler.cpp | 375 ++++++++++++++++++++++++++ Libs/Core/ctkAbstractScheduler.h | 58 +++- Libs/Core/ctkAbstractScheduler_p.h | 68 +++++ Libs/DICOM/Core/ctkDICOMScheduler.cpp | 359 +----------------------- Libs/DICOM/Core/ctkDICOMScheduler.h | 49 +--- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 21 +- 7 files changed, 522 insertions(+), 411 deletions(-) create mode 100644 Libs/Core/ctkAbstractScheduler_p.h diff --git a/Libs/Core/CMakeLists.txt b/Libs/Core/CMakeLists.txt index 8375a1d0fe..a2cc3d10f3 100644 --- a/Libs/Core/CMakeLists.txt +++ b/Libs/Core/CMakeLists.txt @@ -34,6 +34,7 @@ set(KIT_SRCS ctkAbstractLibraryFactory.tpp ctkAbstractScheduler.cpp ctkAbstractScheduler.h + ctkAbstractScheduler_p.h ctkAbstractWorker.cpp ctkAbstractWorker.h ctkBackTrace.cpp @@ -108,6 +109,7 @@ endif() set(KIT_MOC_SRCS ctkAbstractJob.h ctkAbstractScheduler.h + ctkAbstractScheduler_p.h ctkAbstractWorker.h ctkBooleanMapper.h ctkCallback.h @@ -135,6 +137,7 @@ set(KIT_MOC_SRCS set_source_files_properties( ctkAbstractJob.h ctkAbstractScheduler.h + ctkAbstractScheduler_p.h ctkAbstractWorker.h WRAP_EXCLUDE ) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index 0ed8c51842..d394fbc021 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -21,13 +21,126 @@ =========================================================================*/ +// Qt includes +#include +#include +#include +#include +#include + // CTK includes #include "ctkAbstractJob.h" #include "ctkAbstractScheduler.h" +#include "ctkAbstractScheduler_p.h" +#include "ctkAbstractWorker.h" #include "ctkLogger.h" static ctkLogger logger ("org.commontk.core.AbstractScheduler"); +// -------------------------------------------------------------------------- +// ctkAbstractSchedulerPrivate methods + +// -------------------------------------------------------------------------- +ctkAbstractSchedulerPrivate::ctkAbstractSchedulerPrivate(ctkAbstractScheduler& object) + : q_ptr(&object) +{ +} + +// -------------------------------------------------------------------------- +ctkAbstractSchedulerPrivate::~ctkAbstractSchedulerPrivate() = default; + +//--------------------------------------------------------------------------- +void ctkAbstractSchedulerPrivate::init() +{ + Q_Q(ctkAbstractScheduler); + + QObject::connect(q, SIGNAL(queueJobs()), + q, SLOT(onQueueJobsInThreadPool()), + Qt::QueuedConnection); + + this->ThreadPool = QSharedPointer (new QThreadPool()); + this->ThreadPool->setMaxThreadCount(20); +} + +//------------------------------------------------------------------------------ +void ctkAbstractSchedulerPrivate::insertJob(QSharedPointer job) +{ + Q_Q(ctkAbstractScheduler); + + if (!job) + { + return; + } + + logger.debug(QString("ctkAbstractScheduler: creating job object %1 of type %2 in thread %3.\n") + .arg(job->jobUID()) + .arg(job->className()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + + QObject::connect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); + QObject::connect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); + QObject::connect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); + QObject::connect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); + QObject::connect(job.data(), SIGNAL(progressJobDetail(QVariant)), + q, SIGNAL(progressJobDetail(QVariant))); + + QMutexLocker ml(&this->mMutex); + this->JobsQueue.insert(job->jobUID(), job); + emit q->queueJobs(); +} + +//------------------------------------------------------------------------------ +void ctkAbstractSchedulerPrivate::removeJob(const QString& jobUID) +{ + Q_Q(ctkAbstractScheduler); + + logger.debug(QString("ctkAbstractScheduler: deleting job object %1 in thread %2.\n") + .arg(jobUID) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); + + QSharedPointer job = this->JobsQueue.value(jobUID); + if (!job) + { + return; + } + + QObject::disconnect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); + QObject::disconnect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); + QObject::disconnect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); + QObject::disconnect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); + QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), q, SIGNAL(progressJobDetail(QVariant))); + + this->JobsQueue.remove(jobUID); + emit q->queueJobs(); +} + +//------------------------------------------------------------------------------ +int ctkAbstractSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) +{ + int count = 0; + foreach (QSharedPointer queuedJob, this->JobsQueue) + { + if (queuedJob->jobUID() == job->jobUID()) + { + continue; + } + + if ((queuedJob->status() == ctkAbstractJob::JobStatus::Queued || + queuedJob->status() == ctkAbstractJob::JobStatus::Running) && + queuedJob->className() == job->className()) + { + count++; + } + } + + return count; +} + +//------------------------------------------------------------------------------ +QString ctkAbstractSchedulerPrivate::generateUniqueJobUID() +{ + return QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); +} //--------------------------------------------------------------------------- // ctkAbstractScheduler methods @@ -35,12 +148,227 @@ static ctkLogger logger ("org.commontk.core.AbstractScheduler"); // -------------------------------------------------------------------------- ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) : QObject(parent) + , d_ptr(new ctkAbstractSchedulerPrivate(*this)) { + Q_D(ctkAbstractScheduler); + d->init(); +} + +// -------------------------------------------------------------------------- +ctkAbstractScheduler::ctkAbstractScheduler(ctkAbstractSchedulerPrivate* pimpl, QObject* parent) + : Superclass(parent) + , d_ptr(pimpl) +{ + // derived classes must call init manually. Calling init() here may results in + // actions on a derived public class not yet finished to be created } // -------------------------------------------------------------------------- ctkAbstractScheduler::~ctkAbstractScheduler() = default; +//---------------------------------------------------------------------------- +int ctkAbstractScheduler::numberOfJobs() +{ + Q_D(ctkAbstractScheduler); + QMutexLocker ml(&d->mMutex); + return d->JobsQueue.count(); +} + +//---------------------------------------------------------------------------- +int ctkAbstractScheduler::numberOfPersistentJobs() +{ + Q_D(ctkAbstractScheduler); + int cont = 0; + QMutexLocker ml(&d->mMutex); + foreach (QSharedPointer job, d->JobsQueue) + { + if (job->isPersistent()) + { + cont++; + } + } + return cont; +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::addJob(ctkAbstractJob *job) +{ + Q_D(ctkAbstractScheduler); + + QSharedPointer jobShared = QSharedPointer(job); + d->insertJob(jobShared); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::deleteJob(const QString& jobUID) +{ + Q_D(ctkAbstractScheduler); + d->removeJob(jobUID); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::deleteWorker(const QString& jobUID) +{ + Q_D(ctkAbstractScheduler); + + QMap>::iterator it = d->Workers.find(jobUID); + if (it == d->Workers.end()) + { + return; + } + + d->Workers.remove(jobUID); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkAbstractScheduler::getJobSharedByUID(const QString& jobUID) +{ + Q_D(ctkAbstractScheduler); + + QMutexLocker ml(&d->mMutex); + QMap>::iterator it = d->JobsQueue.find(jobUID); + if (it == d->JobsQueue.end()) + { + return nullptr; + } + + return d->JobsQueue.value(jobUID); +} + +//---------------------------------------------------------------------------- +ctkAbstractJob *ctkAbstractScheduler::getJobByUID(const QString& jobUID) +{ + QSharedPointer job = this->getJobSharedByUID(jobUID); + if (!job) + { + return nullptr; + } + + return job.data(); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::waitForFinish() +{ + Q_D(ctkAbstractScheduler); + + int numberOfPersistentJobs = this->numberOfPersistentJobs(); + while(this->numberOfJobs() > numberOfPersistentJobs) + { + QCoreApplication::processEvents(); + d->ThreadPool->waitForDone(300); + } +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::waitForDone(int msec) +{ + Q_D(ctkAbstractScheduler); + + QCoreApplication::processEvents(); + d->ThreadPool->waitForDone(msec); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::stopAllJobs(bool stopPersistentJobs) +{ + Q_D(ctkAbstractScheduler); + + QMutexLocker ml(&d->mMutex); + + // Stops jobs without a worker (in waiting) + foreach (QSharedPointer job, d->JobsQueue) + { + if (job->isPersistent() && !stopPersistentJobs) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Initialized) + { + continue; + } + + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + this->deleteJob(job->jobUID()); + } + + // Stops queued and running jobs + foreach (QSharedPointer worker, d->Workers) + { + QSharedPointer job = worker->jobShared(); + if (job->isPersistent() && !stopPersistentJobs) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Running && + job->status() != ctkAbstractJob::JobStatus::Queued) + { + continue; + } + + job->setStatus(ctkAbstractJob::JobStatus::Stopped); + worker->cancel(); + } +} + +//---------------------------------------------------------------------------- +int ctkAbstractScheduler::maximumThreadCount() const +{ + Q_D(const ctkAbstractScheduler); + return d->ThreadPool->maxThreadCount(); +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::setMaximumThreadCount(const int &maximumThreadCount) +{ + Q_D(ctkAbstractScheduler); + d->ThreadPool->setMaxThreadCount(maximumThreadCount); +} + +//---------------------------------------------------------------------------- +int ctkAbstractScheduler::maximumNumberOfRetry() const +{ + Q_D(const ctkAbstractScheduler); + return d->MaximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) +{ + Q_D(ctkAbstractScheduler); + d->MaximumNumberOfRetry = maximumNumberOfRetry; +} + +//---------------------------------------------------------------------------- +int ctkAbstractScheduler::retryDelay() const +{ + Q_D(const ctkAbstractScheduler); + return d->RetryDelay; +} + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::setRetryDelay(const int &retryDelay) +{ + Q_D(ctkAbstractScheduler); + d->RetryDelay = retryDelay; +} + +//---------------------------------------------------------------------------- +QThreadPool *ctkAbstractScheduler::threadPool() const +{ + Q_D(const ctkAbstractScheduler); + return d->ThreadPool.data(); +} + +//---------------------------------------------------------------------------- +QSharedPointer ctkAbstractScheduler::threadPoolShared() const +{ + Q_D(const ctkAbstractScheduler); + return d->ThreadPool; +} + //---------------------------------------------------------------------------- QVariant ctkAbstractScheduler::jobToDetail(ctkAbstractJob* job) { @@ -117,3 +445,50 @@ void ctkAbstractScheduler::onJobFinished() emit this->jobFinished(data); } + +//---------------------------------------------------------------------------- +void ctkAbstractScheduler::onQueueJobsInThreadPool() +{ + Q_D(ctkAbstractScheduler); + + QMutexLocker ml(&d->mMutex); + foreach (QThread::Priority priority, (QList() + << QThread::Priority::HighestPriority + << QThread::Priority::HighPriority + << QThread::Priority::NormalPriority + << QThread::Priority::LowPriority + << QThread::Priority::LowestPriority)) + { + foreach(QSharedPointer job, d->JobsQueue) + { + if (job->priority() != priority) + { + continue; + } + + if (job->status() != ctkAbstractJob::JobStatus::Initialized) + { + continue; + } + + int numberOfRunningJobsWithSameType = d->getSameTypeJobsInThreadPoolQueueOrRunning(job); + if (numberOfRunningJobsWithSameType >= job->maximumConcurrentJobsPerType()) + { + continue; + } + + logger.debug(QString("ctkDICOMScheduler: creating worker for job %1 in thread %2.\n") + .arg(job->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + + job->setStatus(ctkAbstractJob::JobStatus::Queued); + + QSharedPointer worker = + QSharedPointer(job->createWorker()); + worker->setScheduler(*this); + + d->Workers.insert(job->jobUID(), worker); + d->ThreadPool->start(worker.data(), job->priority()); + } + } +} diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index e253b35734..d5ae3c09e8 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -26,11 +26,14 @@ // Qt includes #include +#include #include +class QThreadPool; // CTK includes #include "ctkCoreExport.h" class ctkAbstractJob; +class ctkAbstractSchedulerPrivate; //------------------------------------------------------------------------------ /// \ingroup Core @@ -38,15 +41,57 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject { Q_OBJECT public: + typedef QObject Superclass; explicit ctkAbstractScheduler(QObject* parent = 0); virtual ~ctkAbstractScheduler(); ///@{ /// Jobs managment - Q_INVOKABLE virtual void deleteJob(const QString& jobUID) = 0; - Q_INVOKABLE virtual void deleteWorker(const QString& jobUID) = 0; + Q_INVOKABLE int numberOfJobs(); + Q_INVOKABLE int numberOfPersistentJobs(); + + Q_INVOKABLE void addJob(ctkAbstractJob* job); + + Q_INVOKABLE virtual void deleteJob(const QString& jobUID); + Q_INVOKABLE virtual void deleteWorker(const QString& jobUID); + + QSharedPointer getJobSharedByUID(const QString& jobUID); + Q_INVOKABLE ctkAbstractJob* getJobByUID(const QString& jobUID); + + Q_INVOKABLE void waitForFinish(); + Q_INVOKABLE void waitForDone(int msec); + + Q_INVOKABLE void stopAllJobs(bool stopPersistentJobs = false); + ///@} + + ///@{ + /// Maximum number of concurrent QThreads spawned by the threadPool in the Job pool + /// default: 20 + int maximumThreadCount() const; + void setMaximumThreadCount(const int& maximumThreadCount); + ///@} + + ///@{ + /// Maximum number of retries that the Job pool will try on each failed Job + /// default: 3 + int maximumNumberOfRetry() const; + void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + ///@} + + ///@{ + /// Retry delay in millisec + /// default: 100 msec + int retryDelay() const; + void setRetryDelay(const int& retryDelay); ///@} + /// Return the threadPool. + Q_INVOKABLE QThreadPool* threadPool() const; + + /// Return threadPool as a shared pointer + /// (not Python-wrappable). + QSharedPointer threadPoolShared() const; + /// Utility method to transform/pass informations between threads by qt signals Q_INVOKABLE virtual QVariant jobToDetail(ctkAbstractJob* job); @@ -55,16 +100,23 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject void jobFinished(QVariant data); void jobCanceled(QVariant data); void jobFailed(QVariant data); + void queueJobs(); + void progressJobDetail(QVariant data); public Q_SLOTS: virtual void onJobStarted(); virtual void onJobFinished(); virtual void onJobCanceled(); virtual void onJobFailed(); + virtual void onQueueJobsInThreadPool(); + +protected: + QScopedPointer d_ptr; + ctkAbstractScheduler(ctkAbstractSchedulerPrivate* pimpl, QObject* parent); private: + Q_DECLARE_PRIVATE(ctkAbstractScheduler); Q_DISABLE_COPY(ctkAbstractScheduler) }; - #endif // ctkAbstractScheduler_h diff --git a/Libs/Core/ctkAbstractScheduler_p.h b/Libs/Core/ctkAbstractScheduler_p.h new file mode 100644 index 0000000000..5c38a85a84 --- /dev/null +++ b/Libs/Core/ctkAbstractScheduler_p.h @@ -0,0 +1,68 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkAbstractSchedulerPrivate_h +#define __ctkAbstractSchedulerPrivate_h + +// Qt includes +#include +#include +class QThreadPool; + +// ctkCore includes +#include "ctkCoreExport.h" +class ctkAbstractJob; +class ctkAbstractWorker; + +// ctkDICOMCore includes +#include "ctkAbstractScheduler.h" + +//------------------------------------------------------------------------------ +class CTK_CORE_EXPORT ctkAbstractSchedulerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkAbstractScheduler) + +protected: + ctkAbstractScheduler* const q_ptr; + +public: + ctkAbstractSchedulerPrivate(ctkAbstractScheduler& object); + virtual ~ctkAbstractSchedulerPrivate(); + + /// Convenient setup methods + virtual void init(); + + virtual void insertJob(QSharedPointer job); + virtual void removeJob(const QString& jobUID); + int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); + QString generateUniqueJobUID(); + + QMutex mMutex; + + int RetryDelay{100}; + int MaximumNumberOfRetry{3}; + + QSharedPointer ThreadPool; + QMap> JobsQueue; + QMap> Workers; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index c21e965d56..eab5b87dc0 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -23,6 +23,7 @@ // ctkCore includes #include "ctkLogger.h" +#include "ctkAbstractScheduler_p.h" // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" @@ -46,16 +47,9 @@ static ctkLogger logger ( "org.commontk.dicom.DICOMJobPool" ); //------------------------------------------------------------------------------ ctkDICOMSchedulerPrivate::ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj) - : q_ptr(&obj) + : ctkAbstractSchedulerPrivate(obj) { ctk::setDICOMLogLevel(ctkErrorLogLevel::Warning); - - this->DicomDatabase = nullptr; - this->ThreadPool = QSharedPointer (new QThreadPool()); - this->ThreadPool->setMaxThreadCount(20); - this->RetryDelay = 100; - this->MaximumNumberOfRetry = 3; - this->MaximumPatientsQuery = 25; } //------------------------------------------------------------------------------ @@ -65,86 +59,6 @@ ctkDICOMSchedulerPrivate::~ctkDICOMSchedulerPrivate() q->removeAllServers(); } -//------------------------------------------------------------------------------ -int ctkDICOMSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) -{ - int count = 0; - foreach (QSharedPointer queuedJob, this->JobsQueue) - { - if (queuedJob->jobUID() == job->jobUID()) - { - continue; - } - - if ((queuedJob->status() == ctkAbstractJob::JobStatus::Queued || - queuedJob->status() == ctkAbstractJob::JobStatus::Running) && - queuedJob->className() == job->className()) - { - count++; - } - } - - return count; -} - -//------------------------------------------------------------------------------ -void ctkDICOMSchedulerPrivate::insertJob(QSharedPointer job) -{ - Q_Q(ctkDICOMScheduler); - - if (!job) - { - return; - } - - logger.debug(QString("ctkDICOMScheduler: creating job object %1 of type %2 in thread %3.\n") - .arg(job->jobUID()) - .arg(job->className()) - .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); - - QObject::connect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); - QObject::connect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); - QObject::connect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); - QObject::connect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); - QObject::connect(job.data(), SIGNAL(progressJobDetail(QVariant)), - q, SIGNAL(progressJobDetail(QVariant))); - - QMutexLocker ml(&this->mMutex); - this->JobsQueue.insert(job->jobUID(), job); - emit q->queueJobs(); -} - -//------------------------------------------------------------------------------ -void ctkDICOMSchedulerPrivate::removeJob(const QString& jobUID) -{ - Q_Q(ctkDICOMScheduler); - - logger.debug(QString("ctkDICOMScheduler: deleting job object %1 in thread %2.\n") - .arg(jobUID) - .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); - - QSharedPointer job = this->JobsQueue.value(jobUID); - if (!job) - { - return; - } - - QObject::disconnect(job.data(), SIGNAL(started()), q, SLOT(onJobStarted())); - QObject::disconnect(job.data(), SIGNAL(canceled()), q, SLOT(onJobCanceled())); - QObject::disconnect(job.data(), SIGNAL(failed()), q, SLOT(onJobFailed())); - QObject::disconnect(job.data(), SIGNAL(finished()), q, SLOT(onJobFinished())); - QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), q, SIGNAL(progressJobDetail(QVariant))); - - this->JobsQueue.remove(jobUID); - emit q->queueJobs(); -} - -//------------------------------------------------------------------------------ -QString ctkDICOMSchedulerPrivate::generateUniqueJobUID() -{ - return QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); -} - //------------------------------------------------------------------------------ ctkDICOMServer *ctkDICOMSchedulerPrivate::getServerFromProxyServersByConnectionName(const QString &connectionName) { @@ -165,12 +79,18 @@ ctkDICOMServer *ctkDICOMSchedulerPrivate::getServerFromProxyServersByConnectionN //------------------------------------------------------------------------------ ctkDICOMScheduler::ctkDICOMScheduler(QObject* parentObject) - : Superclass(parentObject) - , d_ptr(new ctkDICOMSchedulerPrivate(*this)) + : Superclass(new ctkDICOMSchedulerPrivate(*this), parentObject) { - QObject::connect(this, SIGNAL(queueJobs()), - this, SLOT(onQueueJobsInThreadPool()), - Qt::QueuedConnection); + Q_D(ctkDICOMScheduler); + d->init(); +} + +// -------------------------------------------------------------------------- +ctkDICOMScheduler::ctkDICOMScheduler(ctkDICOMSchedulerPrivate* pimpl, QObject* parentObject) + : Superclass(pimpl, parentObject) +{ + // derived classes must call init manually. Calling init() here may results in + // actions on a derived public class not yet finished to be created } //------------------------------------------------------------------------------ @@ -613,26 +533,6 @@ int ctkDICOMScheduler::getServerIndexFromName(const QString& connectionName) } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::waitForFinish() -{ - Q_D(ctkDICOMScheduler); - - int numberOfPersistentJobs = this->numberOfPersistentJobs(); - while(this->numberOfJobs() > numberOfPersistentJobs) - { - QCoreApplication::processEvents(); - d->ThreadPool->waitForDone(300); - } -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::waitForDone(int msec) -{ - Q_D(ctkDICOMScheduler); - - QCoreApplication::processEvents(); - d->ThreadPool->waitForDone(msec); -} void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, const QStringList &studyInstanceUIDs, const QStringList &seriesInstanceUIDs, @@ -687,132 +587,6 @@ void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, } } -//---------------------------------------------------------------------------- -int ctkDICOMScheduler::numberOfJobs() -{ - Q_D(ctkDICOMScheduler); - QMutexLocker ml(&d->mMutex); - return d->JobsQueue.count(); -} - -//---------------------------------------------------------------------------- -int ctkDICOMScheduler::numberOfPersistentJobs() -{ - Q_D(ctkDICOMScheduler); - int cont = 0; - QMutexLocker ml(&d->mMutex); - foreach (QSharedPointer job, d->JobsQueue) - { - if (job->isPersistent()) - { - cont++; - } - } - - return cont; -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::addJob(ctkAbstractJob *job) -{ - Q_D(ctkDICOMScheduler); - - QSharedPointer jobShared = QSharedPointer(job); - d->insertJob(jobShared); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::deleteJob(const QString& jobUID) -{ - Q_D(ctkDICOMScheduler); - d->removeJob(jobUID); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::deleteWorker(const QString& jobUID) -{ - Q_D(ctkDICOMScheduler); - - QMap>::iterator it = d->Workers.find(jobUID); - if (it == d->Workers.end()) - { - return; - } - - d->Workers.remove(jobUID); -} - -//---------------------------------------------------------------------------- -QSharedPointer ctkDICOMScheduler::getJobSharedByUID(const QString& jobUID) -{ - Q_D(ctkDICOMScheduler); - - QMutexLocker ml(&d->mMutex); - QMap>::iterator it = d->JobsQueue.find(jobUID); - if (it == d->JobsQueue.end()) - { - return nullptr; - } - - return d->JobsQueue.value(jobUID); -} - -//---------------------------------------------------------------------------- -ctkAbstractJob *ctkDICOMScheduler::getJobByUID(const QString& jobUID) -{ - QSharedPointer job = this->getJobSharedByUID(jobUID); - if (!job) - { - return nullptr; - } - - return job.data(); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::stopAllJobs(bool stopPersistentJobs) -{ - Q_D(ctkDICOMScheduler); - - QMutexLocker ml(&d->mMutex); - - // Stops jobs without a worker (in waiting) - foreach (QSharedPointer job, d->JobsQueue) - { - if (job->isPersistent() && !stopPersistentJobs) - { - continue; - } - - if (job->status() != ctkAbstractJob::JobStatus::Initialized) - { - continue; - } - - job->setStatus(ctkAbstractJob::JobStatus::Stopped); - this->deleteJob(job->jobUID()); - } - - // Stops queued and running jobs - foreach (QSharedPointer worker, d->Workers) - { - QSharedPointer job = worker->jobShared(); - if (job->isPersistent() && !stopPersistentJobs) - { - continue; - } - - if (job->status() != ctkAbstractJob::JobStatus::Running && - job->status() != ctkAbstractJob::JobStatus::Queued) - { - continue; - } - - job->setStatus(ctkAbstractJob::JobStatus::Stopped); - worker->cancel(); - } -} - //---------------------------------------------------------------------------- void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, const QStringList& studyInstanceUIDs, @@ -934,49 +708,6 @@ void ctkDICOMScheduler::raiseJobsPriorityForSeries(const QStringList& selectedSe } } -//---------------------------------------------------------------------------- -int ctkDICOMScheduler::maximumThreadCount() const -{ - Q_D(const ctkDICOMScheduler); - return d->ThreadPool->maxThreadCount(); -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::setMaximumThreadCount(const int &maximumThreadCount) -{ - Q_D(ctkDICOMScheduler); - - d->ThreadPool->setMaxThreadCount(maximumThreadCount); -} - -//---------------------------------------------------------------------------- -int ctkDICOMScheduler::maximumNumberOfRetry() const -{ - Q_D(const ctkDICOMScheduler); - return d->MaximumNumberOfRetry; -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) -{ - Q_D(ctkDICOMScheduler); - d->MaximumNumberOfRetry = maximumNumberOfRetry; -} - -//---------------------------------------------------------------------------- -int ctkDICOMScheduler::retryDelay() const -{ - Q_D(const ctkDICOMScheduler); - return d->RetryDelay; -} - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::setRetryDelay(const int &retryDelay) -{ - Q_D(ctkDICOMScheduler); - d->RetryDelay = retryDelay; -} - //------------------------------------------------------------------------------ void ctkDICOMScheduler::setMaximumPatientsQuery(const int maximumPatientsQuery) { @@ -1021,20 +752,6 @@ bool ctkDICOMScheduler::isStorageListenerActive() return false; } -//---------------------------------------------------------------------------- -QThreadPool *ctkDICOMScheduler::threadPool() const -{ - Q_D(const ctkDICOMScheduler); - return d->ThreadPool.data(); -} - -//---------------------------------------------------------------------------- -QSharedPointer ctkDICOMScheduler::threadPoolShared() const -{ - Q_D(const ctkDICOMScheduler); - return d->ThreadPool; -} - //---------------------------------------------------------------------------- QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) { @@ -1053,6 +770,7 @@ QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) td.StudyInstanceUID = dicomJob->studyInstanceUID(); td.SeriesInstanceUID = dicomJob->seriesInstanceUID(); td.SOPInstanceUID = dicomJob->sopInstanceUID(); + ctkDICOMQueryJob* queryJob = qobject_cast(job); if (queryJob && queryJob->server()) { @@ -1069,52 +787,3 @@ QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) return data; } - -//---------------------------------------------------------------------------- -void ctkDICOMScheduler::onQueueJobsInThreadPool() -{ - Q_D(ctkDICOMScheduler); - - QList threadPriorityEnums; - threadPriorityEnums.append(QThread::Priority::HighestPriority); - threadPriorityEnums.append(QThread::Priority::HighPriority); - threadPriorityEnums.append(QThread::Priority::NormalPriority); - threadPriorityEnums.append(QThread::Priority::LowPriority); - threadPriorityEnums.append(QThread::Priority::LowestPriority); - - QMutexLocker ml(&d->mMutex); - foreach (QThread::Priority priority, threadPriorityEnums) - { - foreach(QSharedPointer job, d->JobsQueue) - { - if (job->priority() != priority) - { - continue; - } - - if (job->status() != ctkAbstractJob::JobStatus::Initialized) - { - continue; - } - - int numberOfRunningJobsWithSameType = d->getSameTypeJobsInThreadPoolQueueOrRunning(job); - if (numberOfRunningJobsWithSameType >= job->maximumConcurrentJobsPerType()) - { - continue; - } - - logger.debug(QString("ctkDICOMScheduler: creating worker for job %1 in thread %2.\n") - .arg(job->jobUID()) - .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); - - job->setStatus(ctkAbstractJob::JobStatus::Queued); - - QSharedPointer worker = - QSharedPointer(job->createWorker()); - worker->setScheduler(*this); - - d->Workers.insert(job->jobUID(), worker); - d->ThreadPool->start(worker.data(), job->priority()); - } - } -} diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index e930488a45..9a96e0f06f 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -124,7 +124,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler /// (not Python-wrappable). void setDicomDatabase(QSharedPointer dicomDatabase); - ///@{ /// Filters are keyword/value pairs as generated by /// the ctkDICOMWidgets in a human readable (and editable) @@ -164,20 +163,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler ///@{ /// Jobs managment - Q_INVOKABLE int numberOfJobs(); - Q_INVOKABLE int numberOfPersistentJobs(); - Q_INVOKABLE void addJob(ctkAbstractJob* job); - Q_INVOKABLE void deleteJob(const QString& jobUID) override; - Q_INVOKABLE void deleteWorker(const QString& jobUID) override; - QSharedPointer getJobSharedByUID(const QString& jobUID); - Q_INVOKABLE ctkAbstractJob* getJobByUID(const QString& jobUID); - Q_INVOKABLE void waitForFinish(); - Q_INVOKABLE void waitForDone(int msec); Q_INVOKABLE void waitForFinishByUIDs(const QStringList& patientIDs = {}, const QStringList& studyInstanceUIDs = {}, const QStringList& seriesInstanceUIDs = {}, const QStringList& sopInstanceUIDs = {}); - Q_INVOKABLE void stopAllJobs(bool stopPersistentJobs = false); Q_INVOKABLE void stopJobsByUIDs(const QStringList& patientIDs = {}, const QStringList& studyInstanceUIDs = {}, const QStringList& seriesInstanceUIDs = {}, @@ -186,27 +175,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler QThread::Priority priority = QThread::HighestPriority); ///@} - ///@{ - /// Maximum number of concurrent QThreads spawned by the threadPool in the Job pool - /// default: 20 - int maximumThreadCount() const; - void setMaximumThreadCount(const int& maximumThreadCount); - ///@} - - ///@{ - /// Maximum number of retries that the Job pool will try on each failed Job - /// default: 3 - int maximumNumberOfRetry() const; - void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); - ///@} - - ///@{ - /// Retry delay in millisec - /// default: 100 msec - int retryDelay() const; - void setRetryDelay(const int& retryDelay); - ///@} - ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. @@ -220,26 +188,11 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE bool isStorageListenerActive(); ///@} - ///@{ - /// Return the threadPool. - Q_INVOKABLE QThreadPool* threadPool() const; - /// Return threadPool as a shared pointer - /// (not Python-wrappable). - QSharedPointer threadPoolShared() const; - ///@} - /// Utility method to transform/pass informations between threads by Qt signals Q_INVOKABLE QVariant jobToDetail(ctkAbstractJob* job) override; -Q_SIGNALS: - void queueJobs(); - void progressJobDetail(QVariant data); - -public Q_SLOTS: - void onQueueJobsInThreadPool(); - protected: - QScopedPointer d_ptr; + ctkDICOMScheduler(ctkDICOMSchedulerPrivate* pimpl, QObject* parent); private: Q_DECLARE_PRIVATE(ctkDICOMScheduler); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index fe3722665d..c20904ac4f 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -24,6 +24,9 @@ #ifndef __ctkDICOMQueryJobPrivate_h #define __ctkDICOMQueryJobPrivate_h +// ctkCore includes +#include "ctkAbstractScheduler_p.h" + // ctkDICOMCore includes #include "ctkDICOMScheduler.h" @@ -38,34 +41,22 @@ struct ThumbnailUID } ; //------------------------------------------------------------------------------ -class ctkDICOMSchedulerPrivate : public QObject +class ctkDICOMSchedulerPrivate : public ctkAbstractSchedulerPrivate { Q_OBJECT Q_DECLARE_PUBLIC(ctkDICOMScheduler); -protected: - ctkDICOMScheduler* const q_ptr; - public: ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj); virtual ~ctkDICOMSchedulerPrivate(); - int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); - void insertJob(QSharedPointer job); - void removeJob(const QString& jobUID); - QString generateUniqueJobUID(); ctkDICOMServer* getServerFromProxyServersByConnectionName(const QString&); QSharedPointer DicomDatabase; - QSharedPointer ThreadPool; QList> Servers; - QMap> JobsQueue; - QMap> Workers; QMap Filters; - QMutex mMutex; - int RetryDelay; - int MaximumNumberOfRetry; - int MaximumPatientsQuery; + + int MaximumPatientsQuery{25}; }; #endif From a5b8baf8e73145cd70dcb2ba8fe58154a7e7e699 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 00:54:39 -0500 Subject: [PATCH 29/73] COMP: Simplify initialization of ctkDICOMJobDetails --- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 99d17ab1e6..cd838158bf 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -154,28 +154,20 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject }; struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail { - explicit ctkDICOMJobDetail(): - JobType(ctkDICOMJobResponseSet::JobType::None), - JobClass(""), - DICOMLevel(ctkDICOMJob::DICOMLevels::Patients), - JobUID(""), - PatientID(""), - StudyInstanceUID(""), - SeriesInstanceUID(""), - SOPInstanceUID(""), - ConnectionName(""), - NumberOfDataSets(0){} + explicit ctkDICOMJobDetail(){} virtual ~ctkDICOMJobDetail(){}; - ctkDICOMJobResponseSet::JobType JobType; + ctkDICOMJobResponseSet::JobType JobType{ctkDICOMJobResponseSet::JobType::None}; QString JobClass; - ctkDICOMJob::DICOMLevels DICOMLevel; + ctkDICOMJob::DICOMLevels DICOMLevel{ctkDICOMJob::DICOMLevels::Patients}; QString JobUID; QString PatientID; QString StudyInstanceUID; QString SeriesInstanceUID; QString SOPInstanceUID; QString ConnectionName; - int NumberOfDataSets; -}; Q_DECLARE_METATYPE(ctkDICOMJobDetail); + int NumberOfDataSets{0}; +}; +Q_DECLARE_METATYPE(ctkDICOMJobDetail); + #endif From efad66157d5a908869b713f354dbdfffabe3b8cb Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 01:24:35 -0500 Subject: [PATCH 30/73] COMP: Add ctkJobDetail --- Libs/Core/ctkAbstractJob.h | 8 ++++++ Libs/Core/ctkAbstractScheduler.cpp | 6 ++++- Libs/Core/ctkCorePythonQtDecorators.h | 27 +++++++++++++++++++ .../Core/ctkDICOMCorePythonQtDecorators.h | 20 ++------------ Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 8 +++--- 5 files changed, 45 insertions(+), 24 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index a3c143fee7..a4c19efc29 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -141,5 +141,13 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject Q_DISABLE_COPY(ctkAbstractJob) }; +struct CTK_CORE_EXPORT ctkJobDetail { + explicit ctkJobDetail(){} + virtual ~ctkJobDetail() = default; + + QString JobClass; + QString JobUID; +}; +Q_DECLARE_METATYPE(ctkJobDetail); #endif // ctkAbstractJob_h diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index d394fbc021..febf70019f 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -377,8 +377,12 @@ QVariant ctkAbstractScheduler::jobToDetail(ctkAbstractJob* job) return QVariant(); } + ctkJobDetail td; + td.JobClass = job->className(); + td.JobUID = job->jobUID(); + QVariant data; - data.setValue(job->jobUID()); + data.setValue(td); return data; } diff --git a/Libs/Core/ctkCorePythonQtDecorators.h b/Libs/Core/ctkCorePythonQtDecorators.h index a1b4ac6322..b55faab737 100644 --- a/Libs/Core/ctkCorePythonQtDecorators.h +++ b/Libs/Core/ctkCorePythonQtDecorators.h @@ -25,6 +25,7 @@ #include // CTK includes +#include // For ctkJobDetail #include #include #include @@ -48,6 +49,7 @@ class ctkCorePythonQtDecorators : public QObject { PythonQt::self()->registerClass(&ctkBooleanMapper::staticMetaObject, "CTKCore"); PythonQt::self()->registerCPPClass("ctkErrorLogContext", 0, "CTKCore"); + PythonQt::self()->registerCPPClass("ctkJobDetail", 0, "CTKCore"); PythonQt::self()->registerCPPClass("ctkWorkflowStep", 0, "CTKCore"); PythonQt::self()->registerClass(&ctkWorkflowInterstepTransition::staticMetaObject, "CTKCore"); } @@ -233,6 +235,31 @@ public Q_SLOTS: return context->Message; } + // + // ctkJobDetail + // + ctkJobDetail* new_ctkJobDetail() + { + return new ctkJobDetail(); + } + + void setJobClass(ctkJobDetail* td, const QString& jobClass) + { + td->JobClass = jobClass; + } + QString jobClass(ctkJobDetail* td) + { + return td->JobClass; + } + + void setJobUID(ctkJobDetail* td, const QString& jobUID) + { + td->JobUID = jobUID; + } + QString JobUID(ctkJobDetail* td) + { + return td->JobUID; + } }; //----------------------------------------------------------------------------- diff --git a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h index 28b46c3098..ac9c7de9bf 100644 --- a/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h +++ b/Libs/DICOM/Core/ctkDICOMCorePythonQtDecorators.h @@ -45,6 +45,8 @@ class ctkDICOMCorePythonQtDecorators : public QObject ctkDICOMCorePythonQtDecorators() { PythonQt::self()->registerCPPClass("ctkDICOMJobDetail", 0, "CTKDICOMCore"); + PythonQt::self()->addParentClass("ctkDICOMJobDetail", "ctkJobDetail", + PythonQtUpcastingOffset()); } public slots: @@ -57,15 +59,6 @@ public slots: return new ctkDICOMJobDetail(); } - void setJobClass(ctkDICOMJobDetail* td, const QString& jobClass) - { - td->JobClass = jobClass; - } - QString jobClass(ctkDICOMJobDetail* td) - { - return td->JobClass; - } - void setJobType(ctkDICOMJobDetail* td, ctkDICOMJobResponseSet::JobType jobType) { td->JobType = jobType; @@ -75,15 +68,6 @@ public slots: return td->JobType; } - void setJobUID(ctkDICOMJobDetail* td, const QString& jobUID) - { - td->JobUID = jobUID; - } - QString jobUID(ctkDICOMJobDetail* td) - { - return td->JobUID; - } - void setPatientID(ctkDICOMJobDetail* td, const QString& patientID) { td->PatientID = patientID; diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index cd838158bf..119bbbeca9 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -153,14 +153,12 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_DISABLE_COPY(ctkDICOMJobResponseSet); }; -struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail { - explicit ctkDICOMJobDetail(){} - virtual ~ctkDICOMJobDetail(){}; +struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail { + explicit ctkDICOMJobDetail() : ctkJobDetail(){} + virtual ~ctkDICOMJobDetail() = default; ctkDICOMJobResponseSet::JobType JobType{ctkDICOMJobResponseSet::JobType::None}; - QString JobClass; ctkDICOMJob::DICOMLevels DICOMLevel{ctkDICOMJob::DICOMLevels::Patients}; - QString JobUID; QString PatientID; QString StudyInstanceUID; QString SeriesInstanceUID; From 868bfb99d463dba8baa141ca833722d78411fd9f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 01:57:08 -0500 Subject: [PATCH 31/73] COMP: Rename "jobResponseSetToDetail()" to "toVariant()" --- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 7 +++++-- Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 2bbaee26be..612cc4ce47 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -111,7 +111,7 @@ void ctkDICOMInserterWorker::run() foreach (QSharedPointer jobResponseSet, jobResponseSets) { - emit inserterJob->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + emit inserterJob->progressJobDetail(jobResponseSet->toVariant()); } inserterJob->setStatus(ctkAbstractJob::JobStatus::Finished); diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 170f5dd304..9c5fb22840 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -391,7 +391,7 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) } //------------------------------------------------------------------------------ -QVariant ctkDICOMJobResponseSet::jobResponseSetToDetail() +QVariant ctkDICOMJobResponseSet::toVariant() { ctkDICOMJobDetail td; td.JobType = this->jobType(); diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 119bbbeca9..499a129d95 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -142,8 +142,11 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject /// Copy object Q_INVOKABLE void deepCopy(ctkDICOMJobResponseSet* node); - /// Utility method to transform/pass informations between threads by qt signals - Q_INVOKABLE QVariant jobResponseSetToDetail(); + /// Return the QVariant value of this JobResponseSet. + /// + /// The value is set using the ctkDICOMJobDetail metatype and is used to pass + /// information between threads using Qt signals. + Q_INVOKABLE QVariant toVariant(); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index 4295cc3f5b..a231413769 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -126,7 +126,7 @@ class ctkDICOMRetrieveSCUPrivate : public DcmSCU // i.e. a slot in ctkDICOMRetrieveWorker with a counter. When the counter > batchLimit -> insert if (this->retrieve->getLastRetrieveType() == ctkDICOMRetrieve::RetrieveType::RetrieveSeries) { - emit this->retrieve->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + emit this->retrieve->progressJobDetail(jobResponseSet->toVariant()); } this->retrieve->addJobResponseSet(jobResponseSet); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index 7fe8c585af..cc79a6c3b6 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -122,7 +122,7 @@ class ctkDICOMStorageListenerSCUPrivate : public DcmStorageSCP this->listener->addJobResponseSet(jobResponseSet); - emit this->listener->progressJobDetail(jobResponseSet->jobResponseSetToDetail()); + emit this->listener->progressJobDetail(jobResponseSet->toVariant()); } // send C-STORE response (with DIMSE status code) if (status.good()) From ac8c843959191a43ad82657b3bb662ef944590b0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 02:51:00 -0500 Subject: [PATCH 32/73] COMP: Remove scheduler "jobToDetail" and add "toVariant()" job function --- Libs/Core/ctkAbstractJob.cpp | 5 +++ Libs/Core/ctkAbstractJob.h | 15 +++++++++ Libs/Core/ctkAbstractScheduler.cpp | 26 +++------------ Libs/Core/ctkAbstractScheduler.h | 3 -- Libs/DICOM/Core/ctkDICOMJob.cpp | 6 ++++ Libs/DICOM/Core/ctkDICOMJob.h | 7 ++++ Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 15 +-------- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 37 ++++++++++++++++++++-- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 7 ++++ Libs/DICOM/Core/ctkDICOMQueryJob.h | 7 ++++ Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 7 ++++ Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 7 ++++ Libs/DICOM/Core/ctkDICOMScheduler.cpp | 36 --------------------- Libs/DICOM/Core/ctkDICOMScheduler.h | 3 -- 14 files changed, 100 insertions(+), 81 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.cpp b/Libs/Core/ctkAbstractJob.cpp index c30039fb18..87d72a6b80 100644 --- a/Libs/Core/ctkAbstractJob.cpp +++ b/Libs/Core/ctkAbstractJob.cpp @@ -150,3 +150,8 @@ void ctkAbstractJob::setPriority(const QThread::Priority &priority) this->Priority = priority; } +//---------------------------------------------------------------------------- +QVariant ctkAbstractJob::toVariant() +{ + return QVariant::fromValue(ctkJobDetail(*this)); +} diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index a4c19efc29..0bc08c6d77 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -27,6 +27,7 @@ // Qt includes #include #include +#include // CTK includes #include "ctkCoreExport.h" @@ -121,6 +122,13 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject /// Logger report string formatting for specific job Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; + /// Return the QVariant value of this job. + /// + /// The value is set using the ctkJobDetail metatype and is used to pass + /// information between threads using Qt signals. + /// \sa ctkJobDetail + Q_INVOKABLE virtual QVariant toVariant(); + Q_SIGNALS: void started(); void finished(); @@ -141,8 +149,15 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject Q_DISABLE_COPY(ctkAbstractJob) }; +//------------------------------------------------------------------------------ +/// \ingroup Core struct CTK_CORE_EXPORT ctkJobDetail { explicit ctkJobDetail(){} + explicit ctkJobDetail(const ctkAbstractJob& job) + { + this->JobClass = job.className(); + this->JobUID = job.jobUID(); + } virtual ~ctkJobDetail() = default; QString JobClass; diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index febf70019f..7b96f7d6cc 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -369,24 +369,6 @@ QSharedPointer ctkAbstractScheduler::threadPoolShared() const return d->ThreadPool; } -//---------------------------------------------------------------------------- -QVariant ctkAbstractScheduler::jobToDetail(ctkAbstractJob* job) -{ - if (!job) - { - return QVariant(); - } - - ctkJobDetail td; - td.JobClass = job->className(); - td.JobUID = job->jobUID(); - - QVariant data; - data.setValue(td); - - return data; -} - //---------------------------------------------------------------------------- void ctkAbstractScheduler::onJobStarted() { @@ -397,7 +379,7 @@ void ctkAbstractScheduler::onJobStarted() } logger.debug(job->loggerReport("started")); - emit this->jobStarted(this->jobToDetail(job)); + emit this->jobStarted(job->toVariant()); } //---------------------------------------------------------------------------- @@ -409,7 +391,7 @@ void ctkAbstractScheduler::onJobCanceled() return; } logger.debug(job->loggerReport("canceled")); - emit this->jobCanceled(this->jobToDetail(job)); + emit this->jobCanceled(job->toVariant()); } //---------------------------------------------------------------------------- @@ -423,7 +405,7 @@ void ctkAbstractScheduler::onJobFailed() logger.debug(job->loggerReport("failed")); - QVariant data = this->jobToDetail(job); + QVariant data = job->toVariant(); QString jobUID = job->jobUID(); this->deleteWorker(jobUID); this->deleteJob(jobUID); @@ -442,7 +424,7 @@ void ctkAbstractScheduler::onJobFinished() logger.debug(job->loggerReport("finished")); - QVariant data = this->jobToDetail(job); + QVariant data = job->toVariant(); QString jobUID = job->jobUID(); this->deleteWorker(jobUID); this->deleteJob(jobUID); diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index d5ae3c09e8..0b60e80bfa 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -92,9 +92,6 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject /// (not Python-wrappable). QSharedPointer threadPoolShared() const; - /// Utility method to transform/pass informations between threads by qt signals - Q_INVOKABLE virtual QVariant jobToDetail(ctkAbstractJob* job); - Q_SIGNALS: void jobStarted(QVariant data); void jobFinished(QVariant data); diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index de71e42e92..e8ebdf7ca5 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -158,3 +158,9 @@ void ctkDICOMJob::copyJobResponseSets(QListJobResponseSets.append(jobResponseSetCopy); } } + +//------------------------------------------------------------------------------ +QVariant ctkDICOMJob::toVariant() +{ + return QVariant::fromValue(ctkDICOMJobDetail(*this)); +} diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 1095b8cfb6..87ec75548e 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -102,6 +102,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob /// Create a copy of the object Q_INVOKABLE virtual ctkDICOMJob* generateCopy() const = 0; + /// Return the QVariant value of this job. + /// + /// The value is set using the ctkDICOMJobDetail metatype and is used to pass + /// information between threads using Qt signals. + /// \sa ctkDICOMJobDetail + Q_INVOKABLE virtual QVariant toVariant() override; + Q_SIGNALS: void progressJobDetail(QVariant); void finishedJobDetail(QVariant); diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 9c5fb22840..6549613ead 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -393,18 +393,5 @@ void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) //------------------------------------------------------------------------------ QVariant ctkDICOMJobResponseSet::toVariant() { - ctkDICOMJobDetail td; - td.JobType = this->jobType(); - td.JobUID = this->jobUID(); - td.PatientID = this->patientID(); - td.StudyInstanceUID = this->studyInstanceUID(); - td.SeriesInstanceUID = this->seriesInstanceUID(); - td.SOPInstanceUID = this->sopInstanceUID(); - td.ConnectionName = this->connectionName(); - td.NumberOfDataSets = this->datasets().count(); - - QVariant data; - data.setValue(td); - - return data; + return QVariant::fromValue(ctkDICOMJobDetail(*this)); } diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 499a129d95..e66a0c4392 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -157,16 +157,47 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject }; struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail { - explicit ctkDICOMJobDetail() : ctkJobDetail(){} + explicit ctkDICOMJobDetail() = default; + + explicit ctkDICOMJobDetail(const ctkDICOMJob& job) : ctkJobDetail(job) + { + this->DICOMLevel = job.dicomLevel(); + this->PatientID = job.patientID(); + this->StudyInstanceUID = job.studyInstanceUID(); + this->SeriesInstanceUID = job.seriesInstanceUID(); + this->SOPInstanceUID = job.sopInstanceUID(); + } + + explicit ctkDICOMJobDetail(const ctkDICOMJob& job, const QString& connectionName) + : ctkDICOMJobDetail(job) + { + this->ConnectionName = connectionName; + } + + explicit ctkDICOMJobDetail(const ctkDICOMJobResponseSet& responseSet) + { + this->PatientID = responseSet.patientID(); + this->StudyInstanceUID = responseSet.studyInstanceUID(); + this->SeriesInstanceUID = responseSet.seriesInstanceUID(); + this->SOPInstanceUID = responseSet.sopInstanceUID(); + this->ConnectionName = responseSet.connectionName(); + this->NumberOfDataSets = responseSet.datasets().count(); + } virtual ~ctkDICOMJobDetail() = default; - ctkDICOMJobResponseSet::JobType JobType{ctkDICOMJobResponseSet::JobType::None}; - ctkDICOMJob::DICOMLevels DICOMLevel{ctkDICOMJob::DICOMLevels::Patients}; QString PatientID; QString StudyInstanceUID; QString SeriesInstanceUID; QString SOPInstanceUID; + + // Common to DICOM Query and Retrieve jobs, and DICOM JobResponseSet QString ConnectionName; + + // Specific to DICOM Query and Retrieve jobs + ctkDICOMJob::DICOMLevels DICOMLevel{ctkDICOMJob::DICOMLevels::Patients}; + + // Specific to DICOM JobResponseSet + ctkDICOMJobResponseSet::JobType JobType{ctkDICOMJobResponseSet::JobType::None}; int NumberOfDataSets{0}; }; Q_DECLARE_METATYPE(ctkDICOMJobDetail); diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index 0c9254dc89..a7927d6c5f 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -22,6 +22,7 @@ =========================================================================*/ // ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" // For ctkDICOMJobDetail #include "ctkDICOMQueryJob_p.h" #include "ctkDICOMQueryWorker.h" #include "ctkDICOMServer.h" @@ -204,3 +205,9 @@ ctkDICOMWorker *ctkDICOMQueryJob::createWorker() worker->setJob(*this); return worker; } + +//------------------------------------------------------------------------------ +QVariant ctkDICOMQueryJob::toVariant() +{ + return QVariant::fromValue(ctkDICOMJobDetail(*this, this->server()->connectionName())); +} diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index a426c8fa9e..1207e2b245 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -93,6 +93,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; + /// Return the QVariant value of this job. + /// + /// The value is set using the ctkDICOMJobDetail metatype and is used to pass + /// information between threads using Qt signals. + /// \sa ctkDICOMJobDetail + Q_INVOKABLE virtual QVariant toVariant() override; + protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index e7aa2a1404..4ff3b323de 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -22,6 +22,7 @@ =========================================================================*/ // ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" // For ctkDICOMJobDetail #include "ctkDICOMRetrieveJob_p.h" #include "ctkDICOMRetrieveWorker.h" #include "ctkDICOMServer.h" @@ -181,3 +182,9 @@ ctkDICOMWorker *ctkDICOMRetrieveJob::createWorker() worker->setJob(*this); return worker; } + +//------------------------------------------------------------------------------ +QVariant ctkDICOMRetrieveJob::toVariant() +{ + return QVariant::fromValue(ctkDICOMJobDetail(*this, this->server()->connectionName())); +} diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 15ccd559e0..36d0323959 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -62,6 +62,13 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; + /// Return the QVariant value of this job. + /// + /// The value is set using the ctkDICOMJobDetail metatype and is used to pass + /// information between threads using Qt signals. + /// \sa ctkDICOMJobDetail + Q_INVOKABLE virtual QVariant toVariant() override; + protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index eab5b87dc0..f3d23061d4 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -751,39 +751,3 @@ bool ctkDICOMScheduler::isStorageListenerActive() } return false; } - -//---------------------------------------------------------------------------- -QVariant ctkDICOMScheduler::jobToDetail(ctkAbstractJob* job) -{ - ctkDICOMJob* dicomJob = qobject_cast(job); - if (!dicomJob) - { - qCritical() << Q_FUNC_INFO << " failed: unexpected type of job"; - return QVariant(); - } - - ctkDICOMJobDetail td; - td.JobClass = job->className(); - td.JobUID = job->jobUID(); - td.DICOMLevel = dicomJob->dicomLevel(); - td.PatientID = dicomJob->patientID(); - td.StudyInstanceUID = dicomJob->studyInstanceUID(); - td.SeriesInstanceUID = dicomJob->seriesInstanceUID(); - td.SOPInstanceUID = dicomJob->sopInstanceUID(); - - ctkDICOMQueryJob* queryJob = qobject_cast(job); - if (queryJob && queryJob->server()) - { - td.ConnectionName = queryJob->server()->connectionName(); - } - ctkDICOMRetrieveJob* retrieveJob = qobject_cast(job); - if (retrieveJob && retrieveJob->server()) - { - td.ConnectionName = retrieveJob->server()->connectionName(); - } - - QVariant data; - data.setValue(td); - - return data; -} diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 9a96e0f06f..a56d54b089 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -188,9 +188,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE bool isStorageListenerActive(); ///@} - /// Utility method to transform/pass informations between threads by Qt signals - Q_INVOKABLE QVariant jobToDetail(ctkAbstractJob* job) override; - protected: ctkDICOMScheduler(ctkDICOMSchedulerPrivate* pimpl, QObject* parent); From b074c3e9c4f5fe532f391f27dd26c2fc25f9ac5d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 03:27:44 -0500 Subject: [PATCH 33/73] COMP: Move "generateCopy" from ctkDICOMJob to ctkAbstractJob --- Libs/Core/ctkAbstractJob.h | 3 +++ Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMInserterJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMJob.h | 3 --- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMQueryJob.h | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 6 ++---- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 2 +- 10 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index 0bc08c6d77..4a89b394c8 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -119,6 +119,9 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject /// Generate worker for job Q_INVOKABLE virtual ctkAbstractWorker* createWorker() = 0; + /// Create a copy of this object + Q_INVOKABLE virtual ctkAbstractJob* generateCopy() const = 0; + /// Logger report string formatting for specific job Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index e3a1fe0a11..b07017c736 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -128,7 +128,7 @@ QStringList ctkDICOMInserterJob::tagsToExcludeFromStorage() const } //------------------------------------------------------------------------------ -ctkDICOMJob* ctkDICOMInserterJob::generateCopy() const +ctkAbstractJob* ctkDICOMInserterJob::generateCopy() const { ctkDICOMInserterJob* newInserterJob = new ctkDICOMInserterJob; newInserterJob->setDICOMLevel(this->dicomLevel()); diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index 1e37df24dc..19fe8d1a1a 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -69,8 +69,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob QStringList tagsToExcludeFromStorage() const; ///}@ - /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const override; + /// Create a copy of this object + Q_INVOKABLE ctkAbstractJob* generateCopy() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 87ec75548e..5c6b6fec1d 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -99,9 +99,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob void copyJobResponseSets(QList> jobResponseSets); ///@} - /// Create a copy of the object - Q_INVOKABLE virtual ctkDICOMJob* generateCopy() const = 0; - /// Return the QVariant value of this job. /// /// The value is set using the ctkDICOMJobDetail metatype and is used to pass diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index a7927d6c5f..2d89ad1c77 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -176,7 +176,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const } } //------------------------------------------------------------------------------ -ctkDICOMJob* ctkDICOMQueryJob::generateCopy() const +ctkAbstractJob* ctkDICOMQueryJob::generateCopy() const { ctkDICOMQueryJob* newQueryJob = new ctkDICOMQueryJob; newQueryJob->setMaximumPatientsQuery(this->maximumConcurrentJobsPerType()); diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index 1207e2b245..b742984aa2 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -88,7 +88,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const override; + Q_INVOKABLE ctkAbstractJob* generateCopy() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 4ff3b323de..265ac33d68 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -155,7 +155,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const } } //------------------------------------------------------------------------------ -ctkDICOMJob* ctkDICOMRetrieveJob::generateCopy() const +ctkAbstractJob* ctkDICOMRetrieveJob::generateCopy() const { ctkDICOMRetrieveJob* newRetrieveJob = new ctkDICOMRetrieveJob; newRetrieveJob->setServer(this->serverShared()); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 36d0323959..0c93dad1f6 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -57,7 +57,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const override; + Q_INVOKABLE ctkAbstractJob* generateCopy() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index ed938341e4..f8675f9e30 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -113,10 +113,9 @@ QString ctkDICOMStorageListenerJob::loggerReport(const QString &status) const .arg(this->jobUID()); } //------------------------------------------------------------------------------ -ctkDICOMJob* ctkDICOMStorageListenerJob::generateCopy() const +ctkAbstractJob* ctkDICOMStorageListenerJob::generateCopy() const { - ctkDICOMStorageListenerJob* newListenerJob = - new ctkDICOMStorageListenerJob; + ctkDICOMStorageListenerJob* newListenerJob = new ctkDICOMStorageListenerJob; newListenerJob->setAETitle(this->AETitle()); newListenerJob->setPort(this->port()); newListenerJob->setConnectionTimeout(this->connectionTimeout()); @@ -127,7 +126,6 @@ ctkDICOMJob* ctkDICOMStorageListenerJob::generateCopy() const newListenerJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); newListenerJob->setPriority(this->priority()); - return newListenerJob; } diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index ae160aaa61..11b94e6f30 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -69,7 +69,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob Q_INVOKABLE QString loggerReport(const QString& status) const override; /// Create a copy of the object - Q_INVOKABLE ctkDICOMJob* generateCopy() const override; + Q_INVOKABLE ctkAbstractJob* generateCopy() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; From 3adca6fcfb0132d69354d45dc0e8fe2af2d3cc11 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 03:48:40 -0500 Subject: [PATCH 34/73] COMP: Rename ctkAbstractJob "generateCopy()" to "clone()" --- Libs/Core/ctkAbstractJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMInserterJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMQueryJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMWorker.cpp | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Libs/Core/ctkAbstractJob.h b/Libs/Core/ctkAbstractJob.h index 4a89b394c8..f6dd6a9141 100644 --- a/Libs/Core/ctkAbstractJob.h +++ b/Libs/Core/ctkAbstractJob.h @@ -119,8 +119,8 @@ class CTK_CORE_EXPORT ctkAbstractJob : public QObject /// Generate worker for job Q_INVOKABLE virtual ctkAbstractWorker* createWorker() = 0; - /// Create a copy of this object - Q_INVOKABLE virtual ctkAbstractJob* generateCopy() const = 0; + /// Create a copy of this job + Q_INVOKABLE virtual ctkAbstractJob* clone() const = 0; /// Logger report string formatting for specific job Q_INVOKABLE virtual QString loggerReport(const QString& status) const = 0; diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index b07017c736..21430d6440 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -128,7 +128,7 @@ QStringList ctkDICOMInserterJob::tagsToExcludeFromStorage() const } //------------------------------------------------------------------------------ -ctkAbstractJob* ctkDICOMInserterJob::generateCopy() const +ctkAbstractJob* ctkDICOMInserterJob::clone() const { ctkDICOMInserterJob* newInserterJob = new ctkDICOMInserterJob; newInserterJob->setDICOMLevel(this->dicomLevel()); diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index 19fe8d1a1a..db61ece98a 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -69,8 +69,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob QStringList tagsToExcludeFromStorage() const; ///}@ - /// Create a copy of this object - Q_INVOKABLE ctkAbstractJob* generateCopy() const override; + /// \see ctkAbstractJob::clone() + Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index 2d89ad1c77..f90802b337 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -176,7 +176,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const } } //------------------------------------------------------------------------------ -ctkAbstractJob* ctkDICOMQueryJob::generateCopy() const +ctkAbstractJob* ctkDICOMQueryJob::clone() const { ctkDICOMQueryJob* newQueryJob = new ctkDICOMQueryJob; newQueryJob->setMaximumPatientsQuery(this->maximumConcurrentJobsPerType()); diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index b742984aa2..ba2ca42fc4 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -87,8 +87,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const override; - /// Create a copy of the object - Q_INVOKABLE ctkAbstractJob* generateCopy() const override; + /// \see ctkAbstractJob::clone() + Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 265ac33d68..545ad51885 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -155,7 +155,7 @@ QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const } } //------------------------------------------------------------------------------ -ctkAbstractJob* ctkDICOMRetrieveJob::generateCopy() const +ctkAbstractJob* ctkDICOMRetrieveJob::clone() const { ctkDICOMRetrieveJob* newRetrieveJob = new ctkDICOMRetrieveJob; newRetrieveJob->setServer(this->serverShared()); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 0c93dad1f6..658521c8ed 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -56,8 +56,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const override; - /// Create a copy of the object - Q_INVOKABLE ctkAbstractJob* generateCopy() const override; + /// \see ctkAbstractJob::clone() + Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index f0c8708708..700f1315f4 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -266,7 +266,7 @@ void ctkDICOMRetrieveWorker::run() ctkDICOMServer* proxyServer = server->proxyServer(); if (proxyServer && proxyServer->queryRetrieveEnabled()) { - ctkDICOMRetrieveJob* newJob = qobject_cast(retrieveJob->generateCopy()); + ctkDICOMRetrieveJob* newJob = qobject_cast(retrieveJob->clone()); newJob->setRetryCounter(0); newJob->setServer(*proxyServer); scheduler->addJob(newJob); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index f8675f9e30..be2483cbc9 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -113,7 +113,7 @@ QString ctkDICOMStorageListenerJob::loggerReport(const QString &status) const .arg(this->jobUID()); } //------------------------------------------------------------------------------ -ctkAbstractJob* ctkDICOMStorageListenerJob::generateCopy() const +ctkAbstractJob* ctkDICOMStorageListenerJob::clone() const { ctkDICOMStorageListenerJob* newListenerJob = new ctkDICOMStorageListenerJob; newListenerJob->setAETitle(this->AETitle()); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 11b94e6f30..59e0906ebf 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -68,8 +68,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob /// Logger report string formatting for specific task Q_INVOKABLE QString loggerReport(const QString& status) const override; - /// Create a copy of the object - Q_INVOKABLE ctkAbstractJob* generateCopy() const override; + /// \see ctkAbstractJob::clone() + Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job Q_INVOKABLE ctkDICOMWorker* createWorker() override; diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp index b6dd093f06..72338540a1 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMWorker.cpp @@ -48,7 +48,7 @@ void ctkDICOMWorker::startNextJob() return; } - ctkDICOMJob* newJob = job->generateCopy(); + ctkDICOMJob* newJob = job->clone(); newJob->setRetryCounter(newJob->retryCounter() + 1); scheduler->addJob(newJob); } From 3147c375fb1b3ae75e7e4429145342d29ba4cdfc Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 17 Jan 2024 03:37:15 -0500 Subject: [PATCH 35/73] COMP: Move startNextJob/onJobCanceled to ctkAbstractWorker --- Libs/Core/ctkAbstractWorker.cpp | 51 ++++++++++++++++++++++++-- Libs/Core/ctkAbstractWorker.h | 4 +++ Libs/DICOM/Core/ctkDICOMWorker.cpp | 57 ------------------------------ Libs/DICOM/Core/ctkDICOMWorker.h | 4 --- 4 files changed, 53 insertions(+), 63 deletions(-) diff --git a/Libs/Core/ctkAbstractWorker.cpp b/Libs/Core/ctkAbstractWorker.cpp index c15f2ee090..154fb11207 100644 --- a/Libs/Core/ctkAbstractWorker.cpp +++ b/Libs/Core/ctkAbstractWorker.cpp @@ -21,6 +21,11 @@ =========================================================================*/ +// Qt includes +#include +#include + +// CTK includes #include "ctkAbstractJob.h" #include "ctkAbstractScheduler.h" #include "ctkAbstractWorker.h" @@ -28,8 +33,6 @@ // -------------------------------------------------------------------------- ctkAbstractWorker::ctkAbstractWorker() { - this->Job = nullptr; - this->Scheduler = nullptr; this->setAutoDelete(false); } @@ -91,3 +94,47 @@ void ctkAbstractWorker::setScheduler(QSharedPointer schedu { this->Scheduler = scheduler; } + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::startNextJob() +{ + if (!this->Scheduler || !this->Job) + { + return; + } + + ctkAbstractJob* newJob = this->Job->clone(); + newJob->setRetryCounter(newJob->retryCounter() + 1); + this->Scheduler->addJob(newJob); +} + +//---------------------------------------------------------------------------- +void ctkAbstractWorker::onJobCanceled() +{ + if (!this->Job) + { + return; + } + + if (this->Job->retryCounter() < this->Job->maximumNumberOfRetry() && + this->Job->status() != ctkAbstractJob::JobStatus::Stopped) + { + QTimer timer; + timer.setSingleShot(true); + QEventLoop loop; + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(this->Job->retryDelay()); + + this->startNextJob(); + + emit this->Job->finished(); + } + else if (this->Job->status() != ctkAbstractJob::JobStatus::Stopped) + { + emit this->Job->failed(); + } + else + { + emit this->Job->finished(); + } +} diff --git a/Libs/Core/ctkAbstractWorker.h b/Libs/Core/ctkAbstractWorker.h index 1e26a87936..ef36281b2f 100644 --- a/Libs/Core/ctkAbstractWorker.h +++ b/Libs/Core/ctkAbstractWorker.h @@ -66,6 +66,10 @@ class CTK_CORE_EXPORT ctkAbstractWorker : public QObject, public QRunnable void setScheduler(QSharedPointer scheduler); ///@} +public slots: + virtual void startNextJob(); + virtual void onJobCanceled(); + protected: QSharedPointer Job; QSharedPointer Scheduler; diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp index 72338540a1..a9d0388c37 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMWorker.cpp @@ -21,8 +21,6 @@ =========================================================================*/ -#include "ctkDICOMJob.h" -#include "ctkDICOMScheduler.h" #include "ctkDICOMWorker.h" //------------------------------------------------------------------------------ @@ -30,58 +28,3 @@ ctkDICOMWorker::ctkDICOMWorker() = default; //------------------------------------------------------------------------------ ctkDICOMWorker::~ctkDICOMWorker() = default; - -//---------------------------------------------------------------------------- -void ctkDICOMWorker::startNextJob() -{ - QSharedPointer scheduler = - qobject_cast>(this->Scheduler); - if (!scheduler) - { - return; - } - - QSharedPointer job = - qobject_cast>(this->Job); - if (!job) - { - return; - } - - ctkDICOMJob* newJob = job->clone(); - newJob->setRetryCounter(newJob->retryCounter() + 1); - scheduler->addJob(newJob); -} - -//---------------------------------------------------------------------------- -void ctkDICOMWorker::onJobCanceled() -{ - QSharedPointer job = - qobject_cast>(this->Job); - if (!job) - { - return; - } - - if (job->retryCounter() < job->maximumNumberOfRetry() && - job->status() != ctkAbstractJob::JobStatus::Stopped) - { - QTimer timer; - timer.setSingleShot(true); - QEventLoop loop; - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - timer.start(job->retryDelay()); - - this->startNextJob(); - - emit job->finished(); - } - else if (job->status() != ctkAbstractJob::JobStatus::Stopped) - { - emit job->failed(); - } - else - { - emit job->finished(); - } -} diff --git a/Libs/DICOM/Core/ctkDICOMWorker.h b/Libs/DICOM/Core/ctkDICOMWorker.h index 1839bbe813..4db110f38b 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.h +++ b/Libs/DICOM/Core/ctkDICOMWorker.h @@ -45,10 +45,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMWorker : public ctkAbstractWorker explicit ctkDICOMWorker(); virtual ~ctkDICOMWorker(); -public slots: - void startNextJob(); - void onJobCanceled(); - private: Q_DISABLE_COPY(ctkDICOMWorker) }; From 6e77c255512d30771c2c94ba51a814ea151b6781 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Wed, 17 Jan 2024 15:08:55 +0100 Subject: [PATCH 36/73] BUG: Adding missing jobUID and jobType to ctkDICOMJobDetail from responseSet --- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index e66a0c4392..3ac58f634f 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -176,6 +176,8 @@ struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail { explicit ctkDICOMJobDetail(const ctkDICOMJobResponseSet& responseSet) { + this->JobUID = responseSet.jobUID(); + this->JobType = responseSet.jobType(); this->PatientID = responseSet.patientID(); this->StudyInstanceUID = responseSet.studyInstanceUID(); this->SeriesInstanceUID = responseSet.seriesInstanceUID(); From 9fbc04b855f1b89a1955e0661857c2615547f003 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Wed, 17 Jan 2024 15:30:02 +0100 Subject: [PATCH 37/73] BUG: Fix loggerReport for the ctkDICOMInserterJob --- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 49 ++----------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index 21430d6440..5f9a2bf7f4 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -44,51 +44,10 @@ ctkDICOMInserterJob::~ctkDICOMInserterJob() = default; //------------------------------------------------------------------------------ QString ctkDICOMInserterJob::loggerReport(const QString &status) const { - switch (this->dicomLevel()) - { - case ctkDICOMJob::DICOMLevels::Patients: - return QString("ctkDICOMInserterJob: insert job at patients level %1.\n" - "JobUID: %2\n" - "PatientID: %3\n") - .arg(status) - .arg(this->jobUID()) - .arg(this->patientID()); - case ctkDICOMJob::DICOMLevels::Studies: - return QString("ctkDICOMInserterJob: insert job at studies level %1.\n" - "JobUID: %2\n" - "PatientID: %3\n" - "StudyInstanceUID: %4\n") - .arg(status) - .arg(this->jobUID()) - .arg(this->patientID()) - .arg(this->studyInstanceUID()); - case ctkDICOMJob::DICOMLevels::Series: - return QString("ctkDICOMInserterJob: insert job at series level %1.\n" - "JobUID: %2\n" - "PatientID: %3\n" - "StudyInstanceUID: %4\n" - "SeriesInstanceUID: %5\n") - .arg(status) - .arg(this->jobUID()) - .arg(this->patientID()) - .arg(this->studyInstanceUID()) - .arg(this->seriesInstanceUID()); - case ctkDICOMJob::DICOMLevels::Instances: - return QString("ctkDICOMInserterJob: insert job at instances level %1.\n" - "JobUID: %2\n" - "PatientID: %3\n" - "StudyInstanceUID: %4\n" - "SeriesInstanceUID: %5 \n" - "SOPInstanceUID: %6\n") - .arg(status) - .arg(this->jobUID()) - .arg(this->patientID()) - .arg(this->studyInstanceUID()) - .arg(this->seriesInstanceUID()) - .arg(this->sopInstanceUID()); - default: - return QString(""); - } + return QString("ctkDICOMInserterJob: insert job %1.\n" + "Number of jobResponseSet to process: %2\n") + .arg(status) + .arg(this->JobResponseSets.count()); } //------------------------------------------------------------------------------ From 6ebab367418a262b314d23b482980a4166c5d4e0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:09:43 -0500 Subject: [PATCH 38/73] COMP: Improve argument parsing in newly added DICOM tests --- .../Core/Testing/Cpp/ctkDICOMEchoTest1.cpp | 24 ++++++----- .../Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 40 +++++++++++-------- .../Cpp/ctkDICOMPatientItemWidgetTest1.cpp | 16 ++++++-- .../Cpp/ctkDICOMSeriesItemWidgetTest1.cpp | 16 ++++++-- .../Cpp/ctkDICOMServerNodeWidget2Test1.cpp | 14 +++++-- .../Cpp/ctkDICOMStudyItemWidgetTest1.cpp | 16 ++++++-- 6 files changed, 86 insertions(+), 40 deletions(-) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp index dcba0ac3fd..240beab1ef 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp @@ -24,6 +24,8 @@ // Qt includes #include +#include +#include // ctk includes #include "ctkCoreTestingMacros.h" @@ -39,17 +41,22 @@ int ctkDICOMEchoTest1(int argc, char * argv []) { QCoreApplication app(argc, argv); - // run tester server - ctkDICOMTester tester; - std::cerr << "ctkDICOMEchoTest1: Starting dcmqrscp\n"; - tester.startDCMQRSCP(); QStringList arguments = app.arguments(); - arguments.pop_front(); // remove application name + QString testName = arguments.takeFirst(); + if (!arguments.count()) { + std::cerr << "Usage: " << qPrintable(testName) + << " [...]" << std::endl; return EXIT_FAILURE; } - std::cerr << "ctkDICOMEchoTest1: Storing data to dcmqrscp\n"; + + ctkDICOMTester tester; + + std::cout << qPrintable(testName) << ": Starting dcmqrscp" << std::endl; + tester.startDCMQRSCP(); + + std::cout << qPrintable(testName) << ": Storing data to dcmqrscp" << std::endl; tester.storeData(arguments); ctkDICOMEcho echo; @@ -81,15 +88,14 @@ int ctkDICOMEchoTest1(int argc, char * argv []) { CHECK_BOOL(echo.echo(), false); // this has to be successful - std::cerr << "ctkDICOMEchoTest1: Setting up echo\n"; + std::cout << qPrintable(testName) << ": Setting up echo" << std::endl; echo.setCallingAETitle("CTK_AE"); echo.setCalledAETitle("CTK_AE"); echo.setHost("localhost"); echo.setPort(tester.dcmqrscpPort()); - std::cerr << "ctkDICOMEchoTest1: Running echo\n"; + std::cout << qPrintable(testName) << ": Running echo" << std::endl; CHECK_BOOL(echo.echo(), true); return EXIT_SUCCESS; } - diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index 67ef232b3b..26c0d43610 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -38,18 +38,24 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { QCoreApplication app(argc, argv); - // run tester server - ctkDICOMTester tester; - std::cerr << "ctkDICOMSchedulerTest1: Starting dcmqrscp\n"; - tester.startDCMQRSCP(); QStringList arguments = app.arguments(); - arguments.pop_front(); // remove application name - int numberOfImages = arguments.count(); - if (!numberOfImages) + QString testName = arguments.takeFirst(); + + if (!arguments.count()) { + std::cerr << "Usage: " << qPrintable(testName) + << " [...]" << std::endl; return EXIT_FAILURE; } - std::cerr << "ctkDICOMSchedulerTest1: Storing data to dcmqrscp\n"; + + int numberOfImages = arguments.count(); + + ctkDICOMTester tester; + + std::cout << qPrintable(testName) << ": Starting dcmqrscp" << std::endl; + tester.startDCMQRSCP(); + + std::cout << qPrintable(testName) << ": Storing data to dcmqrscp" << std::endl; tester.storeData(arguments); ctkDICOMScheduler scheduler; @@ -71,7 +77,7 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { CHECK_INT(scheduler.maximumPatientsQuery(), 30); // Test scheduler - std::cerr << "ctkDICOMSchedulerTest1: Setting up scheduler\n"; + std::cout << qPrintable(testName) << ": Setting up scheduler" << std::endl; ctkDICOMDatabase database; QString dbFile = "./ctkDICOM.sql"; QFile file(dbFile); @@ -99,18 +105,18 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { filsers.insert("ID", "Facial"); scheduler.setFilters(filsers); - std::cerr << "ctkDICOMSchedulerTest1: Running queryStudies\n"; + std::cout << qPrintable(testName) << ": Running queryStudies" << std::endl; QString patientID = "Facial Expression"; scheduler.queryStudies(patientID); scheduler.waitForFinish(); CHECK_INT(database.patients().count(), 1); - QString patientItem = database.patients()[0]; + QString patientItem = database.patients().at(0); QStringList studies = database.studiesForPatient(patientItem); CHECK_INT(studies.count(), 1); - std::cerr << "ctkDICOMSchedulerTest1: Running querySeries\n"; + std::cout << qPrintable(testName) << ": Running querySeries" << std::endl; QString studyIstanceUID = studies[0]; scheduler.querySeries(patientID, studyIstanceUID); scheduler.waitForFinish(); @@ -118,7 +124,7 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { QStringList series = database.seriesForStudy(studyIstanceUID); CHECK_INT(series.count(), 1); - std::cerr << "ctkDICOMSchedulerTest1: Running queryInstances\n"; + std::cout << qPrintable(testName) << ": Running queryInstances" << std::endl; QString seriesIstanceUID = series[0]; scheduler.queryInstances(patientID, studyIstanceUID, seriesIstanceUID); scheduler.waitForFinish(); @@ -133,9 +139,11 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { CHECK_INT(files.count(), 0); CHECK_INT(urls.count(), numberOfImages); - std::cerr << "ctkDICOMSchedulerTest1: Running multiple retrieveSOPInstance." - " This will test " << numberOfImages << " retrieve concorrent jobs\n"; - foreach (QString sopIstanceUID, instances) + std::cout << qPrintable(testName) << ": " + << "Running multiple retrieveSOPInstance. " + << "This will test " << numberOfImages << " retrieve concorrent jobs" << std::endl; + + foreach (const QString& sopIstanceUID, instances) { scheduler.retrieveSOPInstance(patientID, studyIstanceUID, seriesIstanceUID, sopIstanceUID); } diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp index ca7924b7c1..cbdbf7b269 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp @@ -23,11 +23,12 @@ // Qt includes #include +#include +#include #include -// ctk includes -#include "ctkCoreTestingMacros.h" -#include "ctkUtils.h" +// ctkCore includes +#include // ctkDICOMWidget includes #include "ctkDICOMPatientItemWidget.h" @@ -37,7 +38,14 @@ int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) { QApplication app(argc, argv); + QStringList arguments = app.arguments(); + QString testName = arguments.takeFirst(); + Q_UNUSED(testName); + + bool interactive = arguments.removeOne("-I"); + ctkDICOMPatientItemWidget widget; + // Test the default values CHECK_QSTRING(widget.patientItem(), ""); CHECK_QSTRING(widget.patientID(), ""); @@ -63,7 +71,7 @@ int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) widget.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); - if (argc <= 2 || QString(argv[argc - 1]) != "-I") + if (!interactive) { QTimer::singleShot(200, &app, SLOT(quit())); } diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp index c6ef3c8928..b72f164590 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp @@ -23,11 +23,12 @@ // Qt includes #include +#include +#include #include -// ctk includes -#include "ctkCoreTestingMacros.h" -#include "ctkUtils.h" +// ctkCore includes +#include // ctkDICOMWidget includes #include "ctkDICOMSeriesItemWidget.h" @@ -37,7 +38,14 @@ int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) { QApplication app(argc, argv); + QStringList arguments = app.arguments(); + QString testName = arguments.takeFirst(); + Q_UNUSED(testName); + + bool interactive = arguments.removeOne("-I"); + ctkDICOMSeriesItemWidget widget; + // Test the default values CHECK_QSTRING(widget.seriesItem(), ""); CHECK_QSTRING(widget.patientID(), ""); @@ -75,7 +83,7 @@ int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) widget.setThumbnailSizePixel(100); CHECK_INT(widget.thumbnailSizePixel(), 100); - if (argc <= 2 || QString(argv[argc - 1]) != "-I") + if (!interactive) { QTimer::singleShot(200, &app, SLOT(quit())); } diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp index 8c82d7475a..8af3cd6fdc 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp @@ -23,11 +23,13 @@ // Qt includes #include +#include +#include #include #include -// ctk includes -#include "ctkCoreTestingMacros.h" +// ctkCore includes +#include // ctkDICOMCore includes #include "ctkDICOMScheduler.h" @@ -38,6 +40,12 @@ int ctkDICOMServerNodeWidget2Test1( int argc, char * argv [] ) { QApplication app(argc, argv); + QStringList arguments = app.arguments(); + QString testName = arguments.takeFirst(); + Q_UNUSED(testName); + + bool interactive = arguments.removeOne("-I"); + ctkDICOMServerNodeWidget2 widget; // Test the default values @@ -88,7 +96,7 @@ int ctkDICOMServerNodeWidget2Test1( int argc, char * argv [] ) CHECK_INT(widget.getNumberOfServers(), 2); delete server; - if (argc <= 1 || QString(argv[1]) != "-I") + if (!interactive) { QTimer::singleShot(200, &app, SLOT(quit())); } diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp index 1918116d8f..62d897e011 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp @@ -23,11 +23,12 @@ // Qt includes #include +#include +#include #include -// ctk includes -#include "ctkCoreTestingMacros.h" -#include "ctkUtils.h" +// ctkCore includes +#include // ctkDICOMWidget includes #include "ctkDICOMStudyItemWidget.h" @@ -37,7 +38,14 @@ int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) { QApplication app(argc, argv); + QStringList arguments = app.arguments(); + QString testName = arguments.takeFirst(); + Q_UNUSED(testName); + + bool interactive = arguments.removeOne("-I"); + ctkDICOMStudyItemWidget widget; + // Test the default values CHECK_QSTRING(widget.studyItem(), ""); CHECK_QSTRING(widget.patientID(), ""); @@ -69,7 +77,7 @@ int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) widget.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); - if (argc <= 2 || QString(argv[argc - 1]) != "-I") + if (!interactive) { QTimer::singleShot(200, &app, SLOT(quit())); } From f003815f10f3e9de7163d7477f2c44ddf17eefdc Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:10:50 -0500 Subject: [PATCH 39/73] STYLE: Remove unused includes, fix include comment, remove extra EOF line STYLE: Re-order includes & fix include comments of newly added DICOM classes --- Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp | 4 ++-- .../Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp | 5 ++--- Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp | 5 ++--- .../Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 3 +-- .../DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp | 6 +++--- Libs/DICOM/Core/ctkDICOMInserter.h | 2 +- Libs/DICOM/Core/ctkDICOMInserterJob.h | 4 +--- Libs/DICOM/Core/ctkDICOMInserterWorker.h | 1 - Libs/DICOM/Core/ctkDICOMJob.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMJob.h | 5 +++-- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 4 ++-- Libs/DICOM/Core/ctkDICOMModel.h | 1 + Libs/DICOM/Core/ctkDICOMQuery.h | 4 +--- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMQueryJob.h | 1 - Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMQueryWorker.h | 1 - Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 1 - Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 9 ++++----- Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 1 - Libs/DICOM/Core/ctkDICOMScheduler.cpp | 4 ++-- Libs/DICOM/Core/ctkDICOMScheduler.h | 5 ++--- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 8 ++++---- Libs/DICOM/Core/ctkDICOMServer.cpp | 10 ++++++---- Libs/DICOM/Core/ctkDICOMServer.h | 2 +- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 8 +++++--- Libs/DICOM/Core/ctkDICOMStorageListener.h | 6 ++---- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 4 +++- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 1 - Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp | 6 ++++-- Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h | 1 - Libs/DICOM/Core/ctkDICOMWorker.cpp | 1 + Libs/DICOM/Core/ctkDICOMWorker.h | 1 - .../Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 6 +++--- Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h | 12 ++++++------ Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp | 3 +-- Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h | 8 ++++---- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 10 ++++++---- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp | 8 ++++---- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 12 ++++++++---- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 14 ++++++++------ Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h | 12 ++++++------ Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 12 ++++++------ 45 files changed, 120 insertions(+), 111 deletions(-) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp index 240beab1ef..fd3f819109 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp @@ -27,8 +27,8 @@ #include #include -// ctk includes -#include "ctkCoreTestingMacros.h" +// ctkCore includes +#include // ctkDICOMCore includes #include "ctkDICOMEcho.h" diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp index 43e6a5611c..e8610d426a 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp @@ -25,8 +25,8 @@ // Qt includes #include -// ctk includes -#include "ctkCoreTestingMacros.h" +// ctkCore includes +#include // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" @@ -75,4 +75,3 @@ int ctkDICOMJobResponseSetTest1(int argc, char * argv []) { return EXIT_SUCCESS; } - diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp index 1e3ede8685..cd0ec0cf80 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp @@ -25,8 +25,8 @@ // Qt includes #include -// ctk includes -#include "ctkCoreTestingMacros.h" +// ctkCore includes +#include // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" @@ -139,4 +139,3 @@ int ctkDICOMJobTest1(int argc, char * argv []) { return EXIT_SUCCESS; } - diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index 26c0d43610..5cc400ae1c 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -25,11 +25,10 @@ // Qt includes #include -// ctk includes +// ctkCore includes #include "ctkCoreTestingMacros.h" // ctkDICOMCore includes -#include "ctkDICOMQuery.h" #include "ctkDICOMScheduler.h" #include "ctkDICOMServer.h" #include "ctkDICOMTester.h" diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp index 4743910fda..1c7edf6777 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp @@ -25,8 +25,8 @@ // Qt includes #include -// ctk includes -#include "ctkCoreTestingMacros.h" +// ctkCore includes +#include // ctkDICOMCore includes #include "ctkDICOMServer.h" @@ -36,6 +36,7 @@ int ctkDICOMServerTest1(int argc, char * argv []) { QCoreApplication app(argc, argv); ctkDICOMServer server; + // Test the default values CHECK_QSTRING(server.connectionName(), ""); CHECK_QSTRING(server.callingAETitle(), ""); @@ -75,4 +76,3 @@ int ctkDICOMServerTest1(int argc, char * argv []) { return EXIT_SUCCESS; } - diff --git a/Libs/DICOM/Core/ctkDICOMInserter.h b/Libs/DICOM/Core/ctkDICOMInserter.h index 58e268a0ed..fe41f3bfcf 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.h +++ b/Libs/DICOM/Core/ctkDICOMInserter.h @@ -27,7 +27,7 @@ // Qt includes #include -// CTK includes +// ctkCore includes #include // ctkDICOMCore includes diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index db61ece98a..c96ff0bcfd 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -30,9 +30,7 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" - -#include - +#include "ctkDICOMJob.h" class ctkDICOMServer; /// \ingroup DICOM_Core diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 5eabd5545b..6914d2f28f 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -33,7 +33,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" - class ctkDICOMDatabase; class ctkDICOMInserter; class ctkDICOMInserterWorkerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index e8ebdf7ca5..457751e43e 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -21,10 +21,12 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMJob.h" #include "ctkDICOMJobResponseSet.h" -#include "ctkLogger.h" static ctkLogger logger ( "org.commontk.dicom.ctkDICOMJob" ); diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 5c6b6fec1d..097a46683d 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -29,12 +29,13 @@ #include #include +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" -#include - class ctkDICOMServer; class ctkDICOMJobResponseSet; diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 6549613ead..ff9b09520e 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -21,10 +21,12 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMItem.h" #include "ctkDICOMJobResponseSet.h" -#include "ctkLogger.h" // DCMTK includes #include diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 3ac58f634f..c15deb1b45 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -24,14 +24,14 @@ #ifndef __ctkDICOMJobResponseSet_h #define __ctkDICOMJobResponseSet_h - // Qt includes #include #include #include -#include "ctkDICOMJob.h" +// ctkDICOMCore includes #include "ctkDICOMCoreExport.h" +#include "ctkDICOMJob.h" #include "ctkDICOMItem.h" class ctkDICOMJobResponseSetPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMModel.h b/Libs/DICOM/Core/ctkDICOMModel.h index c8e4eba86d..c1652ac738 100644 --- a/Libs/DICOM/Core/ctkDICOMModel.h +++ b/Libs/DICOM/Core/ctkDICOMModel.h @@ -27,6 +27,7 @@ #include #include +// ctkDICOMCore includes #include "ctkDICOMCoreExport.h" class ctkDICOMModelPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMQuery.h b/Libs/DICOM/Core/ctkDICOMQuery.h index 3199922a91..8096400861 100644 --- a/Libs/DICOM/Core/ctkDICOMQuery.h +++ b/Libs/DICOM/Core/ctkDICOMQuery.h @@ -26,14 +26,12 @@ #include #include -// CTK includes +// ctkCore includes #include // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMDatabase.h" -#include "ctkErrorLogLevel.h" - class ctkDICOMQueryPrivate; class ctkDICOMJobResponseSet; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index f90802b337..e0a119fb69 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -21,12 +21,14 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" // For ctkDICOMJobDetail #include "ctkDICOMQueryJob_p.h" #include "ctkDICOMQueryWorker.h" #include "ctkDICOMServer.h" -#include "ctkLogger.h" static ctkLogger logger ( "org.commontk.dicom.ctkDICOMQueryJob" ); diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index ba2ca42fc4..8f28b2f290 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -33,7 +33,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" - class ctkDICOMQueryJobPrivate; class ctkDICOMServer; diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index 7eba428699..fc1c1a2a50 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -21,12 +21,14 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMQueryWorker_p.h" #include "ctkDICOMQueryJob.h" #include "ctkDICOMScheduler.h" #include "ctkDICOMServer.h" -#include "ctkLogger.h" static ctkLogger logger ("org.commontk.dicom.ctkDICOMQueryWorker"); diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index 25d7a41606..9c8aac886d 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -33,7 +33,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" - class ctkDICOMQuery; class ctkDICOMQueryWorkerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 545ad51885..94cdc69227 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -21,12 +21,14 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" // For ctkDICOMJobDetail #include "ctkDICOMRetrieveJob_p.h" #include "ctkDICOMRetrieveWorker.h" #include "ctkDICOMServer.h" -#include "ctkLogger.h" static ctkLogger logger ( "org.commontk.dicom.ctkDICOMRetrieveJob" ); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 658521c8ed..431ac25277 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -31,7 +31,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" - class ctkDICOMRetrieveJobPrivate; class ctkDICOMServer; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index 700f1315f4..967f063f1e 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -21,13 +21,15 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" #include "ctkDICOMRetrieveWorker_p.h" #include "ctkDICOMRetrieveJob.h" #include "ctkDICOMScheduler.h" #include "ctkDICOMServer.h" -#include "ctkLogger.h" static ctkLogger logger ("org.commontk.dicom.ctkDICOMRetrieveWorker"); @@ -103,13 +105,10 @@ ctkDICOMRetrieveWorker::ctkDICOMRetrieveWorker() ctkDICOMRetrieveWorker::ctkDICOMRetrieveWorker(ctkDICOMRetrieveWorkerPrivate* pimpl) : d_ptr(pimpl) { - Q_D(ctkDICOMRetrieveWorker); } //------------------------------------------------------------------------------ -ctkDICOMRetrieveWorker::~ctkDICOMRetrieveWorker() -{ -} +ctkDICOMRetrieveWorker::~ctkDICOMRetrieveWorker() = default; //---------------------------------------------------------------------------- void ctkDICOMRetrieveWorker::cancel() diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h index cfa8a8f97d..1563f52c8d 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -31,7 +31,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" - class ctkDICOMRetrieve; class ctkDICOMRetrieveWorkerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index f3d23061d4..0eaa2e3388 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -22,8 +22,8 @@ =========================================================================*/ // ctkCore includes -#include "ctkLogger.h" -#include "ctkAbstractScheduler_p.h" +#include +#include // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index a56d54b089..43b50a99d0 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -29,13 +29,12 @@ #include // ctkCore includes -#include "ctkAbstractScheduler.h" +#include +class ctkAbstractJob; // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMDatabase.h" - -class ctkAbstractJob; class ctkDICOMJob; class ctkDICOMIndexer; class ctkDICOMSchedulerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index c20904ac4f..1d454d08b7 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -25,14 +25,14 @@ #define __ctkDICOMQueryJobPrivate_h // ctkCore includes -#include "ctkAbstractScheduler_p.h" +#include +class ctkAbstractJob; +class ctkAbstractWorker; // ctkDICOMCore includes #include "ctkDICOMScheduler.h" -class ctkAbstractWorker; -class ctkAbstractJob; - +//------------------------------------------------------------------------------ struct ThumbnailUID { QString studyInstanceUID; diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index 636645582d..84e5a68fe4 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -21,13 +21,15 @@ =========================================================================*/ -// ctkDICOMCore includes -#include "ctkDICOMServer.h" -#include "ctkLogger.h" - // Qt includes #include +// ctkCore includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMServer.h" + static ctkLogger logger("org.commontk.dicom.DICOMServer"); //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h index 624cf8a2f8..cf5e23e4cf 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.h +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -27,8 +27,8 @@ // Qt includes #include +// ctkDICOMCore includes #include "ctkDICOMCoreExport.h" - class ctkDICOMServerPrivate; /// \ingroup DICOM_Core diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index cc79a6c3b6..192e031cc0 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -24,13 +24,15 @@ #include #include +// ctkCore includes +#include + // ctkDICOMCore includes -#include "ctkDICOMStorageListener.h" #include "ctkDICOMJobResponseSet.h" -#include "ctkLogger.h" +#include "ctkDICOMStorageListener.h" // DCMTK includes -#include "dcmtk/dcmnet/dstorscp.h" /* for DcmStorageSCP */ +#include /* for DcmStorageSCP */ static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListener" ); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index b11eda7e0d..abb6802be4 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -27,15 +27,13 @@ #include #include -// CTK includes +// ctkCore includes #include // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkErrorLogLevel.h" - -class ctkDICOMStorageListenerPrivate; class ctkDICOMJobResponseSet; +class ctkDICOMStorageListenerPrivate; /// \ingroup DICOM_Core class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index be2483cbc9..a758782a4a 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -21,10 +21,12 @@ =========================================================================*/ +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMStorageListenerJob_p.h" #include "ctkDICOMStorageListenerWorker.h" -#include "ctkLogger.h" static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListenerJob" ); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 59e0906ebf..33d78107c9 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -31,7 +31,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" - class ctkDICOMStorageListenerJobPrivate; /// \ingroup DICOM_Core diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index 5942f86d80..7c58ab1969 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -24,12 +24,14 @@ // Qt includes #include +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" #include "ctkDICOMScheduler.h" -#include "ctkDICOMStorageListenerWorker_p.h" #include "ctkDICOMStorageListenerJob.h" -#include "ctkLogger.h" +#include "ctkDICOMStorageListenerWorker_p.h" static ctkLogger logger ("org.commontk.dicom.ctkDICOMStorageListenerWorker"); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h index 307aab153f..89b69ae292 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -32,7 +32,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" - class ctkDICOMStorageListener; class ctkDICOMStorageListenerWorkerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp index a9d0388c37..f2abd4336f 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMWorker.cpp @@ -21,6 +21,7 @@ =========================================================================*/ +// ctkDICOMCore includes #include "ctkDICOMWorker.h" //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Core/ctkDICOMWorker.h b/Libs/DICOM/Core/ctkDICOMWorker.h index 4db110f38b..d3eeaae742 100644 --- a/Libs/DICOM/Core/ctkDICOMWorker.h +++ b/Libs/DICOM/Core/ctkDICOMWorker.h @@ -30,7 +30,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkAbstractWorker.h" - class ctkDICOMJob; class ctkDICOMScheduler; diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index 09b2fa5176..33bc519359 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -27,9 +27,9 @@ #include #include -// ctk includes -#include "ctkCoreTestingMacros.h" -#include "ctkUtils.h" +// ctkCore includes +#include +#include // ctkDICOMWidget includes #include "ctkDICOMVisualBrowserWidget.h" diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index 62e9f97a95..2b5e79f765 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -27,16 +27,16 @@ #include "ctkDICOMWidgetsExport.h" // Qt includes -#include #include +#include -// CTK includes -#include "ctkDICOMStudyItemWidget.h" - -class ctkDICOMPatientItemWidgetPrivate; - +// ctkDICOMWidgets includes class ctkDICOMDatabase; class ctkDICOMScheduler; + +// ctkDICOMWidgets includes +#include "ctkDICOMStudyItemWidget.h" +class ctkDICOMPatientItemWidgetPrivate; class ctkDICOMStudyItemWidget; /// \ingroup DICOM_Widgets diff --git a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp index f83388d661..e1ff86c54f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp @@ -28,7 +28,7 @@ #include #include -/// CTK includes +// ctkCore includes #include #include #include @@ -42,7 +42,6 @@ // ctkDICOMWidgets includes #include "ctkDICOMQueryRetrieveWidget.h" -#include "ctkDICOMQueryResultsTabWidget.h" #include "ui_ctkDICOMQueryRetrieveWidget.h" static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMQueryRetrieveWidget"); diff --git a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h index 5265c99c8b..6fa65374f9 100644 --- a/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h @@ -21,8 +21,6 @@ #ifndef __ctkDICOMQueryRetrieveWidget_h #define __ctkDICOMQueryRetrieveWidget_h -#include "ctkDICOMWidgetsExport.h" - // Qt includes #include #include @@ -30,10 +28,12 @@ #include #include -class ctkDICOMTableManager; -// CTK includes +// ctkDICOMCore includes #include +// ctkDICOMWidgets includes +#include "ctkDICOMWidgetsExport.h" +class ctkDICOMTableManager; class ctkDICOMQueryRetrieveWidgetPrivate; /// \ingroup DICOM_Widgets diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index bac32a2fdf..a2f1ad3099 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -24,16 +24,18 @@ #ifndef __ctkDICOMSeriesItemWidget_h #define __ctkDICOMSeriesItemWidget_h -#include "ctkDICOMWidgetsExport.h" - // Qt includes -#include #include +#include -class ctkDICOMSeriesItemWidgetPrivate; +// ctkDICOMCore includes class ctkDICOMDatabase; class ctkDICOMScheduler; +// ctkDICOMWidgets includes +#include "ctkDICOMWidgetsExport.h" +class ctkDICOMSeriesItemWidgetPrivate; + /// \ingroup DICOM_Widgets class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget { diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 2f15174ce5..4bc3a754c5 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -35,16 +35,16 @@ #include #include -// CTK includes +// ctkCore includes #include #include #include #include // ctkDICOMCore includes -#include "ctkDICOMEcho.h" -#include "ctkDICOMServer.h" -#include "ctkDICOMScheduler.h" +#include +#include +#include // ctkDICOMWidgets includes #include "ctkDICOMServerNodeWidget2.h" diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index 3cd5a1b4df..116bd6112f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -29,14 +29,18 @@ #include #include #include - -#include "ctkDICOMWidgetsExport.h" - class QTableWidgetItem; + +// ctkCore includes class ctkAbstractTask; + +// ctkDICOMCore includes +class ctkDICOMScheduler; class ctkDICOMServer; + +// ctkDICOMWidgets includes +#include "ctkDICOMWidgetsExport.h" class ctkDICOMServerNodeWidget2Private; -class ctkDICOMScheduler; /// \ingroup DICOM_Widgets class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index 6870839ca7..5b46743db7 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -27,20 +27,22 @@ #include "ctkDICOMWidgetsExport.h" // Qt includes -#include #include +#include +class QTableWidget; -// ctkDICOMWidgets includes -#include "ctkDICOMSeriesItemWidget.h" - +// ctkWidgets includes class ctkCollapsibleGroupBox; + +// ctkDICOMCore includes class ctkDICOMDatabase; class ctkDICOMScheduler; + +// ctkDICOMWidgets includes +#include "ctkDICOMSeriesItemWidget.h" class ctkDICOMSeriesItemWidget; class ctkDICOMStudyItemWidgetPrivate; -class QTableWidget; - /// \ingroup DICOM_Widgets class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget { diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h index f8df192dff..eb8db9a9e8 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h @@ -23,20 +23,20 @@ #define __ctkDICOMThumbnailGenerator_h // Qt includes -class QImage; #include +class QImage; -// CTK includes -#include "ctkDICOMWidgetsExport.h" +// ctkDICOMWidgets includes #include "ctkDICOMAbstractThumbnailGenerator.h" - +#include "ctkDICOMWidgetsExport.h" class ctkDICOMThumbnailGeneratorPrivate; + +// DCMTK includes class DicomImage; /// \ingroup DICOM_Widgets /// -/// \brief thumbnail generator class -/// +/// \brief Thumbnail generator class class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstractThumbnailGenerator { Q_OBJECT diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index b9823abb82..87cd929f29 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -24,17 +24,17 @@ #ifndef __ctkDICOMVisualBrowserWidget_h #define __ctkDICOMVisualBrowserWidget_h -#include "ctkDICOMWidgetsExport.h" - // Qt includes -#include #include +#include + +// ctkDICOMCore includes +#include -// CTK includes +// ctkDICOMWidgets includes #include "ctkDICOMPatientItemWidget.h" #include "ctkDICOMStudyItemWidget.h" -#include "ctkDICOMModel.h" -#include "ctkErrorLogLevel.h" +#include "ctkDICOMWidgetsExport.h" // DCMTK includes #include From 0540c885c7c94a5c72ff7f8519354a0f07e604c3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:17:35 -0500 Subject: [PATCH 40/73] COMP: Ensure DICOM Query, Retrieve and Schedule tests use a different DB --- .../Core/Testing/Cpp/ctkDICOMQueryTest2.cpp | 17 +++++++++++------ .../Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp | 17 +++++++++++------ .../Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 15 +++++++++------ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp index 74a0ff2b13..febf347193 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest2.cpp @@ -22,8 +22,12 @@ #include #include #include +#include #include +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMDatabase.h" #include "ctkDICOMQuery.h" @@ -48,17 +52,18 @@ int ctkDICOMQueryTest2( int argc, char * argv [] ) return EXIT_FAILURE; } + QTemporaryDir tempDirectory; + CHECK_BOOL(tempDirectory.isValid(), true); + ctkDICOMTester tester; tester.startDCMQRSCP(); tester.storeData(arguments); ctkDICOMDatabase database; - QString dbFile = "./ctkDICOM.sql"; - if (!database.openDatabase(dbFile)) - { - std::cout << "ctkDICOMDatabase::openDatabase() failed" << std::endl; - return EXIT_FAILURE; - } + + QDir databaseDirectory(tempDirectory.path()); + QString dbFile = QFileInfo(databaseDirectory, QString("ctkDICOM.sql")).absoluteFilePath(); + CHECK_BOOL(database.openDatabase(dbFile), true); database.cleanup(true); ctkDICOMQuery query; diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp index 859ec37ef3..25625147e8 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp @@ -24,8 +24,12 @@ #include #include #include +#include #include +// ctkCore includes +#include + // ctkDICOMCore includes #include "ctkDICOMDatabase.h" #include "ctkDICOMQuery.h" @@ -51,6 +55,9 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) return EXIT_FAILURE; } + QTemporaryDir tempDirectory; + CHECK_BOOL(tempDirectory.isValid(), true); + ctkDICOMTester tester; std::cerr << "ctkDICOMRetrieveTest2: Starting dcmqrscp\n"; tester.startDCMQRSCP(); @@ -59,12 +66,10 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] ) tester.storeData(arguments); ctkDICOMDatabase database; - QString dbFile = "./ctkDICOM.sql"; - if (!database.openDatabase(dbFile)) - { - std::cout << "ctkDICOMDatabase::openDatabase() failed" << std::endl; - return EXIT_FAILURE; - } + + QDir databaseDirectory(tempDirectory.path()); + QString dbFile = QFileInfo(databaseDirectory, QString("ctkDICOM.sql")).absoluteFilePath(); + CHECK_BOOL(database.openDatabase(dbFile), true); database.cleanup(true); std::cerr << "ctkDICOMRetrieveTest2: Setting up query\n"; diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index 5cc400ae1c..d7a6db83a5 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -24,9 +24,10 @@ // Qt includes #include +#include // ctkCore includes -#include "ctkCoreTestingMacros.h" +#include // ctkDICOMCore includes #include "ctkDICOMScheduler.h" @@ -47,6 +48,9 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { return EXIT_FAILURE; } + QTemporaryDir tempDirectory; + CHECK_BOOL(tempDirectory.isValid(), true); + int numberOfImages = arguments.count(); ctkDICOMTester tester; @@ -78,14 +82,13 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { // Test scheduler std::cout << qPrintable(testName) << ": Setting up scheduler" << std::endl; ctkDICOMDatabase database; - QString dbFile = "./ctkDICOM.sql"; - QFile file(dbFile); - file.remove(); - QFile cacheFile("./ctkDICOMTagCache.sql"); - cacheFile.remove(); + + QDir databaseDirectory(tempDirectory.path()); + QString dbFile = QFileInfo(databaseDirectory, QString("ctkDICOM.sql")).absoluteFilePath(); CHECK_BOOL(database.openDatabase(dbFile), true); CHECK_BOOL(database.isOpen(), true); database.cleanup(true); + CHECK_INT(database.patients().count(), 0); scheduler.setDicomDatabase(database); From ef1bc8980b2fa5ee4f530ce5b2d39d3fe6211171 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:24:09 -0500 Subject: [PATCH 41/73] COMP: Add "dcmqrscp" resource lock to CTK DICOM Echo and Scheduler tests --- Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt index 98f16f22aa..1973baba55 100644 --- a/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt +++ b/Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt @@ -66,6 +66,7 @@ SIMPLE_TEST(ctkDICOMEchoTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA ${CTKData_DIR}/Data/DICOM/MRHEAD/000056.IMA ) +set_property(TEST "ctkDICOMEchoTest1" PROPERTY RESOURCE_LOCK "dcmqrscp") # ctkDICOMModel SIMPLE_TEST(ctkDICOMModelTest1 @@ -109,6 +110,7 @@ SIMPLE_TEST(ctkDICOMSchedulerTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000058.IMA ${CTKData_DIR}/Data/DICOM/MRHEAD/000059.IMA ) +set_property(TEST "ctkDICOMSchedulerTest1" PROPERTY RESOURCE_LOCK "dcmqrscp") # ctkDICOMTester SIMPLE_TEST( ctkDICOMTesterTest1 ) From 315af4ccd1abfc6fca6f6a60c1ab50806ad23a08 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:50:57 -0500 Subject: [PATCH 42/73] ENH: Improve Browser and VisualBrowser test output to include testname --- .../Widgets/Testing/Cpp/ctkDICOMBrowserTest1.cpp | 15 ++++++++------- .../Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest1.cpp index 74ea1c6789..2355ad61a8 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMBrowserTest1.cpp @@ -56,13 +56,13 @@ int ctkDICOMBrowserTest1( int argc, char * argv [] ) QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMBrowserTest1-db")); QString dbDir = tempFileInfo.absoluteFilePath(); - qDebug() << "\n\nUsing directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Using directory: " << dbDir; if (tempFileInfo.exists()) { - qDebug() << "\n\nRemoving directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Removing directory: " << dbDir; ctk::removeDirRecursively(dbDir); } - qDebug() << "\n\nMaking directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Making directory: " << dbDir; QDir dir(dbDir); dir.mkdir(dbDir); @@ -72,7 +72,7 @@ int ctkDICOMBrowserTest1( int argc, char * argv [] ) browser.show(); browser.setDisplayImportSummary(false); - qDebug() << "Importing directory " << dicomDirectory; + qDebug().noquote() << testName << ": Importing directory " << dicomDirectory; // [Deprecated] // make sure copy/link dialog doesn't pop up, always copy on import @@ -97,7 +97,8 @@ int ctkDICOMBrowserTest1( int argc, char * argv [] ) browser.importFiles(files); browser.waitForImportFinished(); - qDebug() << browser.patientsAddedDuringImport() + qDebug().noquote() << testName << ":" + << " " << browser.patientsAddedDuringImport() << " " << browser.studiesAddedDuringImport() << " " << browser.seriesAddedDuringImport() << " " << browser.instancesAddedDuringImport(); @@ -110,7 +111,7 @@ int ctkDICOMBrowserTest1( int argc, char * argv [] ) browser.importDirectories(QStringList() << dicomDirectory); browser.waitForImportFinished(); - qDebug() << "\n\nAdded to database directory: " << files; + qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << files; // [Deprecated] // reset to the original copy/import setting @@ -122,7 +123,7 @@ int ctkDICOMBrowserTest1( int argc, char * argv [] ) CHECK_INT(browser.seriesAddedDuringImport(), 1); CHECK_INT(browser.instancesAddedDuringImport(), 100); - qDebug() << "\n\nAdded to database directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << dbDir; if (!interactive) { diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index 33bc519359..c2a46411b9 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -62,20 +62,20 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) // Test visual browser import functionality QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMBrowserTest1-db")); QString dbDir = tempFileInfo.absoluteFilePath(); - qDebug() << "\n\nUsing directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Using directory: " << dbDir; if (tempFileInfo.exists()) { - qDebug() << "\n\nRemoving directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Removing directory: " << dbDir; ctk::removeDirRecursively(dbDir); } - qDebug() << "\n\nMaking directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Making directory: " << dbDir; QDir dir(dbDir); dir.mkdir(dbDir); browser.setDatabaseDirectory(dbDir); browser.show(); - qDebug() << "Importing directory " << argv[1]; + qDebug().noquote() << testName << ": Importing directory " << argv[1]; // Test import of a few specific files QDirIterator it(argv[1], QStringList() << "*.IMA", QDir::Files, QDirIterator::Subdirectories); @@ -90,7 +90,8 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.importFiles(files); browser.waitForImportFinished(); - qDebug() << browser.patientsAddedDuringImport() + qDebug().noquote() << testName << ":" + << " " << browser.patientsAddedDuringImport() << " " << browser.studiesAddedDuringImport() << " " << browser.seriesAddedDuringImport() << " " << browser.instancesAddedDuringImport(); @@ -103,14 +104,14 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.importDirectories(QStringList() << argv[1]); browser.waitForImportFinished(); - qDebug() << "\n\nAdded to database directory: " << files; + qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << files; CHECK_INT(browser.patientsAddedDuringImport(), 1); CHECK_INT(browser.studiesAddedDuringImport(), 1); CHECK_INT(browser.seriesAddedDuringImport(), 1); CHECK_INT(browser.instancesAddedDuringImport(), 100); - qDebug() << "\n\nAdded to database directory: " << dbDir; + qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << dbDir; // Test setting and getting browser.setStorageAETitle("storage"); From 19d3fafed464bdf447e345b0e88f852eb3f828af Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:51:36 -0500 Subject: [PATCH 43/73] BUG: Fix ctkDICOMVisualBrowserWidgetTest1 argument parsing --- .../Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index c2a46411b9..8389d84407 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -38,13 +38,22 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) { QApplication app(argc, argv); - qDebug() << "argc = " << argc; - for (int i = 0; i < argc; ++i) + QStringList arguments = app.arguments(); + QString testName = arguments.takeFirst(); + + bool interactive = arguments.removeOne("-I"); + + if (arguments.count() != 1) { - qDebug() << "\t" << argv[i]; + std::cerr << "Usage: " << qPrintable(testName) + << " [-I] " << std::endl; + return EXIT_FAILURE; } + QString dicomDirectory(arguments.at(0)); + ctkDICOMVisualBrowserWidget browser; + // Test the default values CHECK_QSTRING(browser.storageAETitle(), "CTKSTORE"); CHECK_INT(browser.storagePort(), 11112); @@ -52,7 +61,7 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) CHECK_QSTRING(browser.filteringPatientName(), ""); CHECK_QSTRING(browser.filteringStudyDescription(), ""); CHECK_QSTRING(browser.filteringSeriesDescription(), ""); - CHECK_QSTRING(browser.filteringModalities()[0], "Any"); + CHECK_QSTRING(browser.filteringModalities().at(0), "Any"); CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); CHECK_INT(browser.numberOfStudiesPerPatient(), 2); CHECK_INT(browser.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); @@ -75,10 +84,10 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.setDatabaseDirectory(dbDir); browser.show(); - qDebug().noquote() << testName << ": Importing directory " << argv[1]; + qDebug().noquote() << testName << ": Importing directory " << dicomDirectory; // Test import of a few specific files - QDirIterator it(argv[1], QStringList() << "*.IMA", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it(dicomDirectory, QStringList() << "*.IMA", QDir::Files, QDirIterator::Subdirectories); // Skip a few files it.next(); it.next(); @@ -128,7 +137,7 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) CHECK_QSTRING(browser.filteringSeriesDescription(), "SeriesDescription"); QStringList filteringModalities = {"CT"}; browser.setFilteringModalities(filteringModalities); - CHECK_QSTRING(browser.filteringModalities()[0], "CT"); + CHECK_QSTRING(browser.filteringModalities().at(0), "CT"); browser.setFilteringDate(ctkDICOMPatientItemWidget::DateType::LastYear); CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); browser.setNumberOfStudiesPerPatient(6); @@ -140,7 +149,7 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.setDeleteActionVisible(false); CHECK_BOOL(browser.isDeleteActionVisible(), false); - if (argc <= 2 || QString(argv[argc - 1]) != "-I") + if (!interactive) { QTimer::singleShot(200, &app, SLOT(quit())); } From e9b51c23453a47d051cfd17f99f1dbd6fb9f4523 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 03:52:50 -0500 Subject: [PATCH 44/73] BUG: Update ctkDICOMVisualBrowserWidgetTest1 to use its own DB directory Do not reuse "ctkDICOMBrowserTest1-db" already associated with ctkDICOMBrowserTest1 --- .../Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index 8389d84407..01fc201d50 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -69,7 +69,7 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) CHECK_BOOL(browser.isDeleteActionVisible(), true); // Test visual browser import functionality - QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMBrowserTest1-db")); + QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMVisualBrowserWidgetTest1-db")); QString dbDir = tempFileInfo.absoluteFilePath(); qDebug().noquote() << "\n\n" << testName << ": Using directory: " << dbDir; if (tempFileInfo.exists()) From 742cc8380a582df18f9bd47ded37871d7bf55e06 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Thu, 18 Jan 2024 03:57:54 -0500 Subject: [PATCH 45/73] COMP: Update DICOMWidget CMakeLists.txt to include "Svg" --- Libs/DICOM/Widgets/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/DICOM/Widgets/CMakeLists.txt b/Libs/DICOM/Widgets/CMakeLists.txt index 283b312d27..c238cab113 100644 --- a/Libs/DICOM/Widgets/CMakeLists.txt +++ b/Libs/DICOM/Widgets/CMakeLists.txt @@ -108,7 +108,7 @@ set(KIT_resources # The following macro will read the target libraries from the file 'target_libraries.cmake' ctkFunctionGetTargetLibraries(KIT_target_libraries) -list(APPEND KIT_target_libraries Qt5::Svg) +list(APPEND KIT_target_libraries Qt${CTK_QT_VERSION}::Svg) ctkMacroBuildLib( NAME ${PROJECT_NAME} From 537501f432a339f831b3dd327f82e5c40f5299f1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 07:31:17 -0500 Subject: [PATCH 46/73] COMP: Update DICOM job "createWorker" function to return ctkAbstractJob --- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMInserterJob.h | 5 ++++- Libs/DICOM/Core/ctkDICOMJob.h | 1 - Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMQueryJob.h | 5 ++++- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.h | 5 ++++- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 4 ++-- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 5 ++++- 10 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index 5f9a2bf7f4..989625c560 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -109,7 +109,7 @@ ctkAbstractJob* ctkDICOMInserterJob::clone() const } //------------------------------------------------------------------------------ -ctkDICOMWorker *ctkDICOMInserterJob::createWorker() +ctkAbstractWorker *ctkDICOMInserterJob::createWorker() { ctkDICOMInserterWorker* worker = new ctkDICOMInserterWorker; diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.h b/Libs/DICOM/Core/ctkDICOMInserterJob.h index c96ff0bcfd..7f8981cabe 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.h +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.h @@ -28,6 +28,9 @@ #include #include +// ctkCore includes +class ctkAbstractWorker; + // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" @@ -71,7 +74,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterJob : public ctkDICOMJob Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker() override; + Q_INVOKABLE ctkAbstractWorker* createWorker() override; protected: QString DatabaseFilename; diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 097a46683d..027af98e6a 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -34,7 +34,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkDICOMWorker.h" class ctkDICOMServer; class ctkDICOMJobResponseSet; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index e0a119fb69..f96c71d2b9 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -200,7 +200,7 @@ ctkAbstractJob* ctkDICOMQueryJob::clone() const } //------------------------------------------------------------------------------ -ctkDICOMWorker *ctkDICOMQueryJob::createWorker() +ctkAbstractWorker *ctkDICOMQueryJob::createWorker() { ctkDICOMQueryWorker* worker = new ctkDICOMQueryWorker; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index 8f28b2f290..a70c769c19 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -30,6 +30,9 @@ #include #include +// ctkCore includes +class ctkAbstractWorker; + // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" @@ -90,7 +93,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker() override; + Q_INVOKABLE ctkAbstractWorker* createWorker() override; /// Return the QVariant value of this job. /// diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index 94cdc69227..b3152b6b04 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -177,7 +177,7 @@ ctkAbstractJob* ctkDICOMRetrieveJob::clone() const } //------------------------------------------------------------------------------ -ctkDICOMWorker *ctkDICOMRetrieveJob::createWorker() +ctkAbstractWorker *ctkDICOMRetrieveJob::createWorker() { ctkDICOMRetrieveWorker* worker = new ctkDICOMRetrieveWorker; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h index 431ac25277..bea19abec4 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.h @@ -28,6 +28,9 @@ #include #include +// ctkCore includes +class ctkAbstractWorker; + // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" @@ -59,7 +62,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveJob : public ctkDICOMJob Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker() override; + Q_INVOKABLE ctkAbstractWorker* createWorker() override; /// Return the QVariant value of this job. /// diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 0eaa2e3388..72a5af5e23 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -24,6 +24,7 @@ // ctkCore includes #include #include +#include // ctkDICOMCore includes #include "ctkDICOMInserterJob.h" @@ -643,8 +644,7 @@ void ctkDICOMScheduler::stopJobsByUIDs(const QStringList& patientIDs, // Stops queued and running jobs foreach (QSharedPointer worker, d->Workers) { - QSharedPointer job = - qobject_cast>(worker->jobShared()); + QSharedPointer job = worker->jobShared(); if (!job) { continue; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index a758782a4a..f60412923d 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -132,7 +132,7 @@ ctkAbstractJob* ctkDICOMStorageListenerJob::clone() const } //------------------------------------------------------------------------------ -ctkDICOMWorker *ctkDICOMStorageListenerJob::createWorker() +ctkAbstractWorker *ctkDICOMStorageListenerJob::createWorker() { ctkDICOMStorageListenerWorker* worker = new ctkDICOMStorageListenerWorker; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 33d78107c9..4a445a5d42 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -28,6 +28,9 @@ #include #include +// ctkCore includes +class ctkAbstractWorker; + // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMJob.h" @@ -71,7 +74,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob Q_INVOKABLE ctkAbstractJob* clone() const override; /// Generate worker for job - Q_INVOKABLE ctkDICOMWorker* createWorker() override; + Q_INVOKABLE ctkAbstractWorker* createWorker() override; protected: QScopedPointer d_ptr; From c096ffa0d45ac51d2f0deb4049e0f59b0e9265ab Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 08:27:39 -0500 Subject: [PATCH 47/73] COMP: Simplify setting Finished status in retrieve and storageListener worker --- Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 20 +++---------------- .../Core/ctkDICOMStorageListenerWorker.cpp | 11 ++-------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index 967f063f1e..0802acf331 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -130,24 +130,10 @@ void ctkDICOMRetrieveWorker::run() QSharedPointer scheduler = qobject_cast>(this->Scheduler); - if (!scheduler) - { - emit retrieveJob->canceled(); - this->onJobCanceled(); - retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); - return; - } - ctkDICOMServer* server = retrieveJob->server(); - if (!server) - { - emit retrieveJob->canceled(); - this->onJobCanceled(); - retrieveJob->setStatus(ctkAbstractJob::JobStatus::Finished); - return; - } - - if (retrieveJob->status() == ctkAbstractJob::JobStatus::Stopped) + if (!scheduler + || !server + || retrieveJob->status() == ctkAbstractJob::JobStatus::Stopped) { emit retrieveJob->canceled(); this->onJobCanceled(); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index 7c58ab1969..d588812b4b 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -125,15 +125,8 @@ void ctkDICOMStorageListenerWorker::run() QSharedPointer scheduler = qobject_cast>(this->Scheduler); - if (!scheduler) - { - emit storageListenerJob->canceled(); - this->onJobCanceled(); - storageListenerJob->setStatus(ctkAbstractJob::JobStatus::Finished); - return; - } - - if (storageListenerJob->status() == ctkAbstractJob::JobStatus::Stopped) + if (!scheduler + || storageListenerJob->status() == ctkAbstractJob::JobStatus::Stopped) { emit storageListenerJob->canceled(); this->onJobCanceled(); From 75955c0d3541a24e451d3c6dad985fa9932366e0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 09:58:07 -0500 Subject: [PATCH 48/73] COMP: Simplify worker API marking setJob with shared pointer param virtual --- Libs/Core/ctkAbstractWorker.h | 2 +- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 16 +--------------- Libs/DICOM/Core/ctkDICOMInserterWorker.h | 6 ++---- Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 16 +--------------- Libs/DICOM/Core/ctkDICOMQueryWorker.h | 6 ++---- Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 16 +--------------- Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 6 ++---- .../DICOM/Core/ctkDICOMStorageListenerWorker.cpp | 16 +--------------- Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h | 6 ++---- 9 files changed, 13 insertions(+), 77 deletions(-) diff --git a/Libs/Core/ctkAbstractWorker.h b/Libs/Core/ctkAbstractWorker.h index ef36281b2f..457a74ce28 100644 --- a/Libs/Core/ctkAbstractWorker.h +++ b/Libs/Core/ctkAbstractWorker.h @@ -55,7 +55,7 @@ class CTK_CORE_EXPORT ctkAbstractWorker : public QObject, public QRunnable Q_INVOKABLE ctkAbstractJob* job() const; QSharedPointer jobShared() const; Q_INVOKABLE void setJob(ctkAbstractJob& job); - void setJob(QSharedPointer job); + virtual void setJob(QSharedPointer job); ///@} ///@{ diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 612cc4ce47..857daf9291 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -118,20 +118,6 @@ void ctkDICOMInserterWorker::run() emit inserterJob->finished(); } -//---------------------------------------------------------------------------- -static void skipDelete(QObject* obj) -{ - Q_UNUSED(obj); - // this deleter does not delete the object from memory - // useful if the pointer is not owned by the smart pointer -} - -//---------------------------------------------------------------------------- -void ctkDICOMInserterWorker::setJob(ctkAbstractJob &job) -{ - this->setJob(QSharedPointer(&job, skipDelete)); -} - //---------------------------------------------------------------------------- void ctkDICOMInserterWorker::setJob(QSharedPointer job) { @@ -143,7 +129,7 @@ void ctkDICOMInserterWorker::setJob(QSharedPointer job) return; } - Superclass::setJob(job); + this->Superclass::setJob(job); d->Inserter->setDatabaseFilename(inserterJob->databaseFilename()); d->Inserter->setTagsToPrecache(inserterJob->tagsToPrecache()); diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 6914d2f28f..325cc47dd2 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -53,11 +53,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkDICOMWorker /// Cancel worker void cancel() override; - ///@{ /// Job - Q_INVOKABLE void setJob(ctkAbstractJob& job); - void setJob(QSharedPointer job); - ///@} + void setJob(QSharedPointer job) override; + using ctkAbstractWorker::setJob; ///@{ /// Inserter diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index fc1c1a2a50..3590a9d2c5 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -98,20 +98,6 @@ void ctkDICOMQueryWorker::cancel() d->Query->cancel(); } -//---------------------------------------------------------------------------- -static void skipDelete(QObject* obj) -{ - Q_UNUSED(obj); - // this deleter does not delete the object from memory - // useful if the pointer is not owned by the smart pointer -} - -//---------------------------------------------------------------------------- -void ctkDICOMQueryWorker::setJob(ctkAbstractJob &job) -{ - this->setJob(QSharedPointer(&job, skipDelete)); -} - //---------------------------------------------------------------------------- void ctkDICOMQueryWorker::run() { @@ -213,7 +199,7 @@ void ctkDICOMQueryWorker::setJob(QSharedPointer job) return; } - Superclass::setJob(job); + this->Superclass::setJob(job); d->setQueryParameters(); } diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index 9c8aac886d..4c4aac4a93 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -52,11 +52,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkDICOMWorker /// Cancel worker void cancel() override; - ///@{ /// Job - Q_INVOKABLE void setJob(ctkAbstractJob& job); - void setJob(QSharedPointer job); - ///@} + void setJob(QSharedPointer job) override; + using ctkAbstractWorker::setJob; ///@{ /// Querier diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index 0802acf331..5e8052fef5 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -269,20 +269,6 @@ void ctkDICOMRetrieveWorker::run() emit retrieveJob->finished(); } -//---------------------------------------------------------------------------- -static void skipDelete(QObject* obj) -{ - Q_UNUSED(obj); - // this deleter does not delete the object from memory - // useful if the pointer is not owned by the smart pointer -} - -//---------------------------------------------------------------------------- -void ctkDICOMRetrieveWorker::setJob(ctkAbstractJob &job) -{ - this->setJob(QSharedPointer(&job, skipDelete)); -} - //---------------------------------------------------------------------------- void ctkDICOMRetrieveWorker::setJob(QSharedPointer job) { @@ -295,7 +281,7 @@ void ctkDICOMRetrieveWorker::setJob(QSharedPointer job) return; } - Superclass::setJob(job); + this->Superclass::setJob(job); d->setRetrieveParameters(); } diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h index 1563f52c8d..6f8c72f43d 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -50,11 +50,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkDICOMWorker /// Cancel worker void cancel() override; - ///@{ /// Job - Q_INVOKABLE void setJob(ctkAbstractJob& job); - void setJob(QSharedPointer job); - ///@} + void setJob(QSharedPointer job) override; + using ctkAbstractWorker::setJob; ///@{ /// Retriever diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index d588812b4b..3d33b1a890 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -154,20 +154,6 @@ void ctkDICOMStorageListenerWorker::run() emit storageListenerJob->finished(); } -//---------------------------------------------------------------------------- -static void skipDelete(QObject* obj) -{ - Q_UNUSED(obj); - // this deleter does not delete the object from memory - // useful if the pointer is not owned by the smart pointer -} - -//---------------------------------------------------------------------------- -void ctkDICOMStorageListenerWorker::setJob(ctkAbstractJob &job) -{ - this->setJob(QSharedPointer(&job, skipDelete)); -} - //---------------------------------------------------------------------------- void ctkDICOMStorageListenerWorker::setJob(QSharedPointer job) { @@ -180,7 +166,7 @@ void ctkDICOMStorageListenerWorker::setJob(QSharedPointer job) return; } - Superclass::setJob(job); + this->Superclass::setJob(job); d->setStorageListenerParameters(); } diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h index 89b69ae292..5264e1caf5 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -51,11 +51,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkDICOMWorke /// Cancel worker void cancel() override; - ///@{ /// Job - Q_INVOKABLE void setJob(ctkAbstractJob& job); - void setJob(QSharedPointer job); - ///@} + void setJob(QSharedPointer job) override; + using ctkAbstractWorker::setJob; ///@{ /// Retriever From 83e078a6b555d8da25bfb101635f8089ba9883fd Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 10:13:00 -0500 Subject: [PATCH 49/73] COMP: Add ctkDICOMInserterWorkerPrivate::setInserterParameters The Inserter worker organization matches the three other ones: StorageListener, Retrieve and Query. --- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 25 +++++++++++++++++----- Libs/DICOM/Core/ctkDICOMInserterWorker_p.h | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 857daf9291..123fdb4c00 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -47,6 +47,23 @@ ctkDICOMInserterWorkerPrivate::ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWor //------------------------------------------------------------------------------ ctkDICOMInserterWorkerPrivate::~ctkDICOMInserterWorkerPrivate() = default; +//------------------------------------------------------------------------------ +void ctkDICOMInserterWorkerPrivate::setInserterParameters() +{ + Q_Q(ctkDICOMInserterWorker); + + QSharedPointer inserterJob = + qobject_cast>(q->Job); + if (!inserterJob) + { + return; + } + + this->Inserter->setDatabaseFilename(inserterJob->databaseFilename()); + this->Inserter->setTagsToPrecache(inserterJob->tagsToPrecache()); + this->Inserter->setTagsToExcludeFromStorage(inserterJob->tagsToExcludeFromStorage()); +} + //------------------------------------------------------------------------------ // ctkDICOMInserterWorker methods @@ -121,7 +138,8 @@ void ctkDICOMInserterWorker::run() //---------------------------------------------------------------------------- void ctkDICOMInserterWorker::setJob(QSharedPointer job) { - Q_D(const ctkDICOMInserterWorker); + Q_D(ctkDICOMInserterWorker); + QSharedPointer inserterJob = qobject_cast>(job); if (!inserterJob) @@ -130,10 +148,7 @@ void ctkDICOMInserterWorker::setJob(QSharedPointer job) } this->Superclass::setJob(job); - - d->Inserter->setDatabaseFilename(inserterJob->databaseFilename()); - d->Inserter->setTagsToPrecache(inserterJob->tagsToPrecache()); - d->Inserter->setTagsToExcludeFromStorage(inserterJob->tagsToExcludeFromStorage()); + d->setInserterParameters(); } //---------------------------------------------------------------------------- diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h index bab96ff9cf..df0110b29c 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h @@ -41,6 +41,8 @@ class ctkDICOMInserterWorkerPrivate : public QObject ctkDICOMInserterWorkerPrivate(ctkDICOMInserterWorker* object); virtual ~ctkDICOMInserterWorkerPrivate(); + void setInserterParameters(); + QSharedPointer Inserter; }; From 2ae37723974cbb96e23c169ed53d9d9096642469 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 11:57:02 -0500 Subject: [PATCH 50/73] COMP: Fix constness of ctkDICOMServer method accepting QString param --- Libs/DICOM/Core/ctkDICOMServer.cpp | 2 +- Libs/DICOM/Core/ctkDICOMServer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index 84e5a68fe4..00ad205c61 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -205,7 +205,7 @@ ctkDICOMServer::RetrieveProtocol ctkDICOMServer::retrieveProtocol() const } //------------------------------------------------------------------------------ -void ctkDICOMServer::setRetrieveProtocolAsString(QString protocolString) +void ctkDICOMServer::setRetrieveProtocolAsString(const QString& protocolString) { Q_D(ctkDICOMServer); diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h index cf5e23e4cf..fcb48c2599 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.h +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -110,7 +110,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject }; void setRetrieveProtocol(RetrieveProtocol protocol); RetrieveProtocol retrieveProtocol() const; - Q_INVOKABLE void setRetrieveProtocolAsString(QString protocolString); + Q_INVOKABLE void setRetrieveProtocolAsString(const QString& protocolString); Q_INVOKABLE QString retrieveProtocolAsString() const; ///}@ From 3ad63946900e0a42ad15fb70573032fb640f5750 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 11:59:26 -0500 Subject: [PATCH 51/73] COMP: Fix constness of ctkDICOMScheduler methods for inserting responseSet --- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 4 ++-- Libs/DICOM/Core/ctkDICOMScheduler.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 72a5af5e23..f3afe60387 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -322,7 +322,7 @@ void ctkDICOMScheduler::startListener(const int port, } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::insertJobResponseSet(QSharedPointer jobResponseSet, +void ctkDICOMScheduler::insertJobResponseSet(const QSharedPointer& jobResponseSet, QThread::Priority priority) { QList> jobResponseSets; @@ -331,7 +331,7 @@ void ctkDICOMScheduler::insertJobResponseSet(QSharedPointer> jobResponseSets, +void ctkDICOMScheduler::insertJobResponseSets(const QList>& jobResponseSets, QThread::Priority priority) { Q_D(ctkDICOMScheduler); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 43b50a99d0..28ee12d9d0 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -105,9 +105,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler ///@{ /// Insert results from a job - void insertJobResponseSet(QSharedPointer jobResponseSet, + void insertJobResponseSet(const QSharedPointer& jobResponseSet, QThread::Priority priority = QThread::HighPriority); - void insertJobResponseSets(QList> jobResponseSets, + void insertJobResponseSets(const QList>& jobResponseSets, QThread::Priority priority = QThread::HighPriority); ///@} From 9d5272f192a296683a2eececdfd172ccc4a63668 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 12:04:51 -0500 Subject: [PATCH 52/73] COMP: Prefer qSharedPointerObjectCast instead of qobject_cast inserterJob = - qobject_cast>(q->Job); + qSharedPointerObjectCast(q->Job); if (!inserterJob) { return; @@ -94,7 +94,7 @@ void ctkDICOMInserterWorker::run() { Q_D(const ctkDICOMInserterWorker); QSharedPointer inserterJob = - qobject_cast>(this->Job); + qSharedPointerObjectCast(this->Job); if (!inserterJob) { return; @@ -141,7 +141,7 @@ void ctkDICOMInserterWorker::setJob(QSharedPointer job) Q_D(ctkDICOMInserterWorker); QSharedPointer inserterJob = - qobject_cast>(job); + qSharedPointerObjectCast(job); if (!inserterJob) { return; diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index 3590a9d2c5..c68000567f 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -51,7 +51,7 @@ void ctkDICOMQueryWorkerPrivate::setQueryParameters() Q_Q(ctkDICOMQueryWorker); QSharedPointer queryJob = - qobject_cast>(q->Job); + qSharedPointerObjectCast(q->Job); if (!queryJob) { return; @@ -103,14 +103,14 @@ void ctkDICOMQueryWorker::run() { Q_D(const ctkDICOMQueryWorker); QSharedPointer queryJob = - qobject_cast>(this->Job); + qSharedPointerObjectCast(this->Job); if (!queryJob) { return; } QSharedPointer scheduler = - qobject_cast>(this->Scheduler); + qSharedPointerObjectCast(this->Scheduler); if (!scheduler) { emit queryJob->canceled(); @@ -193,7 +193,7 @@ void ctkDICOMQueryWorker::setJob(QSharedPointer job) Q_D(ctkDICOMQueryWorker); QSharedPointer queryJob = - qobject_cast>(job); + qSharedPointerObjectCast(job); if (!queryJob) { return; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index 5e8052fef5..bbf1f7f65c 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -49,7 +49,7 @@ ctkDICOMRetrieveWorkerPrivate::~ctkDICOMRetrieveWorkerPrivate() Q_Q(ctkDICOMRetrieveWorker); QSharedPointer retrieveJob = - qobject_cast>(q->Job); + qSharedPointerObjectCast(q->Job); if (!retrieveJob) { return; @@ -65,7 +65,7 @@ void ctkDICOMRetrieveWorkerPrivate::setRetrieveParameters() Q_Q(ctkDICOMRetrieveWorker); QSharedPointer retrieveJob = - qobject_cast>(q->Job); + qSharedPointerObjectCast(q->Job); if (!retrieveJob) { return; @@ -122,14 +122,14 @@ void ctkDICOMRetrieveWorker::run() { Q_D(const ctkDICOMRetrieveWorker); QSharedPointer retrieveJob = - qobject_cast>(this->Job); + qSharedPointerObjectCast(this->Job); if (!retrieveJob) { return; } QSharedPointer scheduler = - qobject_cast>(this->Scheduler); + qSharedPointerObjectCast(this->Scheduler); ctkDICOMServer* server = retrieveJob->server(); if (!scheduler || !server @@ -275,7 +275,7 @@ void ctkDICOMRetrieveWorker::setJob(QSharedPointer job) Q_D(ctkDICOMRetrieveWorker); QSharedPointer retrieveJob = - qobject_cast>(job); + qSharedPointerObjectCast(job); if (!retrieveJob) { return; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index f3afe60387..7155f1d798 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -730,7 +730,7 @@ ctkDICOMStorageListenerJob *ctkDICOMScheduler::listenerJob() foreach(QSharedPointer job, d->JobsQueue) { QSharedPointer listenerJob = - qobject_cast>(job); + qSharedPointerObjectCast(job); if (listenerJob) { return listenerJob.data(); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index 3d33b1a890..e1140ccda7 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -54,7 +54,7 @@ void ctkDICOMStorageListenerWorkerPrivate::setStorageListenerParameters() Q_Q(ctkDICOMStorageListenerWorker); QSharedPointer storageListenerJob = - qobject_cast>(q->Job); + qSharedPointerObjectCast(q->Job); if (!storageListenerJob) { return; @@ -117,14 +117,14 @@ void ctkDICOMStorageListenerWorker::run() { Q_D(const ctkDICOMStorageListenerWorker); QSharedPointer storageListenerJob = - qobject_cast>(this->Job); + qSharedPointerObjectCast(this->Job); if (!storageListenerJob) { return; } QSharedPointer scheduler = - qobject_cast>(this->Scheduler); + qSharedPointerObjectCast(this->Scheduler); if (!scheduler || storageListenerJob->status() == ctkAbstractJob::JobStatus::Stopped) { @@ -160,7 +160,7 @@ void ctkDICOMStorageListenerWorker::setJob(QSharedPointer job) Q_D(ctkDICOMStorageListenerWorker); QSharedPointer storageListenerJob = - qobject_cast>(job); + qSharedPointerObjectCast(job); if (!storageListenerJob) { return; @@ -190,7 +190,7 @@ void ctkDICOMStorageListenerWorker::onInsertJobDetail() Q_D(ctkDICOMStorageListenerWorker); QSharedPointer scheduler = - qobject_cast>(this->Scheduler); + qSharedPointerObjectCast(this->Scheduler); if (!scheduler) { return; From d7fd6bea63ede432a44af2c0495077178464e93f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 12:50:58 -0500 Subject: [PATCH 53/73] COMP: Fix more constness of introduced DICOM methods accepting POD type --- Libs/Core/ctkAbstractScheduler.cpp | 6 +++--- Libs/Core/ctkAbstractScheduler.h | 6 +++--- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMQueryJob.h | 2 +- Libs/DICOM/Core/ctkDICOMRetrieve.cpp | 2 +- Libs/DICOM/Core/ctkDICOMRetrieve.h | 2 +- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 8 ++++---- Libs/DICOM/Core/ctkDICOMScheduler.h | 4 ++-- Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 2 +- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp | 14 +++++++------- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 2 +- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp | 2 +- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 2 +- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 8 +++----- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 2 +- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 2 +- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 2 +- 18 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index 7b96f7d6cc..fd523b508c 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -321,7 +321,7 @@ int ctkAbstractScheduler::maximumThreadCount() const } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setMaximumThreadCount(const int &maximumThreadCount) +void ctkAbstractScheduler::setMaximumThreadCount(int maximumThreadCount) { Q_D(ctkAbstractScheduler); d->ThreadPool->setMaxThreadCount(maximumThreadCount); @@ -335,7 +335,7 @@ int ctkAbstractScheduler::maximumNumberOfRetry() const } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setMaximumNumberOfRetry(const int &maximumNumberOfRetry) +void ctkAbstractScheduler::setMaximumNumberOfRetry(int maximumNumberOfRetry) { Q_D(ctkAbstractScheduler); d->MaximumNumberOfRetry = maximumNumberOfRetry; @@ -349,7 +349,7 @@ int ctkAbstractScheduler::retryDelay() const } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setRetryDelay(const int &retryDelay) +void ctkAbstractScheduler::setRetryDelay(int retryDelay) { Q_D(ctkAbstractScheduler); d->RetryDelay = retryDelay; diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index 0b60e80bfa..976bff0c7c 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -68,21 +68,21 @@ class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject /// Maximum number of concurrent QThreads spawned by the threadPool in the Job pool /// default: 20 int maximumThreadCount() const; - void setMaximumThreadCount(const int& maximumThreadCount); + void setMaximumThreadCount(int maximumThreadCount); ///@} ///@{ /// Maximum number of retries that the Job pool will try on each failed Job /// default: 3 int maximumNumberOfRetry() const; - void setMaximumNumberOfRetry(const int& maximumNumberOfRetry); + void setMaximumNumberOfRetry(int maximumNumberOfRetry); ///@} ///@{ /// Retry delay in millisec /// default: 100 msec int retryDelay() const; - void setRetryDelay(const int& retryDelay); + void setRetryDelay(int retryDelay); ///@} /// Return the threadPool. diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index f96c71d2b9..2fadb6e1e6 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -79,7 +79,7 @@ QMap ctkDICOMQueryJob::filters() const } //------------------------------------------------------------------------------ -void ctkDICOMQueryJob::setMaximumPatientsQuery(const int maximumPatientsQuery) +void ctkDICOMQueryJob::setMaximumPatientsQuery(int maximumPatientsQuery) { Q_D(ctkDICOMQueryJob); d->MaximumPatientsQuery = maximumPatientsQuery; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index a70c769c19..ac676e75cf 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -74,7 +74,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. - void setMaximumPatientsQuery(const int maximumPatientsQuery); + void setMaximumPatientsQuery(int maximumPatientsQuery); int maximumPatientsQuery(); ///@} diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp index a231413769..db1646892b 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.cpp @@ -942,7 +942,7 @@ bool ctkDICOMRetrieve::keepAssociationOpen() const } //----------------------------------------------------------------------------- -void ctkDICOMRetrieve::setConnectionTimeout(const int timeout) +void ctkDICOMRetrieve::setConnectionTimeout(int timeout) { Q_D(ctkDICOMRetrieve); d->SCU.setACSETimeout(timeout); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieve.h b/Libs/DICOM/Core/ctkDICOMRetrieve.h index 0c0fc0e8cb..5e7dadfc0b 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieve.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieve.h @@ -105,7 +105,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieve : public QObject ///@{ /// connection timeout, default 3 sec. - void setConnectionTimeout(const int timeout); + void setConnectionTimeout(int timeout); int connectionTimeout() const; ///@} diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 7155f1d798..b826560e04 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -304,9 +304,9 @@ void ctkDICOMScheduler::retrieveSOPInstance(const QString &patientID, } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::startListener(const int port, - const QString &AETitle, - QThread::Priority priority) +void ctkDICOMScheduler::startListener(int port, + const QString &AETitle, + QThread::Priority priority) { Q_D(ctkDICOMScheduler); @@ -709,7 +709,7 @@ void ctkDICOMScheduler::raiseJobsPriorityForSeries(const QStringList& selectedSe } //------------------------------------------------------------------------------ -void ctkDICOMScheduler::setMaximumPatientsQuery(const int maximumPatientsQuery) +void ctkDICOMScheduler::setMaximumPatientsQuery(int maximumPatientsQuery) { Q_D(ctkDICOMScheduler); d->MaximumPatientsQuery = maximumPatientsQuery; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 28ee12d9d0..ad4c7253a4 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -99,7 +99,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler QThread::Priority priority = QThread::LowPriority); /// Start a storage listener - Q_INVOKABLE void startListener(const int port, + Q_INVOKABLE void startListener(int port, const QString &AETitle, QThread::Priority priority = QThread::LowPriority); @@ -177,7 +177,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler ///@{ /// maximum number of responses allowed in one query /// when query is at Patient level. Default is 25. - void setMaximumPatientsQuery(const int maximumPatientsQuery); + void setMaximumPatientsQuery(int maximumPatientsQuery); int maximumPatientsQuery(); ///@} diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index f60412923d..6b08978981 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -65,7 +65,7 @@ ctkDICOMStorageListenerJob::ctkDICOMStorageListenerJob(ctkDICOMStorageListenerJo ctkDICOMStorageListenerJob::~ctkDICOMStorageListenerJob() = default; //---------------------------------------------------------------------------- -void ctkDICOMStorageListenerJob::setPort(const int port) +void ctkDICOMStorageListenerJob::setPort(int port) { Q_D(ctkDICOMStorageListenerJob); d->Port = port; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 4a445a5d42..54a43b270b 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -51,7 +51,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerJob : public ctkDICOMJob ///@{ /// Port, default: 11112 - void setPort(const int port); + void setPort(int port); int port() const; ///@} diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index b46c6fb482..1a6a2d54ef 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -66,9 +66,9 @@ class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget void drawThumbnail(const QString& file, int numberOfFrames); void drawTextWithShadow(QPainter *painter, const QFont &font, - const int &x, - const int &y, - const Qt::Alignment &alignment, + int x, + int y, + Qt::Alignment alignment, const QString &text); void updateThumbnailProgressBar(); @@ -461,9 +461,9 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num //---------------------------------------------------------------------------- void ctkDICOMSeriesItemWidgetPrivate::drawTextWithShadow(QPainter *painter, const QFont &font, - const int &x, - const int &y, - const Qt::Alignment &alignment, + int x, + int y, + Qt::Alignment alignment, const QString &text) { QColor textColor(60, 164, 255, 225); @@ -730,7 +730,7 @@ bool ctkDICOMSeriesItemWidget::IsVisible() const } //------------------------------------------------------------------------------ -void ctkDICOMSeriesItemWidget::setThumbnailSizePixel(const int &thumbnailSizePixel) +void ctkDICOMSeriesItemWidget::setThumbnailSizePixel(int thumbnailSizePixel) { Q_D(ctkDICOMSeriesItemWidget); d->ThumbnailSizePixel = thumbnailSizePixel; diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index a2f1ad3099..61d0ad5c33 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -130,7 +130,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget ///@{ /// Set the thumbnail size in pixel /// 200 by default - void setThumbnailSizePixel(const int &thumbnailSizePixel); + void setThumbnailSizePixel(int thumbnailSizePixel); int thumbnailSizePixel() const; ///@} diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 4bc3a754c5..605ab9e672 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -1113,7 +1113,7 @@ QString ctkDICOMServerNodeWidget2::storageAETitle()const } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::setStoragePort(const int storagePort) +void ctkDICOMServerNodeWidget2::setStoragePort(int storagePort) { Q_D(const ctkDICOMServerNodeWidget2); d->StoragePort->setText(QString::number(storagePort)); diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index 116bd6112f..cfab067460 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -71,7 +71,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget ///@{ /// Storage port /// 11112 by default - void setStoragePort(const int storagePort); + void setStoragePort(int storagePort); int storagePort() const; ///@} diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 3240c9ae45..585231712b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -70,8 +70,7 @@ class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget int getScreenHeight(); int calculateNumerOfSeriesPerRow(); int calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); - void addEmptySeriesItemWidget(const int& rowIndex, - const int& columnIndex); + void addEmptySeriesItemWidget(int rowIndex, int columnIndex); bool isSeriesItemAlreadyAdded(const QString& seriesItem); QString FilteringSeriesDescription; @@ -315,8 +314,7 @@ int ctkDICOMStudyItemWidgetPrivate::calculateThumbnailSizeInPixel(const ctkDICOM } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(const int& rowIndex, - const int& columnIndex) +void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(int rowIndex, int columnIndex) { QTableWidgetItem *tableItem = new QTableWidgetItem; tableItem->setFlags(Qt::NoItemFlags); @@ -659,7 +657,7 @@ QList ctkDICOMStudyItemWidget::seriesItemWidgetsList } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidget::addSeriesItemWidget(const int& tableIndex, +void ctkDICOMStudyItemWidget::addSeriesItemWidget(int tableIndex, const QString &seriesItem, const QString &seriesInstanceUID, const QString &modality, diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index 5b46743db7..6e443cad59 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -168,7 +168,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget Q_INVOKABLE QList seriesItemWidgetsList()const; /// Add/Remove Series item widget - Q_INVOKABLE void addSeriesItemWidget(const int& tableIndex, + Q_INVOKABLE void addSeriesItemWidget(int tableIndex, const QString &seriesItem, const QString &seriesInstanceUID, const QString& modality, diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 92cc69781d..2cfed1f3fe 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1690,7 +1690,7 @@ QStringList ctkDICOMVisualBrowserWidget::filteringModalities() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setNumberOfStudiesPerPatient(const int &numberOfStudiesPerPatient) +void ctkDICOMVisualBrowserWidget::setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient) { Q_D(ctkDICOMVisualBrowserWidget); d->NumberOfStudiesPerPatient = numberOfStudiesPerPatient; diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 87cd929f29..43b15d1c1b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -186,7 +186,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// Number of non collapsed studies per patient /// 2 by default - void setNumberOfStudiesPerPatient(const int &numberOfStudiesPerPatient); + void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); int numberOfStudiesPerPatient() const; /// Set the thumbnail size: small, medium, large From 4a4421b51dfb43de0f348f19a111200c39bcbb43 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 12:51:36 -0500 Subject: [PATCH 54/73] COMP: Remove used of "const int" for ivars in ctkDICOMServerNodeWidget2 --- .../Widgets/ctkDICOMServerNodeWidget2.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 605ab9e672..c3fd0554fe 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -340,7 +340,7 @@ QMap ctkDICOMServerNodeWidget2Private::parameters()const QStringList ctkDICOMServerNodeWidget2Private::serverNodes()const { QStringList nodes; - const int count = this->NodeTable->rowCount(); + int count = this->NodeTable->rowCount(); for (int row = 0; row < count; ++row) { QTableWidgetItem* item = this->NodeTable->item(row, ctkDICOMServerNodeWidget2::NameColumn); @@ -356,7 +356,7 @@ QStringList ctkDICOMServerNodeWidget2Private::serverNodes()const QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(const QString &connectionName)const { QMap parameters; - const int count = this->NodeTable->rowCount(); + int count = this->NodeTable->rowCount(); for (int row = 0; row < count; ++row) { if (this->NodeTable->item(row, 0)->text() == connectionName) @@ -376,7 +376,7 @@ QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(i { return node; } - const int columnCount = this->NodeTable->columnCount(); + int columnCount = this->NodeTable->columnCount(); for (int column = 0; column < columnCount; ++column) { if (!this->NodeTable->item(row, column)) @@ -421,7 +421,7 @@ QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(i QStringList ctkDICOMServerNodeWidget2Private::getAllNodesName() const { QStringList nodesNames; - const int count = this->NodeTable->rowCount(); + int count = this->NodeTable->rowCount(); for (int row = 0; row < count; ++row) { nodesNames.append(this->NodeTable->item(row, ctkDICOMServerNodeWidget2::NameColumn)->data(Qt::DisplayRole).toString()); @@ -434,7 +434,7 @@ QStringList ctkDICOMServerNodeWidget2Private::getAllNodesName() const int ctkDICOMServerNodeWidget2Private::getServerNodeRowFromConnectionName(const QString &connectionName) const { QMap parameters; - const int count = this->NodeTable->rowCount(); + int count = this->NodeTable->rowCount(); for (int row = 0; row < count; ++row) { if (this->NodeTable->item(row, 0)->text() == connectionName) @@ -468,7 +468,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->rowCount(); + int rowCount = this->NodeTable->rowCount(); this->NodeTable->setRowCount(rowCount + 1); QTableWidgetItem *newItem; @@ -742,7 +742,7 @@ ctkDICOMServerNodeWidget2::~ctkDICOMServerNodeWidget2() int ctkDICOMServerNodeWidget2::onAddServerNode() { Q_D(ctkDICOMServerNodeWidget2); - const int rowCount = d->NodeTable->rowCount(); + int rowCount = d->NodeTable->rowCount(); d->NodeTable->setRowCount(rowCount + 1); QString serverName = "server"; @@ -932,7 +932,7 @@ void ctkDICOMServerNodeWidget2::saveSettings() } QSettings settings; - const int rowCount = d->NodeTable->rowCount(); + int rowCount = d->NodeTable->rowCount(); settings.remove("DICOM/ServerNodes"); this->removeAllServers(); @@ -1070,7 +1070,7 @@ void ctkDICOMServerNodeWidget2::readSettings() d->StorageAETitle->setText(settings.value("DICOM/StorageAETitle").toString()); d->StoragePort->setText(settings.value("DICOM/StoragePort").toString()); - const int count = settings.value("DICOM/ServerNodeCount").toInt(); + int count = settings.value("DICOM/ServerNodeCount").toInt(); for (int row = 0; row < count; ++row) { node = settings.value(QString("DICOM/ServerNodes/%1").arg(row)).toMap(); From fbba856a363fd51105ac4850d9fd6d40e4643b04 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:02:51 -0500 Subject: [PATCH 55/73] COMP: Fix constness of ctkDICOMJobResponseSet::setDatasets --- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index ff9b09520e..36221809d7 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -293,7 +293,7 @@ QSharedPointer ctkDICOMJobResponseSet::datasetShared() const } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::setDatasets(QMap dcmItems, bool takeOwnership) +void ctkDICOMJobResponseSet::setDatasets(const QMap& dcmItems, bool takeOwnership) { Q_D(ctkDICOMJobResponseSet); for(const QString& key : dcmItems.keys()) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index c15deb1b45..32ea1b8624 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -134,7 +134,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_INVOKABLE void setDataset(DcmItem* dcmItem, bool takeOwnership = true); Q_INVOKABLE ctkDICOMItem* dataset() const; QSharedPointer datasetShared() const; - Q_INVOKABLE void setDatasets(QMap dcmItems, bool takeOwnership = true); + Q_INVOKABLE void setDatasets(const QMap& dcmItems, bool takeOwnership = true); Q_INVOKABLE QMap datasets() const; QMap> datasetsShared() const; ///@} From 9b0bc8a9a287aabba624c4edf3d872d1238e33c0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:03:18 -0500 Subject: [PATCH 56/73] COMP: Fix constness of ctkDICOMJob::(copy|set)JobResponseSets --- Libs/DICOM/Core/ctkDICOMJob.cpp | 6 +++--- Libs/DICOM/Core/ctkDICOMJob.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 457751e43e..7c35eb7c94 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -133,7 +133,7 @@ QList> ctkDICOMJob::jobResponseSetsShared } //------------------------------------------------------------------------------ -void ctkDICOMJob::setJobResponseSets(QList jobResponseSets) +void ctkDICOMJob::setJobResponseSets(const QList& jobResponseSets) { this->JobResponseSets.clear(); foreach(ctkDICOMJobResponseSet* jobResponseSet, jobResponseSets) @@ -143,13 +143,13 @@ void ctkDICOMJob::setJobResponseSets(QList jobResponseS } //------------------------------------------------------------------------------ -void ctkDICOMJob::setJobResponseSets(QList> jobResponseSets) +void ctkDICOMJob::setJobResponseSets(const QList>& jobResponseSets) { this->JobResponseSets = jobResponseSets; } //------------------------------------------------------------------------------ -void ctkDICOMJob::copyJobResponseSets(QList> jobResponseSets) +void ctkDICOMJob::copyJobResponseSets(const QList>& jobResponseSets) { this->JobResponseSets.clear(); foreach(QSharedPointer jobResponseSet, jobResponseSets) diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 027af98e6a..1320b06d5e 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -94,9 +94,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob /// Access the list of responses. Q_INVOKABLE QList jobResponseSets() const; QList> jobResponseSetsShared() const; - Q_INVOKABLE void setJobResponseSets(QList jobResponseSets); - void setJobResponseSets(QList> jobResponseSets); - void copyJobResponseSets(QList> jobResponseSets); + Q_INVOKABLE void setJobResponseSets(const QList& jobResponseSets); + void setJobResponseSets(const QList>& jobResponseSets); + void copyJobResponseSets(const QList>& jobResponseSets); ///@} /// Return the QVariant value of this job. From bf286362599c0c09bb59bfc1c5734b0f28b829e8 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:04:50 -0500 Subject: [PATCH 57/73] COMP: Fix more constness in new DICOM widgets accepting QVariant --- Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp | 2 +- Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h | 2 +- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp | 4 ++-- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 4 ++-- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 2 +- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index b373257ee1..39ca650d58 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -688,7 +688,7 @@ void ctkDICOMPatientItemWidget::generateStudies() } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::updateGUIFromScheduler(QVariant data) +void ctkDICOMPatientItemWidget::updateGUIFromScheduler(const QVariant& data) { Q_D(ctkDICOMPatientItemWidget); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index 2b5e79f765..eeb0b9793a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -162,7 +162,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget public Q_SLOTS: void generateStudies(); - void updateGUIFromScheduler(QVariant data); + void updateGUIFromScheduler(const QVariant& data); void onSeriesItemClicked(); void raiseSelectedSeriesJobsPriority(); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 1a6a2d54ef..d867f1aafc 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -865,7 +865,7 @@ void ctkDICOMSeriesItemWidget::generateInstances() } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(QVariant data) +void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(const QVariant& data) { Q_D(ctkDICOMSeriesItemWidget); @@ -885,7 +885,7 @@ void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(QVariant data) } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(QVariant data) +void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(const QVariant& data) { Q_D(ctkDICOMSeriesItemWidget); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index 61d0ad5c33..dd5c90ce42 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -158,8 +158,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget public Q_SLOTS: void generateInstances(); - void updateGUIFromScheduler(QVariant data); - void updateSeriesProgressBar(QVariant data); + void updateGUIFromScheduler(const QVariant& data); + void updateSeriesProgressBar(const QVariant& data); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 585231712b..6f3f5281a1 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -759,7 +759,7 @@ void ctkDICOMStudyItemWidget::generateSeries(bool toggled) } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidget::updateGUIFromScheduler(QVariant data) +void ctkDICOMStudyItemWidget::updateGUIFromScheduler(const QVariant& data) { Q_D(ctkDICOMStudyItemWidget); diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index 6e443cad59..a183964dd0 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -180,7 +180,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget public Q_SLOTS: void generateSeries(bool toggled = true); - void updateGUIFromScheduler(QVariant data); + void updateGUIFromScheduler(const QVariant& data); void onStudySelectionClicked(bool); protected: From 007982f206a10bef13e49e485e2b9d1a6d65ed5a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:05:37 -0500 Subject: [PATCH 58/73] COMP: Fix more constness in DICOM ctkDICOMVisualBrowserWidget methods --- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 22 +++++++++---------- .../Widgets/ctkDICOMVisualBrowserWidget.h | 22 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 2cfed1f3fe..5083ad350d 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -2024,7 +2024,7 @@ void ctkDICOMVisualBrowserWidget::openImportDialog() } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::importDirectories(QStringList directories, ImportDirectoryMode mode) +void ctkDICOMVisualBrowserWidget::importDirectories(const QStringList& directories, ImportDirectoryMode mode) { Q_D(ctkDICOMVisualBrowserWidget); foreach (const QString& directory, directories) @@ -2034,7 +2034,7 @@ void ctkDICOMVisualBrowserWidget::importDirectories(QStringList directories, Imp } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::importDirectory(QString directory, ImportDirectoryMode mode) +void ctkDICOMVisualBrowserWidget::importDirectory(const QString& directory, ImportDirectoryMode mode) { Q_D(ctkDICOMVisualBrowserWidget); d->importDirectory(directory, mode); @@ -2060,7 +2060,7 @@ void ctkDICOMVisualBrowserWidget::waitForImportFinished() } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onImportDirectory(QString directory, ImportDirectoryMode mode) +void ctkDICOMVisualBrowserWidget::onImportDirectory(const QString& directory, ImportDirectoryMode mode) { this->importDirectory(directory, mode); } @@ -2232,7 +2232,7 @@ void ctkDICOMVisualBrowserWidget::updateDatabase() //------------------------------------------------------------------------------ QStringList ctkDICOMVisualBrowserWidget::fileListForCurrentSelection(ctkDICOMModel::IndexType level, - QList selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -2261,7 +2261,7 @@ void ctkDICOMVisualBrowserWidget::showMetadata(const QStringList &fileList) //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType level, - QList selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -2583,7 +2583,7 @@ void ctkDICOMVisualBrowserWidget::onQueryPatients() } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) +void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(const QVariant& data) { Q_D(ctkDICOMVisualBrowserWidget); d->ProgressFrame->hide(); @@ -2617,7 +2617,7 @@ void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QVariant data) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onTaskFailed(QVariant data) +void ctkDICOMVisualBrowserWidget::onTaskFailed(const QVariant& data) { Q_D(ctkDICOMVisualBrowserWidget); ctkDICOMJobDetail td = data.value(); @@ -2978,7 +2978,7 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::exportSelectedItems(ctkDICOMModel::IndexType level, - QList selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -3007,7 +3007,7 @@ void ctkDICOMVisualBrowserWidget::exportSelectedItems(ctkDICOMModel::IndexType l } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids) +void ctkDICOMVisualBrowserWidget::exportSeries(const QString& dirPath, const QStringList& uids) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -3143,7 +3143,7 @@ void ctkDICOMVisualBrowserWidget::exportSeries(QString dirPath, QStringList uids } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onImportDirectoriesSelected(QStringList directories) +void ctkDICOMVisualBrowserWidget::onImportDirectoriesSelected(const QStringList& directories) { Q_D(ctkDICOMVisualBrowserWidget); this->importDirectories(directories, this->importDirectoryMode()); @@ -3250,7 +3250,7 @@ void ctkDICOMVisualBrowserWidget::closeEvent(QCloseEvent *event) } //------------------------------------------------------------------------------ -bool ctkDICOMVisualBrowserWidget::confirmDeleteSelectedUIDs(QStringList uids) +bool ctkDICOMVisualBrowserWidget::confirmDeleteSelectedUIDs(const QStringList& uids) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 43b15d1c1b..540709ec66 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -263,14 +263,14 @@ public Q_SLOTS: /// By default, \a mode is ImportDirectoryMode::ImportDirectoryAddLink is set. /// /// \sa importDirectory(QString directory, int mode) - void importDirectories(QStringList directories, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + void importDirectories(const QStringList& directories, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); /// \brief Import a directory /// /// This can be used to externally trigger an import (i.e. for testing or to support drag-and-drop) /// /// By default, \a mode is ImportDirectoryMode::ImportDirectoryAddLink is set. - void importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + void importDirectory(const QString& directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); /// \brief Import a list of files /// @@ -286,7 +286,7 @@ public Q_SLOTS: void waitForImportFinished(); /// \deprecated importDirectory() should be used - void onImportDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + void onImportDirectory(const QString& directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); /// slots to capture status updates from the database during an /// import operation @@ -313,8 +313,8 @@ public Q_SLOTS: void onFilteringDateComboBoxChanged(int); void onQueryPatients(); void onShowPatients(); - void updateGUIFromScheduler(QVariant); - void onTaskFailed(QVariant); + void updateGUIFromScheduler(const QVariant&); + void onTaskFailed(const QVariant&); void onPatientItemChanged(int); void onClose(); void onLoad(); @@ -342,18 +342,18 @@ public Q_SLOTS: /// empty, uses the UID. /// Returns true if the user confirms the delete, false otherwise. /// Remembers if the user doesn't want to show the confirmation again. - bool confirmDeleteSelectedUIDs(QStringList uids); + bool confirmDeleteSelectedUIDs(const QStringList& uids); /// Get file list for right click selection - QStringList fileListForCurrentSelection(ctkDICOMModel::IndexType level, QList selectedWidget); + QStringList fileListForCurrentSelection(ctkDICOMModel::IndexType level, const QList& selectedWidget); /// Show window that displays DICOM fields of all selected items void showMetadata(const QStringList& fileList); /// Remove items (both database and widget) - void removeSelectedItems(ctkDICOMModel::IndexType level, QList selectedWidgets = QList()); + void removeSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets = QList()); /// Export the items associated with the selected widget - void exportSelectedItems(ctkDICOMModel::IndexType level, QList selectedWidgets); + void exportSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets); /// Export the series associated with the selected UIDs - void exportSeries(QString dirPath, QStringList uids); + void exportSeries(const QString& dirPath, const QStringList& uids); protected Q_SLOTS: ///@{ @@ -363,7 +363,7 @@ protected Q_SLOTS: /// directories from the Import Dialog. /// /// \sa importDirectories(QString directory, int mode) - void onImportDirectoriesSelected(QStringList directories); + void onImportDirectoriesSelected(const QStringList& directories); void onImportDirectoryComboBoxCurrentIndexChanged(int index); ///@} From 2873a57ba9f8be49018d700df385993c771a9540 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:13:28 -0500 Subject: [PATCH 59/73] STYLE: Fix spacing, remove extra EOF lines --- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 1 - Libs/DICOM/Core/ctkDICOMScheduler.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index 989625c560..386223b56f 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -116,4 +116,3 @@ ctkAbstractWorker *ctkDICOMInserterJob::createWorker() worker->setJob(*this); return worker; } - diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index ad4c7253a4..dd86788ea2 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -95,7 +95,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_INVOKABLE void retrieveSOPInstance(const QString& patientID, const QString& studyInstanceUID, const QString& seriesInstanceUID, - const QString &SOPInstanceUID, + const QString& SOPInstanceUID, QThread::Priority priority = QThread::LowPriority); /// Start a storage listener From 749d128f68448dbfd43896842f71adc8422f81ce Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:29:41 -0500 Subject: [PATCH 60/73] COMP: Fix constness of connectionName param in API for set/remove server --- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp | 6 +++--- Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 8 +++++--- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 6 +++--- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 6 +++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index c3fd0554fe..1c0bd9c889 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -1199,7 +1199,7 @@ ctkDICOMServer* ctkDICOMServerNodeWidget2::getNthServer(int id) } //---------------------------------------------------------------------------- -ctkDICOMServer* ctkDICOMServerNodeWidget2::getServer(const char *connectionName) +ctkDICOMServer* ctkDICOMServerNodeWidget2::getServer(const QString& connectionName) { Q_D(ctkDICOMServerNodeWidget2); if (!d->Scheduler) @@ -1226,7 +1226,7 @@ void ctkDICOMServerNodeWidget2::addServer(ctkDICOMServer* server) } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::removeServer(const char *connectionName) +void ctkDICOMServerNodeWidget2::removeServer(const QString& connectionName) { Q_D(ctkDICOMServerNodeWidget2); if (!d->Scheduler) @@ -1281,7 +1281,7 @@ QString ctkDICOMServerNodeWidget2::getServerNameFromIndex(int id) } //---------------------------------------------------------------------------- -int ctkDICOMServerNodeWidget2::getServerIndexFromName(const char *connectionName) +int ctkDICOMServerNodeWidget2::getServerIndexFromName(const QString& connectionName) { Q_D(ctkDICOMServerNodeWidget2); if (!d->Scheduler) diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index cfab067460..69e867eb90 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -87,16 +87,18 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget void setScheduler(QSharedPointer scheduler); /// Servers + ///@{ Q_INVOKABLE int getNumberOfServers(); Q_INVOKABLE ctkDICOMServer* getNthServer(int id); - Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE ctkDICOMServer* getServer(const QString& connectionName); Q_INVOKABLE void addServer(ctkDICOMServer* server); - Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeServer(const QString& connectionName); Q_INVOKABLE void removeNthServer(int id); Q_INVOKABLE void removeAllServers(); Q_INVOKABLE QString getServerNameFromIndex(int id); - Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + Q_INVOKABLE int getServerIndexFromName(const QString& connectionName); Q_INVOKABLE void stopAllJobs(); + ///@} public Q_SLOTS: /// Add an empty server node and make it current diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 5083ad350d..45e924f179 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1537,7 +1537,7 @@ ctkDICOMServer* ctkDICOMVisualBrowserWidget::getNthServer(int id) } //---------------------------------------------------------------------------- -ctkDICOMServer* ctkDICOMVisualBrowserWidget::getServer(const char *connectionName) +ctkDICOMServer* ctkDICOMVisualBrowserWidget::getServer(const QString& connectionName) { Q_D(ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget->getServer(connectionName); @@ -1551,7 +1551,7 @@ void ctkDICOMVisualBrowserWidget::addServer(ctkDICOMServer* server) } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidget::removeServer(const char *connectionName) +void ctkDICOMVisualBrowserWidget::removeServer(const QString& connectionName) { Q_D(ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget->removeServer(connectionName); @@ -1579,7 +1579,7 @@ QString ctkDICOMVisualBrowserWidget::getServerNameFromIndex(int id) } //---------------------------------------------------------------------------- -int ctkDICOMVisualBrowserWidget::getServerIndexFromName(const char *connectionName) +int ctkDICOMVisualBrowserWidget::getServerIndexFromName(const QString& connectionName) { Q_D(ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget->getServerIndexFromName(connectionName); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 540709ec66..f7eef4a1e7 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -147,13 +147,13 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// Servers Q_INVOKABLE int getNumberOfServers(); Q_INVOKABLE ctkDICOMServer* getNthServer(int id); - Q_INVOKABLE ctkDICOMServer* getServer(const char* connectionName); + Q_INVOKABLE ctkDICOMServer* getServer(const QString& connectionName); Q_INVOKABLE void addServer(ctkDICOMServer* server); - Q_INVOKABLE void removeServer(const char* connectionName); + Q_INVOKABLE void removeServer(const QString& connectionName); Q_INVOKABLE void removeNthServer(int id); Q_INVOKABLE void removeAllServers(); Q_INVOKABLE QString getServerNameFromIndex(int id); - Q_INVOKABLE int getServerIndexFromName(const char* connectionName); + Q_INVOKABLE int getServerIndexFromName(const QString& connectionName); Q_INVOKABLE ctkDICOMServerNodeWidget2* serverSettingsWidget(); Q_INVOKABLE ctkCollapsibleGroupBox* serverSettingsGroupBox(); From 57d92c1df5d780afdc8a4fb356f1eb41c4c68666 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:35:51 -0500 Subject: [PATCH 61/73] COMP: Remove "const bool&" in DICOM SeriesItem and VisualBrowser widgets --- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp | 6 +++--- Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h | 6 +++--- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 5 ++--- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index d867f1aafc..f3540872c9 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -667,7 +667,7 @@ QString ctkDICOMSeriesItemWidget::seriesDescription() const } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::setStopJobs(const bool &stopJobs) +void ctkDICOMSeriesItemWidget::setStopJobs(bool stopJobs) { Q_D(ctkDICOMSeriesItemWidget); d->StopJobs = stopJobs; @@ -681,7 +681,7 @@ bool ctkDICOMSeriesItemWidget::stopJobs() const } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::setRaiseJobsPriority(const bool &raiseJobsPriority) +void ctkDICOMSeriesItemWidget::setRaiseJobsPriority(bool raiseJobsPriority) { Q_D(ctkDICOMSeriesItemWidget); d->RaiseJobsPriority = raiseJobsPriority; @@ -702,7 +702,7 @@ bool ctkDICOMSeriesItemWidget::isCloud() const } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::setRetrieveFailed(const bool &retrieveFailed) +void ctkDICOMSeriesItemWidget::setRetrieveFailed(bool retrieveFailed) { Q_D(ctkDICOMSeriesItemWidget); d->RetrieveFailed = retrieveFailed; diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index dd5c90ce42..fadd154770 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -102,13 +102,13 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget ///@{ /// Stop Series widget to run new jobs - void setStopJobs(const bool& stopJobs); + void setStopJobs(bool stopJobs); bool stopJobs() const; ///@} ///@{ /// Set high priority to all jobs run from the Series widget - void setRaiseJobsPriority(const bool& raiseJobsPriority); + void setRaiseJobsPriority(bool raiseJobsPriority); bool raiseJobsPriority() const; ///@} @@ -117,7 +117,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget ///@{ /// in case the retrieve job failed - void setRetrieveFailed(const bool& retrieveFailed); + void setRetrieveFailed(bool retrieveFailed); bool retrieveFailed() const; ///@} diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 45e924f179..b588046a65 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1718,7 +1718,7 @@ ctkDICOMStudyItemWidget::ThumbnailSizeOption ctkDICOMVisualBrowserWidget::thumbn } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setSendActionVisible(const bool &visible) +void ctkDICOMVisualBrowserWidget::setSendActionVisible(bool visible) { Q_D(ctkDICOMVisualBrowserWidget); d->SendActionVisible = visible; @@ -1731,9 +1731,8 @@ bool ctkDICOMVisualBrowserWidget::isSendActionVisible() const return d->SendActionVisible; } - //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setDeleteActionVisible(const bool &visible) +void ctkDICOMVisualBrowserWidget::setDeleteActionVisible(bool visible) { Q_D(ctkDICOMVisualBrowserWidget); d->DeleteActionVisible = visible; diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index f7eef4a1e7..c731867de0 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -196,12 +196,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// Set if send action on right click context menu is available /// false by default - void setSendActionVisible(const bool &visible); + void setSendActionVisible(bool visible); bool isSendActionVisible() const; /// Set if cancel action on right click context menu is available /// true by default - void setDeleteActionVisible(const bool &visible); + void setDeleteActionVisible(bool visible); bool isDeleteActionVisible() const; /// Add/Remove Patient item widget From bbab77b079e6fb0f6f1bdcbd102a6b46b455bc9a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:54:26 -0500 Subject: [PATCH 62/73] COMP: Fix more constness in DICOM ctkDICOMVisualBrowserWidget methods --- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 4 ++-- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index b588046a65..d83f9d0a36 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -1469,7 +1469,7 @@ QSharedPointer ctkDICOMVisualBrowserWidget::dicomDatabaseShare } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidget::setTagsToPrecache(const QStringList tags) +void ctkDICOMVisualBrowserWidget::setTagsToPrecache(const QStringList& tags) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -1495,7 +1495,7 @@ const QStringList ctkDICOMVisualBrowserWidget::tagsToPrecache() } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidget::setStorageAETitle(QString storageAETitle) +void ctkDICOMVisualBrowserWidget::setStorageAETitle(const QString& storageAETitle) { Q_D(const ctkDICOMVisualBrowserWidget); d->ServerNodeWidget->setStorageAETitle(storageAETitle); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index c731867de0..50e5a1467a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -131,12 +131,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// delegate to the corresponding routines of the internal /// instance of the database. /// @see ctkDICOMDatabase - Q_INVOKABLE void setTagsToPrecache(const QStringList tags); + Q_INVOKABLE void setTagsToPrecache(const QStringList& tags); Q_INVOKABLE const QStringList tagsToPrecache(); /// Storage AE title /// "CTKSTORE" by default - void setStorageAETitle(QString storageAETitle); + void setStorageAETitle(const QString& storageAETitle); QString storageAETitle() const; /// Storage port From 8d1a47025b4747b740dbd661b2e1576ef95a7946 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:55:13 -0500 Subject: [PATCH 63/73] DOC: Group docstrings of newly introduced methods in VisualBrowserWidget --- .../Widgets/ctkDICOMVisualBrowserWidget.h | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 50e5a1467a..16fb0d02fb 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -127,23 +127,30 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// (not Python-wrappable). QSharedPointer dicomDatabaseShared() const; + ///@{ /// See ctkDICOMDatabase for description - these accessors /// delegate to the corresponding routines of the internal /// instance of the database. /// @see ctkDICOMDatabase Q_INVOKABLE void setTagsToPrecache(const QStringList& tags); Q_INVOKABLE const QStringList tagsToPrecache(); + ///@} + ///@{ /// Storage AE title /// "CTKSTORE" by default void setStorageAETitle(const QString& storageAETitle); QString storageAETitle() const; + ///@} + ///@{ /// Storage port /// 11112 by default void setStoragePort(int storagePort); int storagePort() const; + ///@} + ///@{ /// Servers Q_INVOKABLE int getNumberOfServers(); Q_INVOKABLE ctkDICOMServer* getNthServer(int id); @@ -156,17 +163,28 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget Q_INVOKABLE int getServerIndexFromName(const QString& connectionName); Q_INVOKABLE ctkDICOMServerNodeWidget2* serverSettingsWidget(); Q_INVOKABLE ctkCollapsibleGroupBox* serverSettingsGroupBox(); + ///@} + ///@{ /// Query Filters /// Empty by default void setFilteringPatientID(const QString& filteringPatientID); QString filteringPatientID() const; + ///@} + + ///@{ /// Empty by default void setFilteringPatientName(const QString& filteringPatientName); QString filteringPatientName() const; + ///@} + + ///@{ /// Empty by default void setFilteringStudyDescription(const QString& filteringStudyDescription); QString filteringStudyDescription() const; + ///@} + + ///@{ /// Available values: /// Any, /// Today, @@ -175,47 +193,67 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// LastMonth, /// LastYear. /// Any by default. + /// \sa ctkDICOMPatientItemWidget::DateType void setFilteringDate(const ctkDICOMPatientItemWidget::DateType& filteringDate); ctkDICOMPatientItemWidget::DateType filteringDate() const; + ///@] + + ///@{ /// Empty by default void setFilteringSeriesDescription(const QString& filteringSeriesDescription); QString filteringSeriesDescription() const; + ///@} + + ///@{ /// ["Any", "CR", "CT", "MR", "NM", "US", "PT", "XA"] by default void setFilteringModalities(const QStringList& filteringModalities); QStringList filteringModalities() const; + ///@} + ///@{ /// Number of non collapsed studies per patient /// 2 by default void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); int numberOfStudiesPerPatient() const; + ///@} + ///@{ /// Set the thumbnail size: small, medium, large /// medium by default void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; + ///@} + ///@{ /// Set if send action on right click context menu is available /// false by default void setSendActionVisible(bool visible); bool isSendActionVisible() const; + ///@} + ///@{ /// Set if cancel action on right click context menu is available /// true by default void setDeleteActionVisible(bool visible); bool isDeleteActionVisible() const; + ///@} + ///@{ /// Add/Remove Patient item widget Q_INVOKABLE void addPatientItemWidget(const QString& patientItem); Q_INVOKABLE void removePatientItemWidget(const QString& patientItem); + ///@} /// Get Patient item widget Q_INVOKABLE ctkDICOMPatientItemWidget* getPatientItemWidgetByPatientName(const QString& patientName); + ///@{ /// Accessors to status of last directory import operation int patientsAddedDuringImport(); int studiesAddedDuringImport(); int seriesAddedDuringImport(); int instancesAddedDuringImport(); + ///@} /// Set counters of imported patients, studies, series, instances to zero. void resetItemsAddedDuringImportCounters(); @@ -288,12 +326,14 @@ public Q_SLOTS: /// \deprecated importDirectory() should be used void onImportDirectory(const QString& directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); + ///@{ /// slots to capture status updates from the database during an /// import operation void onIndexingProgress(int); void onIndexingProgressStep(const QString&); void onIndexingProgressDetail(const QString&); void onIndexingComplete(int patientsAdded, int studiesAdded, int seriesAdded, int imagesAdded); + ///@} /// Show pop-up window for the user to select database directory void selectDatabaseDirectory(); From 0ae38b779723b8f7c1df793a41f420f5b2ee8270 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 13:59:09 -0500 Subject: [PATCH 64/73] COMP: Remove deprecated ctkDICOMVisualBrowserWidget::onImportDirectory --- Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp | 6 ------ Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h | 3 --- 2 files changed, 9 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index d83f9d0a36..6b76311b68 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -2058,12 +2058,6 @@ void ctkDICOMVisualBrowserWidget::waitForImportFinished() d->Indexer->waitForImportFinished(); } -//------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onImportDirectory(const QString& directory, ImportDirectoryMode mode) -{ - this->importDirectory(directory, mode); -} - //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::onIndexingProgress(int percent) { diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 16fb0d02fb..b0d439f22b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -323,9 +323,6 @@ public Q_SLOTS: /// instancesAddedDuringImport() methods. void waitForImportFinished(); - /// \deprecated importDirectory() should be used - void onImportDirectory(const QString& directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = ImportDirectoryAddLink); - ///@{ /// slots to capture status updates from the database during an /// import operation From 7694c2a6ddc25b148739a5e27c22d03c20a65bd4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 14:56:15 -0500 Subject: [PATCH 65/73] COMP: Fix constness of ctkDICOMStorageListener::done() signal --- Libs/DICOM/Core/ctkDICOMStorageListener.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index abb6802be4..59e2fc9d91 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -99,7 +99,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject void error(const QString& message); /// Signal is emitted inside the listener() function when finished with value /// true for success or false for error - void done(const bool& error); + void done(bool error); /// Signal is emitted inside the listener() function when a frame has been fetched void progressJobDetail(QVariant); From 81940a82a9fd83efd2ff8bebca9ba13f97792b56 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 15:08:13 -0500 Subject: [PATCH 66/73] COMP: Rename ctkAbstractScheduler to ctkJobScheduler --- Libs/Core/ctkAbstractScheduler.cpp | 124 +++++++++++++------------- Libs/Core/ctkAbstractScheduler.h | 22 ++--- Libs/Core/ctkAbstractScheduler_p.h | 14 +-- Libs/Core/ctkAbstractWorker.cpp | 12 +-- Libs/Core/ctkAbstractWorker.h | 12 +-- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 2 +- Libs/DICOM/Core/ctkDICOMScheduler.h | 4 +- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 2 +- 8 files changed, 96 insertions(+), 96 deletions(-) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkAbstractScheduler.cpp index fd523b508c..13f732578a 100644 --- a/Libs/Core/ctkAbstractScheduler.cpp +++ b/Libs/Core/ctkAbstractScheduler.cpp @@ -30,29 +30,29 @@ // CTK includes #include "ctkAbstractJob.h" -#include "ctkAbstractScheduler.h" -#include "ctkAbstractScheduler_p.h" +#include "ctkJobScheduler.h" +#include "ctkJobScheduler_p.h" #include "ctkAbstractWorker.h" #include "ctkLogger.h" static ctkLogger logger ("org.commontk.core.AbstractScheduler"); // -------------------------------------------------------------------------- -// ctkAbstractSchedulerPrivate methods +// ctkJobSchedulerPrivate methods // -------------------------------------------------------------------------- -ctkAbstractSchedulerPrivate::ctkAbstractSchedulerPrivate(ctkAbstractScheduler& object) +ctkJobSchedulerPrivate::ctkJobSchedulerPrivate(ctkJobScheduler& object) : q_ptr(&object) { } // -------------------------------------------------------------------------- -ctkAbstractSchedulerPrivate::~ctkAbstractSchedulerPrivate() = default; +ctkJobSchedulerPrivate::~ctkJobSchedulerPrivate() = default; //--------------------------------------------------------------------------- -void ctkAbstractSchedulerPrivate::init() +void ctkJobSchedulerPrivate::init() { - Q_Q(ctkAbstractScheduler); + Q_Q(ctkJobScheduler); QObject::connect(q, SIGNAL(queueJobs()), q, SLOT(onQueueJobsInThreadPool()), @@ -63,16 +63,16 @@ void ctkAbstractSchedulerPrivate::init() } //------------------------------------------------------------------------------ -void ctkAbstractSchedulerPrivate::insertJob(QSharedPointer job) +void ctkJobSchedulerPrivate::insertJob(QSharedPointer job) { - Q_Q(ctkAbstractScheduler); + Q_Q(ctkJobScheduler); if (!job) { return; } - logger.debug(QString("ctkAbstractScheduler: creating job object %1 of type %2 in thread %3.\n") + logger.debug(QString("ctkJobScheduler: creating job object %1 of type %2 in thread %3.\n") .arg(job->jobUID()) .arg(job->className()) .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); @@ -90,11 +90,11 @@ void ctkAbstractSchedulerPrivate::insertJob(QSharedPointer job) } //------------------------------------------------------------------------------ -void ctkAbstractSchedulerPrivate::removeJob(const QString& jobUID) +void ctkJobSchedulerPrivate::removeJob(const QString& jobUID) { - Q_Q(ctkAbstractScheduler); + Q_Q(ctkJobScheduler); - logger.debug(QString("ctkAbstractScheduler: deleting job object %1 in thread %2.\n") + logger.debug(QString("ctkJobScheduler: deleting job object %1 in thread %2.\n") .arg(jobUID) .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); @@ -115,7 +115,7 @@ void ctkAbstractSchedulerPrivate::removeJob(const QString& jobUID) } //------------------------------------------------------------------------------ -int ctkAbstractSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) +int ctkJobSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job) { int count = 0; foreach (QSharedPointer queuedJob, this->JobsQueue) @@ -137,25 +137,25 @@ int ctkAbstractSchedulerPrivate::getSameTypeJobsInThreadPoolQueueOrRunning(QShar } //------------------------------------------------------------------------------ -QString ctkAbstractSchedulerPrivate::generateUniqueJobUID() +QString ctkJobSchedulerPrivate::generateUniqueJobUID() { return QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); } //--------------------------------------------------------------------------- -// ctkAbstractScheduler methods +// ctkJobScheduler methods // -------------------------------------------------------------------------- -ctkAbstractScheduler::ctkAbstractScheduler(QObject* parent) +ctkJobScheduler::ctkJobScheduler(QObject* parent) : QObject(parent) - , d_ptr(new ctkAbstractSchedulerPrivate(*this)) + , d_ptr(new ctkJobSchedulerPrivate(*this)) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); d->init(); } // -------------------------------------------------------------------------- -ctkAbstractScheduler::ctkAbstractScheduler(ctkAbstractSchedulerPrivate* pimpl, QObject* parent) +ctkJobScheduler::ctkJobScheduler(ctkJobSchedulerPrivate* pimpl, QObject* parent) : Superclass(parent) , d_ptr(pimpl) { @@ -164,20 +164,20 @@ ctkAbstractScheduler::ctkAbstractScheduler(ctkAbstractSchedulerPrivate* pimpl, Q } // -------------------------------------------------------------------------- -ctkAbstractScheduler::~ctkAbstractScheduler() = default; +ctkJobScheduler::~ctkJobScheduler() = default; //---------------------------------------------------------------------------- -int ctkAbstractScheduler::numberOfJobs() +int ctkJobScheduler::numberOfJobs() { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QMutexLocker ml(&d->mMutex); return d->JobsQueue.count(); } //---------------------------------------------------------------------------- -int ctkAbstractScheduler::numberOfPersistentJobs() +int ctkJobScheduler::numberOfPersistentJobs() { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); int cont = 0; QMutexLocker ml(&d->mMutex); foreach (QSharedPointer job, d->JobsQueue) @@ -191,25 +191,25 @@ int ctkAbstractScheduler::numberOfPersistentJobs() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::addJob(ctkAbstractJob *job) +void ctkJobScheduler::addJob(ctkAbstractJob *job) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QSharedPointer jobShared = QSharedPointer(job); d->insertJob(jobShared); } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::deleteJob(const QString& jobUID) +void ctkJobScheduler::deleteJob(const QString& jobUID) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); d->removeJob(jobUID); } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::deleteWorker(const QString& jobUID) +void ctkJobScheduler::deleteWorker(const QString& jobUID) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QMap>::iterator it = d->Workers.find(jobUID); if (it == d->Workers.end()) @@ -221,9 +221,9 @@ void ctkAbstractScheduler::deleteWorker(const QString& jobUID) } //---------------------------------------------------------------------------- -QSharedPointer ctkAbstractScheduler::getJobSharedByUID(const QString& jobUID) +QSharedPointer ctkJobScheduler::getJobSharedByUID(const QString& jobUID) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QMutexLocker ml(&d->mMutex); QMap>::iterator it = d->JobsQueue.find(jobUID); @@ -236,7 +236,7 @@ QSharedPointer ctkAbstractScheduler::getJobSharedByUID(const QSt } //---------------------------------------------------------------------------- -ctkAbstractJob *ctkAbstractScheduler::getJobByUID(const QString& jobUID) +ctkAbstractJob *ctkJobScheduler::getJobByUID(const QString& jobUID) { QSharedPointer job = this->getJobSharedByUID(jobUID); if (!job) @@ -248,9 +248,9 @@ ctkAbstractJob *ctkAbstractScheduler::getJobByUID(const QString& jobUID) } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::waitForFinish() +void ctkJobScheduler::waitForFinish() { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); int numberOfPersistentJobs = this->numberOfPersistentJobs(); while(this->numberOfJobs() > numberOfPersistentJobs) @@ -261,18 +261,18 @@ void ctkAbstractScheduler::waitForFinish() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::waitForDone(int msec) +void ctkJobScheduler::waitForDone(int msec) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QCoreApplication::processEvents(); d->ThreadPool->waitForDone(msec); } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::stopAllJobs(bool stopPersistentJobs) +void ctkJobScheduler::stopAllJobs(bool stopPersistentJobs) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QMutexLocker ml(&d->mMutex); @@ -314,63 +314,63 @@ void ctkAbstractScheduler::stopAllJobs(bool stopPersistentJobs) } //---------------------------------------------------------------------------- -int ctkAbstractScheduler::maximumThreadCount() const +int ctkJobScheduler::maximumThreadCount() const { - Q_D(const ctkAbstractScheduler); + Q_D(const ctkJobScheduler); return d->ThreadPool->maxThreadCount(); } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setMaximumThreadCount(int maximumThreadCount) +void ctkJobScheduler::setMaximumThreadCount(int maximumThreadCount) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); d->ThreadPool->setMaxThreadCount(maximumThreadCount); } //---------------------------------------------------------------------------- -int ctkAbstractScheduler::maximumNumberOfRetry() const +int ctkJobScheduler::maximumNumberOfRetry() const { - Q_D(const ctkAbstractScheduler); + Q_D(const ctkJobScheduler); return d->MaximumNumberOfRetry; } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setMaximumNumberOfRetry(int maximumNumberOfRetry) +void ctkJobScheduler::setMaximumNumberOfRetry(int maximumNumberOfRetry) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); d->MaximumNumberOfRetry = maximumNumberOfRetry; } //---------------------------------------------------------------------------- -int ctkAbstractScheduler::retryDelay() const +int ctkJobScheduler::retryDelay() const { - Q_D(const ctkAbstractScheduler); + Q_D(const ctkJobScheduler); return d->RetryDelay; } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::setRetryDelay(int retryDelay) +void ctkJobScheduler::setRetryDelay(int retryDelay) { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); d->RetryDelay = retryDelay; } //---------------------------------------------------------------------------- -QThreadPool *ctkAbstractScheduler::threadPool() const +QThreadPool *ctkJobScheduler::threadPool() const { - Q_D(const ctkAbstractScheduler); + Q_D(const ctkJobScheduler); return d->ThreadPool.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkAbstractScheduler::threadPoolShared() const +QSharedPointer ctkJobScheduler::threadPoolShared() const { - Q_D(const ctkAbstractScheduler); + Q_D(const ctkJobScheduler); return d->ThreadPool; } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::onJobStarted() +void ctkJobScheduler::onJobStarted() { ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) @@ -383,7 +383,7 @@ void ctkAbstractScheduler::onJobStarted() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::onJobCanceled() +void ctkJobScheduler::onJobCanceled() { ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) @@ -395,7 +395,7 @@ void ctkAbstractScheduler::onJobCanceled() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::onJobFailed() +void ctkJobScheduler::onJobFailed() { ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) @@ -414,7 +414,7 @@ void ctkAbstractScheduler::onJobFailed() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::onJobFinished() +void ctkJobScheduler::onJobFinished() { ctkAbstractJob* job = qobject_cast(this->sender()); if (!job) @@ -433,9 +433,9 @@ void ctkAbstractScheduler::onJobFinished() } //---------------------------------------------------------------------------- -void ctkAbstractScheduler::onQueueJobsInThreadPool() +void ctkJobScheduler::onQueueJobsInThreadPool() { - Q_D(ctkAbstractScheduler); + Q_D(ctkJobScheduler); QMutexLocker ml(&d->mMutex); foreach (QThread::Priority priority, (QList() diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkAbstractScheduler.h index 976bff0c7c..4d077698b6 100644 --- a/Libs/Core/ctkAbstractScheduler.h +++ b/Libs/Core/ctkAbstractScheduler.h @@ -21,8 +21,8 @@ =========================================================================*/ -#ifndef __ctkAbstractScheduler_h -#define __ctkAbstractScheduler_h +#ifndef __ctkJobScheduler_h +#define __ctkJobScheduler_h // Qt includes #include @@ -33,17 +33,17 @@ class QThreadPool; // CTK includes #include "ctkCoreExport.h" class ctkAbstractJob; -class ctkAbstractSchedulerPrivate; +class ctkJobSchedulerPrivate; //------------------------------------------------------------------------------ /// \ingroup Core -class CTK_CORE_EXPORT ctkAbstractScheduler : public QObject +class CTK_CORE_EXPORT ctkJobScheduler : public QObject { Q_OBJECT public: typedef QObject Superclass; - explicit ctkAbstractScheduler(QObject* parent = 0); - virtual ~ctkAbstractScheduler(); + explicit ctkJobScheduler(QObject* parent = 0); + virtual ~ctkJobScheduler(); ///@{ /// Jobs managment @@ -108,12 +108,12 @@ public Q_SLOTS: virtual void onQueueJobsInThreadPool(); protected: - QScopedPointer d_ptr; - ctkAbstractScheduler(ctkAbstractSchedulerPrivate* pimpl, QObject* parent); + QScopedPointer d_ptr; + ctkJobScheduler(ctkJobSchedulerPrivate* pimpl, QObject* parent); private: - Q_DECLARE_PRIVATE(ctkAbstractScheduler); - Q_DISABLE_COPY(ctkAbstractScheduler) + Q_DECLARE_PRIVATE(ctkJobScheduler); + Q_DISABLE_COPY(ctkJobScheduler) }; -#endif // ctkAbstractScheduler_h +#endif // ctkJobScheduler_h diff --git a/Libs/Core/ctkAbstractScheduler_p.h b/Libs/Core/ctkAbstractScheduler_p.h index 5c38a85a84..e6ba3308ac 100644 --- a/Libs/Core/ctkAbstractScheduler_p.h +++ b/Libs/Core/ctkAbstractScheduler_p.h @@ -18,8 +18,8 @@ =========================================================================*/ -#ifndef __ctkAbstractSchedulerPrivate_h -#define __ctkAbstractSchedulerPrivate_h +#ifndef __ctkJobSchedulerPrivate_h +#define __ctkJobSchedulerPrivate_h // Qt includes #include @@ -35,17 +35,17 @@ class ctkAbstractWorker; #include "ctkAbstractScheduler.h" //------------------------------------------------------------------------------ -class CTK_CORE_EXPORT ctkAbstractSchedulerPrivate : public QObject +class CTK_CORE_EXPORT ctkJobSchedulerPrivate : public QObject { Q_OBJECT - Q_DECLARE_PUBLIC(ctkAbstractScheduler) + Q_DECLARE_PUBLIC(ctkJobScheduler) protected: - ctkAbstractScheduler* const q_ptr; + ctkJobScheduler* const q_ptr; public: - ctkAbstractSchedulerPrivate(ctkAbstractScheduler& object); - virtual ~ctkAbstractSchedulerPrivate(); + ctkJobSchedulerPrivate(ctkJobScheduler& object); + virtual ~ctkJobSchedulerPrivate(); /// Convenient setup methods virtual void init(); diff --git a/Libs/Core/ctkAbstractWorker.cpp b/Libs/Core/ctkAbstractWorker.cpp index 154fb11207..fdddcd6505 100644 --- a/Libs/Core/ctkAbstractWorker.cpp +++ b/Libs/Core/ctkAbstractWorker.cpp @@ -27,7 +27,7 @@ // CTK includes #include "ctkAbstractJob.h" -#include "ctkAbstractScheduler.h" +#include "ctkJobScheduler.h" #include "ctkAbstractWorker.h" // -------------------------------------------------------------------------- @@ -72,25 +72,25 @@ void ctkAbstractWorker::setJob(QSharedPointer job) } //---------------------------------------------------------------------------- -ctkAbstractScheduler *ctkAbstractWorker::scheduler() const +ctkJobScheduler *ctkAbstractWorker::scheduler() const { return this->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkAbstractWorker::schedulerShared() const +QSharedPointer ctkAbstractWorker::schedulerShared() const { return this->Scheduler; } //---------------------------------------------------------------------------- -void ctkAbstractWorker::setScheduler(ctkAbstractScheduler &scheduler) +void ctkAbstractWorker::setScheduler(ctkJobScheduler &scheduler) { - this->Scheduler = QSharedPointer(&scheduler, skipDelete); + this->Scheduler = QSharedPointer(&scheduler, skipDelete); } //---------------------------------------------------------------------------- -void ctkAbstractWorker::setScheduler(QSharedPointer scheduler) +void ctkAbstractWorker::setScheduler(QSharedPointer scheduler) { this->Scheduler = scheduler; } diff --git a/Libs/Core/ctkAbstractWorker.h b/Libs/Core/ctkAbstractWorker.h index 457a74ce28..d73abb26d1 100644 --- a/Libs/Core/ctkAbstractWorker.h +++ b/Libs/Core/ctkAbstractWorker.h @@ -32,7 +32,7 @@ #include "ctkCoreExport.h" class ctkAbstractJob; -class ctkAbstractScheduler; +class ctkJobScheduler; //------------------------------------------------------------------------------ /// \ingroup Core @@ -60,10 +60,10 @@ class CTK_CORE_EXPORT ctkAbstractWorker : public QObject, public QRunnable ///@{ /// Scheduler - Q_INVOKABLE ctkAbstractScheduler* scheduler() const; - QSharedPointer schedulerShared() const; - Q_INVOKABLE void setScheduler(ctkAbstractScheduler& scheduler); - void setScheduler(QSharedPointer scheduler); + Q_INVOKABLE ctkJobScheduler* scheduler() const; + QSharedPointer schedulerShared() const; + Q_INVOKABLE void setScheduler(ctkJobScheduler& scheduler); + void setScheduler(QSharedPointer scheduler); ///@} public slots: @@ -72,7 +72,7 @@ public slots: protected: QSharedPointer Job; - QSharedPointer Scheduler; + QSharedPointer Scheduler; private: Q_DISABLE_COPY(ctkAbstractWorker) diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index b826560e04..742c6063f7 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -48,7 +48,7 @@ static ctkLogger logger ( "org.commontk.dicom.DICOMJobPool" ); //------------------------------------------------------------------------------ ctkDICOMSchedulerPrivate::ctkDICOMSchedulerPrivate(ctkDICOMScheduler& obj) - : ctkAbstractSchedulerPrivate(obj) + : ctkJobSchedulerPrivate(obj) { ctk::setDICOMLogLevel(ctkErrorLogLevel::Warning); } diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index dd86788ea2..5defd54638 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -42,7 +42,7 @@ class ctkDICOMServer; class ctkDICOMStorageListenerJob; /// \ingroup DICOM_Core -class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler +class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkJobScheduler { Q_OBJECT Q_PROPERTY(int maximumThreadCount READ maximumThreadCount WRITE setMaximumThreadCount); @@ -51,7 +51,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkAbstractScheduler Q_PROPERTY(int maximumPatientsQuery READ maximumPatientsQuery WRITE setMaximumPatientsQuery); public: - typedef ctkAbstractScheduler Superclass; + typedef ctkJobScheduler Superclass; explicit ctkDICOMScheduler(QObject* parent = 0); virtual ~ctkDICOMScheduler(); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index 1d454d08b7..af5ee17f88 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -41,7 +41,7 @@ struct ThumbnailUID } ; //------------------------------------------------------------------------------ -class ctkDICOMSchedulerPrivate : public ctkAbstractSchedulerPrivate +class ctkDICOMSchedulerPrivate : public ctkJobSchedulerPrivate { Q_OBJECT Q_DECLARE_PUBLIC(ctkDICOMScheduler); From 71ed8f0a1ab703c47718db76555d05670ff76952 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 15:09:55 -0500 Subject: [PATCH 67/73] COMP: Rename ctkAbstractScheduler files to ctkJobScheduler --- Libs/Core/CMakeLists.txt | 14 +++++++------- ...tkAbstractScheduler.cpp => ctkJobScheduler.cpp} | 0 .../{ctkAbstractScheduler.h => ctkJobScheduler.h} | 0 ...tkAbstractScheduler_p.h => ctkJobScheduler_p.h} | 2 +- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 2 +- Libs/DICOM/Core/ctkDICOMScheduler.h | 2 +- Libs/DICOM/Core/ctkDICOMScheduler_p.h | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) rename Libs/Core/{ctkAbstractScheduler.cpp => ctkJobScheduler.cpp} (100%) rename Libs/Core/{ctkAbstractScheduler.h => ctkJobScheduler.h} (100%) rename Libs/Core/{ctkAbstractScheduler_p.h => ctkJobScheduler_p.h} (98%) diff --git a/Libs/Core/CMakeLists.txt b/Libs/Core/CMakeLists.txt index a2cc3d10f3..de35547864 100644 --- a/Libs/Core/CMakeLists.txt +++ b/Libs/Core/CMakeLists.txt @@ -32,9 +32,6 @@ set(KIT_SRCS ctkAbstractQObjectFactory.tpp ctkAbstractLibraryFactory.h ctkAbstractLibraryFactory.tpp - ctkAbstractScheduler.cpp - ctkAbstractScheduler.h - ctkAbstractScheduler_p.h ctkAbstractWorker.cpp ctkAbstractWorker.h ctkBackTrace.cpp @@ -72,6 +69,9 @@ set(KIT_SRCS ctkFileLogger.cpp ctkFileLogger.h ctkHighPrecisionTimer.cpp + ctkJobScheduler.cpp + ctkJobScheduler.h + ctkJobScheduler_p.h ctkLinearValueProxy.cpp ctkLinearValueProxy.h ctkLogger.cpp @@ -108,8 +108,6 @@ endif() # Headers that should run through moc set(KIT_MOC_SRCS ctkAbstractJob.h - ctkAbstractScheduler.h - ctkAbstractScheduler_p.h ctkAbstractWorker.h ctkBooleanMapper.h ctkCallback.h @@ -122,6 +120,8 @@ set(KIT_MOC_SRCS ctkErrorLogQtMessageHandler.h ctkErrorLogTerminalOutput.h ctkFileLogger.h + ctkJobScheduler.h + ctkJobScheduler_p.h ctkLinearValueProxy.h ctkLogger.h ctkModelTester.h @@ -136,9 +136,9 @@ set(KIT_MOC_SRCS # Abstract class should not be wrapped ! set_source_files_properties( ctkAbstractJob.h - ctkAbstractScheduler.h - ctkAbstractScheduler_p.h ctkAbstractWorker.h + ctkJobScheduler.h + ctkJobScheduler_p.h WRAP_EXCLUDE ) diff --git a/Libs/Core/ctkAbstractScheduler.cpp b/Libs/Core/ctkJobScheduler.cpp similarity index 100% rename from Libs/Core/ctkAbstractScheduler.cpp rename to Libs/Core/ctkJobScheduler.cpp diff --git a/Libs/Core/ctkAbstractScheduler.h b/Libs/Core/ctkJobScheduler.h similarity index 100% rename from Libs/Core/ctkAbstractScheduler.h rename to Libs/Core/ctkJobScheduler.h diff --git a/Libs/Core/ctkAbstractScheduler_p.h b/Libs/Core/ctkJobScheduler_p.h similarity index 98% rename from Libs/Core/ctkAbstractScheduler_p.h rename to Libs/Core/ctkJobScheduler_p.h index e6ba3308ac..49191836d5 100644 --- a/Libs/Core/ctkAbstractScheduler_p.h +++ b/Libs/Core/ctkJobScheduler_p.h @@ -32,7 +32,7 @@ class ctkAbstractJob; class ctkAbstractWorker; // ctkDICOMCore includes -#include "ctkAbstractScheduler.h" +#include "ctkJobScheduler.h" //------------------------------------------------------------------------------ class CTK_CORE_EXPORT ctkJobSchedulerPrivate : public QObject diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index 742c6063f7..c5403600b1 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -23,7 +23,7 @@ // ctkCore includes #include -#include +#include #include // ctkDICOMCore includes diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 5defd54638..4100547e34 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -29,7 +29,7 @@ #include // ctkCore includes -#include +#include class ctkAbstractJob; // ctkDICOMCore includes diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index af5ee17f88..e17575d2af 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -25,7 +25,7 @@ #define __ctkDICOMQueryJobPrivate_h // ctkCore includes -#include +#include class ctkAbstractJob; class ctkAbstractWorker; From f8bf2f35dd848dfa43db286e4c134f08412c6766 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 14:52:19 -0500 Subject: [PATCH 68/73] COMP: Simplify copy of JobResponseSet introducing clone() --- Libs/DICOM/Core/ctkDICOMItem.cpp | 9 +++ Libs/DICOM/Core/ctkDICOMItem.h | 5 +- Libs/DICOM/Core/ctkDICOMJob.cpp | 3 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 70 ++++++++-------------- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 4 +- 5 files changed, 41 insertions(+), 50 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMItem.cpp b/Libs/DICOM/Core/ctkDICOMItem.cpp index 17b5549e3a..518a017b81 100644 --- a/Libs/DICOM/Core/ctkDICOMItem.cpp +++ b/Libs/DICOM/Core/ctkDICOMItem.cpp @@ -129,6 +129,15 @@ void ctkDICOMItem::InitializeFromFile(const QString& filename, InitializeFromItem(dataset, true); } +ctkDICOMItem* ctkDICOMItem::Clone() +{ + Q_D(ctkDICOMItem); + ctkDICOMItem* newItem = new ctkDICOMItem; + DcmItem* dcmItem = dynamic_cast(d->m_DcmItem->clone()); + newItem->InitializeFromItem(dcmItem, true); + return newItem; +} + void ctkDICOMItem::Serialize() { Q_D(ctkDICOMItem); diff --git a/Libs/DICOM/Core/ctkDICOMItem.h b/Libs/DICOM/Core/ctkDICOMItem.h index d139c4c123..9a42b73bd1 100644 --- a/Libs/DICOM/Core/ctkDICOMItem.h +++ b/Libs/DICOM/Core/ctkDICOMItem.h @@ -96,7 +96,10 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMItem const Uint32 maxReadLength = DCM_MaxReadLength, const E_FileReadMode readMode = ERM_autoDetect); - + /// \brief Clone this object. + /// + /// \returns deep copy of this object. + ctkDICOMItem* Clone(); /// \brief Save dataset to file /// diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 7c35eb7c94..2ab85bd8da 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -155,8 +155,7 @@ void ctkDICOMJob::copyJobResponseSets(const QList jobResponseSet, jobResponseSets) { QSharedPointer jobResponseSetCopy = - QSharedPointer(new ctkDICOMJobResponseSet); - jobResponseSetCopy->deepCopy(jobResponseSet.data()); + QSharedPointer(jobResponseSet->clone()); this->JobResponseSets.append(jobResponseSetCopy); } } diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index 36221809d7..d5ab9007c8 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -340,56 +340,36 @@ QMap> ctkDICOMJobResponseSet::datasetsShar } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::deepCopy(ctkDICOMJobResponseSet *node) -{ - Q_D(ctkDICOMJobResponseSet); - - if (!node) +ctkDICOMJobResponseSet* ctkDICOMJobResponseSet::clone() +{ + ctkDICOMJobResponseSet* newJobResponseSet = new ctkDICOMJobResponseSet; + + newJobResponseSet->setFilePath(this->filePath()); + newJobResponseSet->setCopyFile(this->copyFile()); + newJobResponseSet->setOverwriteExistingDataset(this->overwriteExistingDataset()); + newJobResponseSet->setJobType(this->jobType()); + newJobResponseSet->setJobUID(this->jobUID()); + newJobResponseSet->setPatientID(this->patientID()); + newJobResponseSet->setStudyInstanceUID(this->studyInstanceUID()); + newJobResponseSet->setSeriesInstanceUID(this->seriesInstanceUID()); + newJobResponseSet->setSOPInstanceUID(this->sopInstanceUID()); + newJobResponseSet->setConnectionName(this->connectionName()); + + // Clone datasets + QMap datasets = this->datasets(); + for(const QString& key : datasets.keys()) { - return; - } - - this->setFilePath(node->filePath()); - this->setCopyFile(node->copyFile()); - this->setOverwriteExistingDataset(node->overwriteExistingDataset()); - this->setJobType(node->jobType()); - this->setJobUID(node->jobUID()); - this->setPatientID(node->patientID()); - this->setStudyInstanceUID(node->studyInstanceUID()); - this->setSeriesInstanceUID(node->seriesInstanceUID()); - this->setSOPInstanceUID(node->sopInstanceUID()); - this->setConnectionName(node->connectionName()); - - d->Datasets.clear(); - - QMap nodeDatasets = node->datasets(); - for(const QString& key : nodeDatasets.keys()) - { - ctkDICOMItem* nodeDataset = nodeDatasets.value(key); - if (!nodeDataset) + ctkDICOMItem* dataset = datasets.value(key); + if (!dataset) { continue; } - - DcmItem* nodedcmItem = nodeDataset->GetDcmItemPointer(); - DcmDataset* nodedcmDataset = dynamic_cast(nodedcmItem); - DcmItem* dcmItem = nullptr; - if (nodedcmDataset) - { - dcmItem = new DcmDataset(); - dcmItem->copyFrom(*nodedcmDataset); - } - else - { - dcmItem = new DcmItem(); - dcmItem->copyFrom(*nodedcmItem); - } - - QSharedPointer dataset = - QSharedPointer(new ctkDICOMItem); - dataset->InitializeFromItem(dcmItem, true); - d->Datasets.insert(key, dataset); + QSharedPointer newDataset = + QSharedPointer(dataset->Clone()); + newJobResponseSet->d_func()->Datasets.insert(key, newDataset); } + + return newJobResponseSet; } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 32ea1b8624..65c3d4e1d6 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -139,8 +139,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject QMap> datasetsShared() const; ///@} - /// Copy object - Q_INVOKABLE void deepCopy(ctkDICOMJobResponseSet* node); + /// Create a copy of this JobResponseSet. + Q_INVOKABLE ctkDICOMJobResponseSet* clone(); /// Return the QVariant value of this JobResponseSet. /// From 92ebce8c512ccdd24f3ac1ad8390e6d4349d086d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 15:37:18 -0500 Subject: [PATCH 69/73] DOC: Add comment in ctkDICOMItem::Clone() --- Libs/DICOM/Core/ctkDICOMItem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Libs/DICOM/Core/ctkDICOMItem.cpp b/Libs/DICOM/Core/ctkDICOMItem.cpp index 518a017b81..2eea84c65a 100644 --- a/Libs/DICOM/Core/ctkDICOMItem.cpp +++ b/Libs/DICOM/Core/ctkDICOMItem.cpp @@ -133,6 +133,9 @@ ctkDICOMItem* ctkDICOMItem::Clone() { Q_D(ctkDICOMItem); ctkDICOMItem* newItem = new ctkDICOMItem; + // Given that we exclusively handle `DcmItem` and `DcmDataset` (where `DcmDataset` + // inherits from `DcmItem`), we safely assume that casting from `DcmObject` to + // `DcmItem` is always valid. DcmItem* dcmItem = dynamic_cast(d->m_DcmItem->clone()); newItem->InitializeFromItem(dcmItem, true); return newItem; From 3040abc63c71178142ba885973d04700574527a1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 16:25:17 -0500 Subject: [PATCH 70/73] STYLE: Cleanup includes --- Libs/DICOM/Core/ctkDICOMEcho.cpp | 10 ++-------- Libs/DICOM/Core/ctkDICOMEcho.h | 3 +-- Libs/DICOM/Core/ctkDICOMInserter.cpp | 2 +- Libs/DICOM/Core/ctkDICOMInserter.h | 3 --- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 8 ++++---- Libs/DICOM/Core/ctkDICOMInserterWorker.h | 3 --- Libs/DICOM/Core/ctkDICOMInserterWorker_p.h | 4 ++++ Libs/DICOM/Core/ctkDICOMJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJob.h | 2 -- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 1 + Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 1 + Libs/DICOM/Core/ctkDICOMQueryJob.h | 4 ++-- Libs/DICOM/Core/ctkDICOMQueryJob_p.h | 5 +++++ Libs/DICOM/Core/ctkDICOMQueryWorker.h | 3 +-- Libs/DICOM/Core/ctkDICOMQueryWorker_p.h | 3 +++ Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h | 5 +++++ Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h | 4 ++++ Libs/DICOM/Core/ctkDICOMScheduler_p.h | 9 +++++++++ Libs/DICOM/Core/ctkDICOMServer.cpp | 1 + Libs/DICOM/Core/ctkDICOMServer.h | 1 - Libs/DICOM/Core/ctkDICOMStorageListenerJob.h | 1 + 22 files changed, 47 insertions(+), 30 deletions(-) diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp index b111e17d84..901d2dc01b 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.cpp +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -29,16 +29,10 @@ #include "ctkLogger.h" // DCMTK includes +#include #include -#include -#include -#include -#include -#include -#include -#include #include /* for class OFStandard */ -#include /* for class DicomDirInterface */ +#include static ctkLogger logger ( "org.commontk.dicom.DICOMEcho" ); diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h index 3298774c22..46bdd5b31d 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.h +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -22,8 +22,8 @@ #define __ctkDICOMEcho_h // Qt includes -#include #include +#include #include // CTK includes @@ -31,7 +31,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkErrorLogLevel.h" class ctkDICOMEchoPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMInserter.cpp b/Libs/DICOM/Core/ctkDICOMInserter.cpp index 4c5580ce3e..16968112fe 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserter.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMInserter.h" #include "ctkDICOMJobResponseSet.h" -static ctkLogger logger ( "org.commontk.dicom.DICOMQuery" ); +static ctkLogger logger ("org.commontk.dicom.DICOMQuery"); //------------------------------------------------------------------------------ class ctkDICOMInserterPrivate diff --git a/Libs/DICOM/Core/ctkDICOMInserter.h b/Libs/DICOM/Core/ctkDICOMInserter.h index fe41f3bfcf..1e81551af6 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.h +++ b/Libs/DICOM/Core/ctkDICOMInserter.h @@ -27,9 +27,6 @@ // Qt includes #include -// ctkCore includes -#include - // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index 386223b56f..452d1a05ff 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -26,7 +26,7 @@ #include "ctkDICOMInserterWorker.h" #include "ctkLogger.h" -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMInserterJob" ); +static ctkLogger logger ("org.commontk.dicom.ctkDICOMInserterJob"); //------------------------------------------------------------------------------ // ctkDICOMInserterJob methods diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 5a450a0b9e..0781befeac 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -24,13 +24,13 @@ // Qt includes #include +// ctkCore includes +#include + // ctkDICOMCore includes -#include "ctkDICOMDatabase.h" -#include "ctkDICOMInserterWorker_p.h" #include "ctkDICOMInserterJob.h" +#include "ctkDICOMInserterWorker_p.h" #include "ctkDICOMJobResponseSet.h" -#include "ctkDICOMScheduler.h" -#include "ctkLogger.h" static ctkLogger logger ("org.commontk.dicom.ctkDICOMInserterWorker"); diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 325cc47dd2..2b4385439e 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -26,14 +26,11 @@ // Qt includes #include -#include -#include #include // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" #include "ctkDICOMWorker.h" -class ctkDICOMDatabase; class ctkDICOMInserter; class ctkDICOMInserterWorkerPrivate; diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h index df0110b29c..42822c5857 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker_p.h @@ -24,6 +24,10 @@ #ifndef __ctkDICOMInserterWorkerPrivate_h #define __ctkDICOMInserterWorkerPrivate_h +// Qt includes +#include +#include + // ctkDICOMCore includes #include "ctkDICOMInserter.h" #include "ctkDICOMInserterWorker.h" diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 2ab85bd8da..65d8b1f7f9 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -28,7 +28,7 @@ #include "ctkDICOMJob.h" #include "ctkDICOMJobResponseSet.h" -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMJob" ); +static ctkLogger logger ("org.commontk.dicom.ctkDICOMJob"); //------------------------------------------------------------------------------ // ctkDICOMJob methods diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 1320b06d5e..0638491fe4 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -34,8 +34,6 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" - -class ctkDICOMServer; class ctkDICOMJobResponseSet; /// \ingroup DICOM_Core diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 65c3d4e1d6..647cfcd51f 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -156,6 +156,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject Q_DISABLE_COPY(ctkDICOMJobResponseSet); }; +//------------------------------------------------------------------------------ struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail { explicit ctkDICOMJobDetail() = default; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index 2fadb6e1e6..a59c07841a 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -177,6 +177,7 @@ QString ctkDICOMQueryJob::loggerReport(const QString &status) const return QString(""); } } + //------------------------------------------------------------------------------ ctkAbstractJob* ctkDICOMQueryJob::clone() const { diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index ac676e75cf..5cdf78c8c4 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -25,10 +25,10 @@ #define __ctkDICOMQueryJob_h // Qt includes -#include #include -#include +#include #include +#include // ctkCore includes class ctkAbstractWorker; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob_p.h b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h index 3d9a60a8fb..8a34f04f40 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob_p.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob_p.h @@ -24,6 +24,11 @@ #ifndef __ctkDICOMQueryJobPrivate_h #define __ctkDICOMQueryJobPrivate_h +// Qt includes +#include +#include +#include + // ctkDICOMCore includes #include "ctkDICOMQueryJob.h" diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index 4c4aac4a93..e9fd769df6 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -25,9 +25,8 @@ #define __ctkDICOMQueryWorker_h // Qt includes -#include #include -#include +#include #include // ctkDICOMCore includes diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h index f11830fe3a..cba54cac5a 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker_p.h @@ -24,6 +24,9 @@ #ifndef __ctkDICOMQueryWorkerPrivate_h #define __ctkDICOMQueryWorkerPrivate_h +// Qt includes +#include + // ctkDICOMCore includes #include "ctkDICOMQuery.h" #include "ctkDICOMQueryWorker.h" diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h index 7a3b91b732..20b7ea9580 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob_p.h @@ -24,8 +24,13 @@ #ifndef __ctkDICOMRetrieveJobPrivate_h #define __ctkDICOMRetrieveJobPrivate_h +// Qt includes +#include +#include + // ctkDICOMCore includes #include "ctkDICOMRetrieveJob.h" +#include "ctkDICOMServer.h" //------------------------------------------------------------------------------ class ctkDICOMRetrieveJobPrivate : public QObject diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h index 9f1c5ca0b9..a0ba9e6d8c 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker_p.h @@ -24,6 +24,10 @@ #ifndef __ctkDICOMRetrieveWorkerPrivate_h #define __ctkDICOMRetrieveWorkerPrivate_h +// Qt includes +#include +#include + // ctkDICOMCore includes #include "ctkDICOMRetrieve.h" #include "ctkDICOMRetrieveWorker.h" diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index e17575d2af..710a506ab4 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -24,10 +24,19 @@ #ifndef __ctkDICOMQueryJobPrivate_h #define __ctkDICOMQueryJobPrivate_h +// Qt includes +#include +#include +#include +#include +class QVariant; + // ctkCore includes #include class ctkAbstractJob; class ctkAbstractWorker; +class ctkDICOMDatabase; +class ctkDICOMServer; // ctkDICOMCore includes #include "ctkDICOMScheduler.h" diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index 00ad205c61..ae7a606b3a 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -23,6 +23,7 @@ // Qt includes #include +#include // ctkCore includes #include diff --git a/Libs/DICOM/Core/ctkDICOMServer.h b/Libs/DICOM/Core/ctkDICOMServer.h index fcb48c2599..ee62acc8b5 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.h +++ b/Libs/DICOM/Core/ctkDICOMServer.h @@ -151,5 +151,4 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMServer : public QObject Q_DISABLE_COPY(ctkDICOMServer); }; - #endif diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h index 54a43b270b..1ea17734e3 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.h @@ -27,6 +27,7 @@ // Qt includes #include #include +#include // ctkCore includes class ctkAbstractWorker; From 3e025b84e6810412645352c74cb5f6fa3d9f0b84 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 17:34:08 -0500 Subject: [PATCH 71/73] STYLE: Fix line endings --- Applications/ctkDICOMVisualBrowser/target_libraries.cmake | 2 +- Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 1 - Libs/DICOM/Widgets/Resources/UI/Icons/add.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/load.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/query.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/save.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg | 2 +- Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg | 2 +- 10 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Applications/ctkDICOMVisualBrowser/target_libraries.cmake b/Applications/ctkDICOMVisualBrowser/target_libraries.cmake index 014ce6da4a..0b3ea7caa4 100644 --- a/Applications/ctkDICOMVisualBrowser/target_libraries.cmake +++ b/Applications/ctkDICOMVisualBrowser/target_libraries.cmake @@ -6,4 +6,4 @@ set(target_libraries CTKDICOMWidgets - ) \ No newline at end of file + ) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index d7a6db83a5..268323aebb 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -165,4 +165,3 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { return EXIT_SUCCESS; } - diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg index 846383523a..0b2996f8db 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/add.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg index 560d174b9b..de34deff91 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/delete.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg index c386420774..3d4d3dc1b3 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/dns.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg index 4e5d743153..48afa908b9 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/load.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg index c611282e40..2f1acace02 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/query.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg index 9cc945f49f..198d3b8d56 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/save.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg index 8c2108c746..88455731b1 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/text_document.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg index 5b6554fd6a..69e6392135 100644 --- a/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/wait.svg @@ -1 +1 @@ - \ No newline at end of file + From 61f8d36a5c6142df914787719a26adc4fd0d59da Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Thu, 18 Jan 2024 23:38:44 +0100 Subject: [PATCH 72/73] STYLE: Remove ctkDICOMWorker class --- Libs/DICOM/Core/CMakeLists.txt | 3 -- Libs/DICOM/Core/ctkDICOMInserterWorker.h | 6 +-- Libs/DICOM/Core/ctkDICOMQueryWorker.h | 6 +-- Libs/DICOM/Core/ctkDICOMRetrieveWorker.h | 6 +-- .../Core/ctkDICOMStorageListenerWorker.h | 6 +-- Libs/DICOM/Core/ctkDICOMWorker.cpp | 31 ----------- Libs/DICOM/Core/ctkDICOMWorker.h | 52 ------------------- 7 files changed, 12 insertions(+), 98 deletions(-) delete mode 100644 Libs/DICOM/Core/ctkDICOMWorker.cpp delete mode 100644 Libs/DICOM/Core/ctkDICOMWorker.h diff --git a/Libs/DICOM/Core/CMakeLists.txt b/Libs/DICOM/Core/CMakeLists.txt index 49199036b9..fbf02cd2e2 100644 --- a/Libs/DICOM/Core/CMakeLists.txt +++ b/Libs/DICOM/Core/CMakeLists.txt @@ -88,8 +88,6 @@ set(KIT_SRCS ctkDICOMDisplayedFieldGeneratorStudyNumberOfSeriesRule.cpp ctkDICOMDisplayedFieldGeneratorPatientNumberOfStudiesRule.h ctkDICOMDisplayedFieldGeneratorPatientNumberOfStudiesRule.cpp - ctkDICOMWorker.cpp - ctkDICOMWorker.h ) # Abstract class should not be wrapped ! @@ -136,7 +134,6 @@ set(KIT_MOC_SRCS ctkDICOMStorageListenerWorker.h ctkDICOMStorageListenerWorker_p.h ctkDICOMTester.h - ctkDICOMWorker.h ) # UI files diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.h b/Libs/DICOM/Core/ctkDICOMInserterWorker.h index 2b4385439e..ffd710aa7d 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.h +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.h @@ -30,17 +30,17 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkDICOMWorker.h" +#include "ctkAbstractWorker.h" class ctkDICOMInserter; class ctkDICOMInserterWorkerPrivate; /// \ingroup DICOM_Core -class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkDICOMWorker +class CTK_DICOM_CORE_EXPORT ctkDICOMInserterWorker : public ctkAbstractWorker { Q_OBJECT public: - typedef ctkDICOMWorker Superclass; + typedef ctkAbstractWorker Superclass; explicit ctkDICOMInserterWorker(); virtual ~ctkDICOMInserterWorker(); diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.h b/Libs/DICOM/Core/ctkDICOMQueryWorker.h index e9fd769df6..b49f9a7c03 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.h +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.h @@ -31,17 +31,17 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkDICOMWorker.h" +#include "ctkAbstractWorker.h" class ctkDICOMQuery; class ctkDICOMQueryWorkerPrivate; /// \ingroup DICOM_Core -class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkDICOMWorker +class CTK_DICOM_CORE_EXPORT ctkDICOMQueryWorker : public ctkAbstractWorker { Q_OBJECT public: - typedef ctkDICOMWorker Superclass; + typedef ctkAbstractWorker Superclass; explicit ctkDICOMQueryWorker(); virtual ~ctkDICOMQueryWorker(); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h index 6f8c72f43d..1eacc8cce0 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.h @@ -30,17 +30,17 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkDICOMWorker.h" +#include "ctkAbstractWorker.h" class ctkDICOMRetrieve; class ctkDICOMRetrieveWorkerPrivate; /// \ingroup DICOM_Core -class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkDICOMWorker +class CTK_DICOM_CORE_EXPORT ctkDICOMRetrieveWorker : public ctkAbstractWorker { Q_OBJECT public: - typedef ctkDICOMWorker Superclass; + typedef ctkAbstractWorker Superclass; explicit ctkDICOMRetrieveWorker(); virtual ~ctkDICOMRetrieveWorker(); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h index 5264e1caf5..e890054dbe 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.h @@ -31,17 +31,17 @@ // ctkDICOMCore includes #include "ctkDICOMCoreExport.h" -#include "ctkDICOMWorker.h" +#include "ctkAbstractWorker.h" class ctkDICOMStorageListener; class ctkDICOMStorageListenerWorkerPrivate; /// \ingroup DICOM_Core -class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkDICOMWorker +class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListenerWorker : public ctkAbstractWorker { Q_OBJECT public: - typedef ctkDICOMWorker Superclass; + typedef ctkAbstractWorker Superclass; explicit ctkDICOMStorageListenerWorker(); virtual ~ctkDICOMStorageListenerWorker(); diff --git a/Libs/DICOM/Core/ctkDICOMWorker.cpp b/Libs/DICOM/Core/ctkDICOMWorker.cpp deleted file mode 100644 index f2abd4336f..0000000000 --- a/Libs/DICOM/Core/ctkDICOMWorker.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - This file was originally developed by Davide Punzo, punzodavide@hotmail.it, - and development was supported by the Center for Intelligent Image-guided Interventions (CI3). - -=========================================================================*/ - -// ctkDICOMCore includes -#include "ctkDICOMWorker.h" - -//------------------------------------------------------------------------------ -ctkDICOMWorker::ctkDICOMWorker() = default; - -//------------------------------------------------------------------------------ -ctkDICOMWorker::~ctkDICOMWorker() = default; diff --git a/Libs/DICOM/Core/ctkDICOMWorker.h b/Libs/DICOM/Core/ctkDICOMWorker.h deleted file mode 100644 index d3eeaae742..0000000000 --- a/Libs/DICOM/Core/ctkDICOMWorker.h +++ /dev/null @@ -1,52 +0,0 @@ -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - This file was originally developed by Davide Punzo, punzodavide@hotmail.it, - and development was supported by the Center for Intelligent Image-guided Interventions (CI3). - -=========================================================================*/ - -#ifndef __ctkDICOMWorker_h -#define __ctkDICOMWorker_h - -// Qt includes -#include - -// ctkDICOMCore includes -#include "ctkDICOMCoreExport.h" -#include "ctkAbstractWorker.h" -class ctkDICOMJob; -class ctkDICOMScheduler; - -//------------------------------------------------------------------------------ -/// \ingroup Core -class CTK_DICOM_CORE_EXPORT ctkDICOMWorker : public ctkAbstractWorker -{ - Q_OBJECT - -public: - typedef ctkAbstractWorker Superclass; - explicit ctkDICOMWorker(); - virtual ~ctkDICOMWorker(); - -private: - Q_DISABLE_COPY(ctkDICOMWorker) -}; - - -#endif // ctkDICOMWorker_h From e1f97cb602426a1705ad485fa1140b2954925b1f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Thu, 18 Jan 2024 20:10:29 -0500 Subject: [PATCH 73/73] STYLE: Seletively apply clang reformatting As a step toward automating styling in CTK & Slicer, the most common styling practices accross CTK & Slicer code base have been applied. --- Libs/Core/ctkJobScheduler.cpp | 20 +- Libs/Core/ctkJobScheduler_p.h | 4 +- .../Core/Testing/Cpp/ctkDICOMEchoTest1.cpp | 4 +- .../Cpp/ctkDICOMJobResponseSetTest1.cpp | 5 +- .../Core/Testing/Cpp/ctkDICOMJobTest1.cpp | 4 +- .../Testing/Cpp/ctkDICOMSchedulerTest1.cpp | 6 +- .../Core/Testing/Cpp/ctkDICOMServerTest1.cpp | 4 +- Libs/DICOM/Core/ctkDICOMEcho.cpp | 8 +- Libs/DICOM/Core/ctkDICOMEcho.h | 2 +- Libs/DICOM/Core/ctkDICOMInserter.cpp | 8 +- Libs/DICOM/Core/ctkDICOMInserterJob.cpp | 12 +- Libs/DICOM/Core/ctkDICOMInserterWorker.cpp | 2 +- Libs/DICOM/Core/ctkDICOMJob.cpp | 14 +- Libs/DICOM/Core/ctkDICOMJob.h | 3 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp | 24 +- Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 6 +- Libs/DICOM/Core/ctkDICOMQueryJob.cpp | 10 +- Libs/DICOM/Core/ctkDICOMQueryJob.h | 4 +- Libs/DICOM/Core/ctkDICOMQueryWorker.cpp | 4 +- Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp | 8 +- Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp | 4 +- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 66 ++-- Libs/DICOM/Core/ctkDICOMScheduler.h | 6 +- Libs/DICOM/Core/ctkDICOMServer.cpp | 12 +- Libs/DICOM/Core/ctkDICOMStorageListener.cpp | 239 ++++++------- Libs/DICOM/Core/ctkDICOMStorageListener.h | 32 +- .../DICOM/Core/ctkDICOMStorageListenerJob.cpp | 8 +- .../Core/ctkDICOMStorageListenerWorker.cpp | 6 +- .../ctkDICOMVisualBrowserWidgetPlugin.cpp | 4 +- .../ctkDICOMVisualBrowserWidgetPlugin.h | 4 +- .../Cpp/ctkDICOMPatientItemWidgetTest1.cpp | 2 +- .../Cpp/ctkDICOMSeriesItemWidgetTest1.cpp | 2 +- .../Cpp/ctkDICOMServerNodeWidget2Test1.cpp | 2 +- .../Cpp/ctkDICOMStudyItemWidgetTest1.cpp | 2 +- .../Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp | 25 +- .../Widgets/ctkDICOMPatientItemWidget.cpp | 56 +-- .../DICOM/Widgets/ctkDICOMPatientItemWidget.h | 8 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 26 +- .../Widgets/ctkDICOMServerNodeWidget2.cpp | 116 +++---- .../DICOM/Widgets/ctkDICOMServerNodeWidget2.h | 5 +- .../DICOM/Widgets/ctkDICOMStudyItemWidget.cpp | 62 ++-- Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h | 8 +- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 318 +++++++++--------- .../Widgets/ctkDICOMVisualBrowserWidget.h | 16 +- 44 files changed, 601 insertions(+), 580 deletions(-) diff --git a/Libs/Core/ctkJobScheduler.cpp b/Libs/Core/ctkJobScheduler.cpp index 13f732578a..141e813314 100644 --- a/Libs/Core/ctkJobScheduler.cpp +++ b/Libs/Core/ctkJobScheduler.cpp @@ -35,7 +35,7 @@ #include "ctkAbstractWorker.h" #include "ctkLogger.h" -static ctkLogger logger ("org.commontk.core.AbstractScheduler"); +static ctkLogger logger("org.commontk.core.AbstractScheduler"); // -------------------------------------------------------------------------- // ctkJobSchedulerPrivate methods @@ -52,20 +52,20 @@ ctkJobSchedulerPrivate::~ctkJobSchedulerPrivate() = default; //--------------------------------------------------------------------------- void ctkJobSchedulerPrivate::init() { - Q_Q(ctkJobScheduler); + Q_Q(ctkJobScheduler); QObject::connect(q, SIGNAL(queueJobs()), q, SLOT(onQueueJobsInThreadPool()), Qt::QueuedConnection); - this->ThreadPool = QSharedPointer (new QThreadPool()); + this->ThreadPool = QSharedPointer(new QThreadPool()); this->ThreadPool->setMaxThreadCount(20); } //------------------------------------------------------------------------------ void ctkJobSchedulerPrivate::insertJob(QSharedPointer job) { - Q_Q(ctkJobScheduler); + Q_Q(ctkJobScheduler); if (!job) { @@ -92,7 +92,7 @@ void ctkJobSchedulerPrivate::insertJob(QSharedPointer job) //------------------------------------------------------------------------------ void ctkJobSchedulerPrivate::removeJob(const QString& jobUID) { - Q_Q(ctkJobScheduler); + Q_Q(ctkJobScheduler); logger.debug(QString("ctkJobScheduler: deleting job object %1 in thread %2.\n") .arg(jobUID) @@ -191,7 +191,7 @@ int ctkJobScheduler::numberOfPersistentJobs() } //---------------------------------------------------------------------------- -void ctkJobScheduler::addJob(ctkAbstractJob *job) +void ctkJobScheduler::addJob(ctkAbstractJob* job) { Q_D(ctkJobScheduler); @@ -236,7 +236,7 @@ QSharedPointer ctkJobScheduler::getJobSharedByUID(const QString& } //---------------------------------------------------------------------------- -ctkAbstractJob *ctkJobScheduler::getJobByUID(const QString& jobUID) +ctkAbstractJob* ctkJobScheduler::getJobByUID(const QString& jobUID) { QSharedPointer job = this->getJobSharedByUID(jobUID); if (!job) @@ -253,7 +253,7 @@ void ctkJobScheduler::waitForFinish() Q_D(ctkJobScheduler); int numberOfPersistentJobs = this->numberOfPersistentJobs(); - while(this->numberOfJobs() > numberOfPersistentJobs) + while (this->numberOfJobs() > numberOfPersistentJobs) { QCoreApplication::processEvents(); d->ThreadPool->waitForDone(300); @@ -356,7 +356,7 @@ void ctkJobScheduler::setRetryDelay(int retryDelay) } //---------------------------------------------------------------------------- -QThreadPool *ctkJobScheduler::threadPool() const +QThreadPool* ctkJobScheduler::threadPool() const { Q_D(const ctkJobScheduler); return d->ThreadPool.data(); @@ -445,7 +445,7 @@ void ctkJobScheduler::onQueueJobsInThreadPool() << QThread::Priority::LowPriority << QThread::Priority::LowestPriority)) { - foreach(QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { if (job->priority() != priority) { diff --git a/Libs/Core/ctkJobScheduler_p.h b/Libs/Core/ctkJobScheduler_p.h index 49191836d5..0c3553a0ef 100644 --- a/Libs/Core/ctkJobScheduler_p.h +++ b/Libs/Core/ctkJobScheduler_p.h @@ -38,10 +38,10 @@ class ctkAbstractWorker; class CTK_CORE_EXPORT ctkJobSchedulerPrivate : public QObject { Q_OBJECT - Q_DECLARE_PUBLIC(ctkJobScheduler) + Q_DECLARE_PUBLIC(ctkJobScheduler) protected: - ctkJobScheduler* const q_ptr; + ctkJobScheduler* const q_ptr; public: ctkJobSchedulerPrivate(ctkJobScheduler& object); diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp index fd3f819109..bb3fe2af79 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMEchoTest1.cpp @@ -37,8 +37,8 @@ // STD includes #include -int ctkDICOMEchoTest1(int argc, char * argv []) { - +int ctkDICOMEchoTest1(int argc, char* argv[]) +{ QCoreApplication app(argc, argv); QStringList arguments = app.arguments(); diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp index e8610d426a..b653adc4b2 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobResponseSetTest1.cpp @@ -31,9 +31,8 @@ // ctkDICOMCore includes #include "ctkDICOMJobResponseSet.h" - -int ctkDICOMJobResponseSetTest1(int argc, char * argv []) { - +int ctkDICOMJobResponseSetTest1(int argc, char* argv[]) +{ QCoreApplication app(argc, argv); // Query Job and virtual parents (ctkDICOMJob and ctkAbstractJob) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp index cd0ec0cf80..f3fa8893a0 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMJobTest1.cpp @@ -35,8 +35,8 @@ #include "ctkDICOMServer.h" #include "ctkDICOMStorageListenerJob.h" -int ctkDICOMJobTest1(int argc, char * argv []) { - +int ctkDICOMJobTest1(int argc, char* argv[]) +{ QCoreApplication app(argc, argv); // Query Job and virtual parents (ctkDICOMJob and ctkAbstractJob) diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp index 268323aebb..6acc63722d 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMSchedulerTest1.cpp @@ -34,8 +34,8 @@ #include "ctkDICOMServer.h" #include "ctkDICOMTester.h" -int ctkDICOMSchedulerTest1(int argc, char * argv []) { - +int ctkDICOMSchedulerTest1(int argc, char* argv[]) +{ QCoreApplication app(argc, argv); QStringList arguments = app.arguments(); @@ -137,7 +137,7 @@ int ctkDICOMSchedulerTest1(int argc, char * argv []) { QStringList urls = database.urlsForSeries(seriesIstanceUID); urls.removeAll(QString("")); - CHECK_INT(instances.count(),numberOfImages); + CHECK_INT(instances.count(), numberOfImages); CHECK_INT(files.count(), 0); CHECK_INT(urls.count(), numberOfImages); diff --git a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp index 1c7edf6777..c98573047c 100644 --- a/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp +++ b/Libs/DICOM/Core/Testing/Cpp/ctkDICOMServerTest1.cpp @@ -31,8 +31,8 @@ // ctkDICOMCore includes #include "ctkDICOMServer.h" -int ctkDICOMServerTest1(int argc, char * argv []) { - +int ctkDICOMServerTest1(int argc, char* argv[]) +{ QCoreApplication app(argc, argv); ctkDICOMServer server; diff --git a/Libs/DICOM/Core/ctkDICOMEcho.cpp b/Libs/DICOM/Core/ctkDICOMEcho.cpp index 901d2dc01b..ad635ae387 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.cpp +++ b/Libs/DICOM/Core/ctkDICOMEcho.cpp @@ -31,10 +31,10 @@ // DCMTK includes #include #include -#include /* for class OFStandard */ +#include /* for class OFStandard */ #include -static ctkLogger logger ( "org.commontk.dicom.DICOMEcho" ); +static ctkLogger logger("org.commontk.dicom.DICOMEcho"); //------------------------------------------------------------------------------ class ctkDICOMEchoPrivate @@ -48,7 +48,7 @@ class ctkDICOMEchoPrivate QString CalledAETitle; QString Host; int Port; - DcmSCU SCU; + DcmSCU SCU; }; //------------------------------------------------------------------------------ @@ -119,7 +119,7 @@ void ctkDICOMEcho::setCalledAETitle(const QString& calledAETitle) } //------------------------------------------------------------------------------ -QString ctkDICOMEcho::calledAETitle()const +QString ctkDICOMEcho::calledAETitle() const { Q_D(const ctkDICOMEcho); return d->CalledAETitle; diff --git a/Libs/DICOM/Core/ctkDICOMEcho.h b/Libs/DICOM/Core/ctkDICOMEcho.h index 46bdd5b31d..296a098b02 100644 --- a/Libs/DICOM/Core/ctkDICOMEcho.h +++ b/Libs/DICOM/Core/ctkDICOMEcho.h @@ -59,7 +59,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMEcho : public QObject /// Set methods for connectivity. /// Empty by default void setCallingAETitle(const QString& callingAETitle); - QString callingAETitle()const; + QString callingAETitle() const; void setCalledAETitle(const QString& calledAETitle); QString calledAETitle() const; diff --git a/Libs/DICOM/Core/ctkDICOMInserter.cpp b/Libs/DICOM/Core/ctkDICOMInserter.cpp index 16968112fe..ceaf811b27 100644 --- a/Libs/DICOM/Core/ctkDICOMInserter.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserter.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMInserter.h" #include "ctkDICOMJobResponseSet.h" -static ctkLogger logger ("org.commontk.dicom.DICOMQuery"); +static ctkLogger logger("org.commontk.dicom.DICOMQuery"); //------------------------------------------------------------------------------ class ctkDICOMInserterPrivate @@ -68,7 +68,7 @@ ctkDICOMInserter::ctkDICOMInserter(QObject* parentObject) ctkDICOMInserter::~ctkDICOMInserter() = default; //------------------------------------------------------------------------------ -void ctkDICOMInserter::setDatabaseFilename(const QString &databaseFilename) +void ctkDICOMInserter::setDatabaseFilename(const QString& databaseFilename) { Q_D(ctkDICOMInserter); d->DatabaseFilename = databaseFilename; @@ -82,7 +82,7 @@ QString ctkDICOMInserter::databaseFilename() const } //------------------------------------------------------------------------------ -void ctkDICOMInserter::setTagsToPrecache(const QStringList &tagsToPrecache) +void ctkDICOMInserter::setTagsToPrecache(const QStringList& tagsToPrecache) { Q_D(ctkDICOMInserter); d->TagsToPrecache = tagsToPrecache; @@ -96,7 +96,7 @@ QStringList ctkDICOMInserter::tagsToPrecache() const } //------------------------------------------------------------------------------ -void ctkDICOMInserter::setTagsToExcludeFromStorage(const QStringList &tagsToExcludeFromStorage) +void ctkDICOMInserter::setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage) { Q_D(ctkDICOMInserter); d->TagsToExcludeFromStorage = tagsToExcludeFromStorage; diff --git a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp index 452d1a05ff..0e5c9d638b 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterJob.cpp @@ -26,7 +26,7 @@ #include "ctkDICOMInserterWorker.h" #include "ctkLogger.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMInserterJob"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMInserterJob"); //------------------------------------------------------------------------------ // ctkDICOMInserterJob methods @@ -42,7 +42,7 @@ ctkDICOMInserterJob::ctkDICOMInserterJob() ctkDICOMInserterJob::~ctkDICOMInserterJob() = default; //------------------------------------------------------------------------------ -QString ctkDICOMInserterJob::loggerReport(const QString &status) const +QString ctkDICOMInserterJob::loggerReport(const QString& status) const { return QString("ctkDICOMInserterJob: insert job %1.\n" "Number of jobResponseSet to process: %2\n") @@ -51,7 +51,7 @@ QString ctkDICOMInserterJob::loggerReport(const QString &status) const } //------------------------------------------------------------------------------ -void ctkDICOMInserterJob::setDatabaseFilename(const QString &databaseFilename) +void ctkDICOMInserterJob::setDatabaseFilename(const QString& databaseFilename) { this->DatabaseFilename = databaseFilename; } @@ -63,7 +63,7 @@ QString ctkDICOMInserterJob::databaseFilename() const } //------------------------------------------------------------------------------ -void ctkDICOMInserterJob::setTagsToPrecache(const QStringList &tagsToPrecache) +void ctkDICOMInserterJob::setTagsToPrecache(const QStringList& tagsToPrecache) { this->TagsToPrecache = tagsToPrecache; } @@ -75,7 +75,7 @@ QStringList ctkDICOMInserterJob::tagsToPrecache() const } //------------------------------------------------------------------------------ -void ctkDICOMInserterJob::setTagsToExcludeFromStorage(const QStringList &tagsToExcludeFromStorage) +void ctkDICOMInserterJob::setTagsToExcludeFromStorage(const QStringList& tagsToExcludeFromStorage) { this->TagsToExcludeFromStorage = tagsToExcludeFromStorage; } @@ -109,7 +109,7 @@ ctkAbstractJob* ctkDICOMInserterJob::clone() const } //------------------------------------------------------------------------------ -ctkAbstractWorker *ctkDICOMInserterJob::createWorker() +ctkAbstractWorker* ctkDICOMInserterJob::createWorker() { ctkDICOMInserterWorker* worker = new ctkDICOMInserterWorker; diff --git a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp index 0781befeac..5d1fdf7045 100644 --- a/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMInserterWorker.cpp @@ -32,7 +32,7 @@ #include "ctkDICOMInserterWorker_p.h" #include "ctkDICOMJobResponseSet.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMInserterWorker"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMInserterWorker"); //------------------------------------------------------------------------------ // ctkDICOMInserterWorkerPrivate methods diff --git a/Libs/DICOM/Core/ctkDICOMJob.cpp b/Libs/DICOM/Core/ctkDICOMJob.cpp index 65d8b1f7f9..4fc5d8ce3c 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMJob.cpp @@ -28,7 +28,7 @@ #include "ctkDICOMJob.h" #include "ctkDICOMJobResponseSet.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMJob"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMJob"); //------------------------------------------------------------------------------ // ctkDICOMJob methods @@ -59,7 +59,7 @@ ctkDICOMJob::DICOMLevels ctkDICOMJob::dicomLevel() const } //---------------------------------------------------------------------------- -void ctkDICOMJob::setPatientID(const QString &patientID) +void ctkDICOMJob::setPatientID(const QString& patientID) { this->PatientID = patientID; } @@ -115,10 +115,10 @@ static void skipDelete(QObject* obj) } //------------------------------------------------------------------------------ -QList ctkDICOMJob::jobResponseSets() const +QList ctkDICOMJob::jobResponseSets() const { - QList jobResponseSets; - foreach(QSharedPointer jobResponseSet, this->JobResponseSets) + QList jobResponseSets; + foreach (QSharedPointer jobResponseSet, this->JobResponseSets) { jobResponseSets.append(jobResponseSet.data()); } @@ -136,7 +136,7 @@ QList> ctkDICOMJob::jobResponseSetsShared void ctkDICOMJob::setJobResponseSets(const QList& jobResponseSets) { this->JobResponseSets.clear(); - foreach(ctkDICOMJobResponseSet* jobResponseSet, jobResponseSets) + foreach (ctkDICOMJobResponseSet* jobResponseSet, jobResponseSets) { this->JobResponseSets.append(QSharedPointer(jobResponseSet, skipDelete)); } @@ -152,7 +152,7 @@ void ctkDICOMJob::setJobResponseSets(const QList>& jobResponseSets) { this->JobResponseSets.clear(); - foreach(QSharedPointer jobResponseSet, jobResponseSets) + foreach (QSharedPointer jobResponseSet, jobResponseSets) { QSharedPointer jobResponseSetCopy = QSharedPointer(jobResponseSet->clone()); diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 0638491fe4..daaca74bfb 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -51,7 +51,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob explicit ctkDICOMJob(); virtual ~ctkDICOMJob(); - enum DICOMLevels{ + enum DICOMLevels + { Patients, Studies, Series, diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp index d5ab9007c8..17f41565e6 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.cpp @@ -34,7 +34,7 @@ static ctkLogger logger("org.commontk.dicom.DICOMTaskResults"); //------------------------------------------------------------------------------ -class ctkDICOMJobResponseSetPrivate: public QObject +class ctkDICOMJobResponseSetPrivate : public QObject { Q_DECLARE_PUBLIC(ctkDICOMJobResponseSet); @@ -98,7 +98,7 @@ ctkDICOMJobResponseSet::ctkDICOMJobResponseSet(QObject* parent) ctkDICOMJobResponseSet::~ctkDICOMJobResponseSet() = default; //---------------------------------------------------------------------------- -void ctkDICOMJobResponseSet::setFilePath(const QString &filePath) +void ctkDICOMJobResponseSet::setFilePath(const QString& filePath) { Q_D(ctkDICOMJobResponseSet); d->FilePath = filePath; @@ -169,7 +169,7 @@ ctkDICOMJobResponseSet::JobType ctkDICOMJobResponseSet::jobType() const } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::setJobUID(const QString &jobUID) +void ctkDICOMJobResponseSet::setJobUID(const QString& jobUID) { Q_D(ctkDICOMJobResponseSet); d->JobUID = jobUID; @@ -239,7 +239,7 @@ QString ctkDICOMJobResponseSet::sopInstanceUID() const } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::setConnectionName(const QString &connectionName) +void ctkDICOMJobResponseSet::setConnectionName(const QString& connectionName) { Q_D(ctkDICOMJobResponseSet); d->ConnectionName = connectionName; @@ -253,7 +253,7 @@ QString ctkDICOMJobResponseSet::connectionName() const } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::setDataset(DcmItem *dcmItem, bool takeOwnership) +void ctkDICOMJobResponseSet::setDataset(DcmItem* dcmItem, bool takeOwnership) { Q_D(ctkDICOMJobResponseSet); if (!dcmItem) @@ -271,7 +271,7 @@ void ctkDICOMJobResponseSet::setDataset(DcmItem *dcmItem, bool takeOwnership) } //------------------------------------------------------------------------------ -ctkDICOMItem *ctkDICOMJobResponseSet::dataset() const +ctkDICOMItem* ctkDICOMJobResponseSet::dataset() const { Q_D(const ctkDICOMJobResponseSet); if (d->Datasets.count() == 0) @@ -293,12 +293,12 @@ QSharedPointer ctkDICOMJobResponseSet::datasetShared() const } //------------------------------------------------------------------------------ -void ctkDICOMJobResponseSet::setDatasets(const QMap& dcmItems, bool takeOwnership) +void ctkDICOMJobResponseSet::setDatasets(const QMap& dcmItems, bool takeOwnership) { Q_D(ctkDICOMJobResponseSet); - for(const QString& key : dcmItems.keys()) + for (const QString& key : dcmItems.keys()) { - DcmItem *dcmItem = dcmItems.value(key); + DcmItem* dcmItem = dcmItems.value(key); if (!dcmItem) { continue; @@ -318,7 +318,7 @@ QMap ctkDICOMJobResponseSet::datasets() const Q_D(const ctkDICOMJobResponseSet); QMap datasets; - for(const QString& key : d->Datasets.keys()) + for (const QString& key : d->Datasets.keys()) { QSharedPointer dcmItem = d->Datasets.value(key); if (!dcmItem) @@ -356,8 +356,8 @@ ctkDICOMJobResponseSet* ctkDICOMJobResponseSet::clone() newJobResponseSet->setConnectionName(this->connectionName()); // Clone datasets - QMap datasets = this->datasets(); - for(const QString& key : datasets.keys()) + QMap datasets = this->datasets(); + for (const QString& key : datasets.keys()) { ctkDICOMItem* dataset = datasets.value(key); if (!dataset) diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 647cfcd51f..d4062515f7 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -78,7 +78,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject ///@{ /// Job type - enum JobType { + enum JobType + { None = 0, QueryPatients, QueryStudies, @@ -157,7 +158,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject }; //------------------------------------------------------------------------------ -struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail { +struct CTK_DICOM_CORE_EXPORT ctkDICOMJobDetail : ctkJobDetail +{ explicit ctkDICOMJobDetail() = default; explicit ctkDICOMJobDetail(const ctkDICOMJob& job) : ctkJobDetail(job) diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp index a59c07841a..1755be4836 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMQueryWorker.h" #include "ctkDICOMServer.h" -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMQueryJob" ); +static ctkLogger logger("org.commontk.dicom.ctkDICOMQueryJob"); //------------------------------------------------------------------------------ // ctkDICOMQueryJobPrivate methods @@ -65,7 +65,7 @@ ctkDICOMQueryJob::ctkDICOMQueryJob(ctkDICOMQueryJobPrivate* pimpl) ctkDICOMQueryJob::~ctkDICOMQueryJob() = default; //---------------------------------------------------------------------------- -void ctkDICOMQueryJob::setFilters(const QMap &filters) +void ctkDICOMQueryJob::setFilters(const QMap& filters) { Q_D(ctkDICOMQueryJob); d->Filters = filters; @@ -115,7 +115,7 @@ QSharedPointer ctkDICOMQueryJob::serverShared() const } //---------------------------------------------------------------------------- -void ctkDICOMQueryJob::setServer(ctkDICOMServer &server) +void ctkDICOMQueryJob::setServer(ctkDICOMServer& server) { Q_D(ctkDICOMQueryJob); d->Server = QSharedPointer(&server, skipDelete); @@ -129,7 +129,7 @@ void ctkDICOMQueryJob::setServer(QSharedPointer server) } //---------------------------------------------------------------------------- -QString ctkDICOMQueryJob::loggerReport(const QString &status) const +QString ctkDICOMQueryJob::loggerReport(const QString& status) const { switch (this->dicomLevel()) { @@ -201,7 +201,7 @@ ctkAbstractJob* ctkDICOMQueryJob::clone() const } //------------------------------------------------------------------------------ -ctkAbstractWorker *ctkDICOMQueryJob::createWorker() +ctkAbstractWorker* ctkDICOMQueryJob::createWorker() { ctkDICOMQueryWorker* worker = new ctkDICOMQueryWorker; diff --git a/Libs/DICOM/Core/ctkDICOMQueryJob.h b/Libs/DICOM/Core/ctkDICOMQueryJob.h index 5cdf78c8c4..a709bb3988 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryJob.h +++ b/Libs/DICOM/Core/ctkDICOMQueryJob.h @@ -67,8 +67,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQueryJob : public ctkDICOMJob /// StartDate DCM_StudyDate QString 20090101 /// EndDate DCM_StudyDate QString 20091231 /// No filter (empty) by default. - Q_INVOKABLE void setFilters(const QMap &filters); - Q_INVOKABLE QMap filters()const; + Q_INVOKABLE void setFilters(const QMap& filters); + Q_INVOKABLE QMap filters() const; ///@} ///@{ diff --git a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp index c68000567f..3c7aa721a7 100644 --- a/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMQueryWorker.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMScheduler.h" #include "ctkDICOMServer.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMQueryWorker"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMQueryWorker"); //------------------------------------------------------------------------------ // ctkDICOMQueryWorkerPrivate methods @@ -134,7 +134,7 @@ void ctkDICOMQueryWorker::run() .arg(queryJob->jobUID()) .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); - switch(queryJob->dicomLevel()) + switch (queryJob->dicomLevel()) { case ctkDICOMJob::DICOMLevels::Patients: if (!d->Query->queryPatients()) diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp index b3152b6b04..5621e9d961 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveJob.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMRetrieveWorker.h" #include "ctkDICOMServer.h" -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMRetrieveJob" ); +static ctkLogger logger("org.commontk.dicom.ctkDICOMRetrieveJob"); //------------------------------------------------------------------------------ // ctkDICOMRetrieveJobPrivate methods @@ -86,7 +86,7 @@ QSharedPointer ctkDICOMRetrieveJob::serverShared() const } //---------------------------------------------------------------------------- -void ctkDICOMRetrieveJob::setServer(ctkDICOMServer &server) +void ctkDICOMRetrieveJob::setServer(ctkDICOMServer& server) { Q_D(ctkDICOMRetrieveJob); d->Server = QSharedPointer(&server, skipDelete); @@ -100,7 +100,7 @@ void ctkDICOMRetrieveJob::setServer(QSharedPointer server) } //---------------------------------------------------------------------------- -QString ctkDICOMRetrieveJob::loggerReport(const QString &status) const +QString ctkDICOMRetrieveJob::loggerReport(const QString& status) const { switch (this->dicomLevel()) { @@ -177,7 +177,7 @@ ctkAbstractJob* ctkDICOMRetrieveJob::clone() const } //------------------------------------------------------------------------------ -ctkAbstractWorker *ctkDICOMRetrieveJob::createWorker() +ctkAbstractWorker* ctkDICOMRetrieveJob::createWorker() { ctkDICOMRetrieveWorker* worker = new ctkDICOMRetrieveWorker; diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index bbf1f7f65c..41db62dddd 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -31,7 +31,7 @@ #include "ctkDICOMScheduler.h" #include "ctkDICOMServer.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMRetrieveWorker"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMRetrieveWorker"); //------------------------------------------------------------------------------ // ctkDICOMRetrieveWorkerPrivate methods @@ -286,7 +286,7 @@ void ctkDICOMRetrieveWorker::setJob(QSharedPointer job) } //---------------------------------------------------------------------------- -ctkDICOMRetrieve *ctkDICOMRetrieveWorker::retriever() const +ctkDICOMRetrieve* ctkDICOMRetrieveWorker::retriever() const { Q_D(const ctkDICOMRetrieveWorker); return d->Retrieve.data(); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index c5403600b1..13b229728a 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -41,7 +41,7 @@ #include -static ctkLogger logger ( "org.commontk.dicom.DICOMJobPool" ); +static ctkLogger logger("org.commontk.dicom.DICOMJobPool"); //------------------------------------------------------------------------------ // ctkDICOMSchedulerPrivate methods @@ -61,7 +61,7 @@ ctkDICOMSchedulerPrivate::~ctkDICOMSchedulerPrivate() } //------------------------------------------------------------------------------ -ctkDICOMServer *ctkDICOMSchedulerPrivate::getServerFromProxyServersByConnectionName(const QString &connectionName) +ctkDICOMServer* ctkDICOMSchedulerPrivate::getServerFromProxyServersByConnectionName(const QString& connectionName) { foreach (QSharedPointer server, this->Servers) { @@ -105,7 +105,7 @@ void ctkDICOMScheduler::queryPatients(QThread::Priority priority) { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -132,7 +132,7 @@ void ctkDICOMScheduler::queryStudies(const QString& patientID, { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -160,7 +160,7 @@ void ctkDICOMScheduler::querySeries(const QString& patientID, { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -190,7 +190,7 @@ void ctkDICOMScheduler::queryInstances(const QString& patientID, { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -214,13 +214,13 @@ void ctkDICOMScheduler::queryInstances(const QString& patientID, } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::retrieveStudy(const QString &patientID, - const QString &studyInstanceUID, - QThread::Priority priority) +void ctkDICOMScheduler::retrieveStudy(const QString& patientID, + const QString& studyInstanceUID, + QThread::Priority priority) { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -242,14 +242,14 @@ void ctkDICOMScheduler::retrieveStudy(const QString &patientID, } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::retrieveSeries(const QString &patientID, - const QString &studyInstanceUID, - const QString &seriesInstanceUID, - QThread::Priority priority) +void ctkDICOMScheduler::retrieveSeries(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + QThread::Priority priority) { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -272,15 +272,15 @@ void ctkDICOMScheduler::retrieveSeries(const QString &patientID, } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::retrieveSOPInstance(const QString &patientID, - const QString &studyInstanceUID, - const QString &seriesInstanceUID, - const QString &SOPInstanceUID, - QThread::Priority priority) +void ctkDICOMScheduler::retrieveSOPInstance(const QString& patientID, + const QString& studyInstanceUID, + const QString& seriesInstanceUID, + const QString& SOPInstanceUID, + QThread::Priority priority) { Q_D(ctkDICOMScheduler); - foreach(QSharedPointer server, d->Servers) + foreach (QSharedPointer server, d->Servers) { if (!server->queryRetrieveEnabled()) { @@ -305,7 +305,7 @@ void ctkDICOMScheduler::retrieveSOPInstance(const QString &patientID, //---------------------------------------------------------------------------- void ctkDICOMScheduler::startListener(int port, - const QString &AETitle, + const QString& AETitle, QThread::Priority priority) { Q_D(ctkDICOMScheduler); @@ -358,14 +358,14 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -ctkDICOMDatabase* ctkDICOMScheduler::dicomDatabase()const +ctkDICOMDatabase* ctkDICOMScheduler::dicomDatabase() const { Q_D(const ctkDICOMScheduler); return d->DicomDatabase.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMScheduler::dicomDatabaseShared()const +QSharedPointer ctkDICOMScheduler::dicomDatabaseShared() const { Q_D(const ctkDICOMScheduler); return d->DicomDatabase; @@ -386,7 +386,7 @@ void ctkDICOMScheduler::setDicomDatabase(QSharedPointer dicomD } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::setFilters(const QMap &filters) +void ctkDICOMScheduler::setFilters(const QMap& filters) { Q_D(ctkDICOMScheduler); d->Filters = filters; @@ -521,7 +521,7 @@ QString ctkDICOMScheduler::getServerNameFromIndex(int id) int ctkDICOMScheduler::getServerIndexFromName(const QString& connectionName) { Q_D(ctkDICOMScheduler); - for(int serverIndex = 0; serverIndex < d->Servers.size(); ++serverIndex) + for (int serverIndex = 0; serverIndex < d->Servers.size(); ++serverIndex) { QSharedPointer server = d->Servers.at(serverIndex); if (server && server->connectionName() == connectionName) @@ -534,10 +534,10 @@ int ctkDICOMScheduler::getServerIndexFromName(const QString& connectionName) } //---------------------------------------------------------------------------- -void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, - const QStringList &studyInstanceUIDs, - const QStringList &seriesInstanceUIDs, - const QStringList &sopInstanceUIDs) +void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList& patientIDs, + const QStringList& studyInstanceUIDs, + const QStringList& seriesInstanceUIDs, + const QStringList& sopInstanceUIDs) { Q_D(ctkDICOMScheduler); @@ -550,7 +550,7 @@ void ctkDICOMScheduler::waitForFinishByUIDs(const QStringList &patientIDs, } bool wait = true; - while(wait) + while (wait) { QCoreApplication::processEvents(); d->ThreadPool->waitForDone(300); @@ -723,11 +723,11 @@ int ctkDICOMScheduler::maximumPatientsQuery() } //---------------------------------------------------------------------------- -ctkDICOMStorageListenerJob *ctkDICOMScheduler::listenerJob() +ctkDICOMStorageListenerJob* ctkDICOMScheduler::listenerJob() { Q_D(ctkDICOMScheduler); QMutexLocker ml(&d->mMutex); - foreach(QSharedPointer job, d->JobsQueue) + foreach (QSharedPointer job, d->JobsQueue) { QSharedPointer listenerJob = qSharedPointerObjectCast(job); diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 4100547e34..8f8d971a36 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -51,7 +51,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkJobScheduler Q_PROPERTY(int maximumPatientsQuery READ maximumPatientsQuery WRITE setMaximumPatientsQuery); public: - typedef ctkJobScheduler Superclass; + typedef ctkJobScheduler Superclass; explicit ctkDICOMScheduler(QObject* parent = 0); virtual ~ctkDICOMScheduler(); @@ -140,8 +140,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkJobScheduler /// StartDate DCM_StudyDate QString 20090101 /// EndDate DCM_StudyDate QString 20091231 /// No filter (empty) by default. - Q_INVOKABLE void setFilters(const QMap &filters); - Q_INVOKABLE QMap filters()const; + Q_INVOKABLE void setFilters(const QMap& filters); + Q_INVOKABLE QMap filters() const; ///@} ///@{ diff --git a/Libs/DICOM/Core/ctkDICOMServer.cpp b/Libs/DICOM/Core/ctkDICOMServer.cpp index ae7a606b3a..b1edfb9ad0 100644 --- a/Libs/DICOM/Core/ctkDICOMServer.cpp +++ b/Libs/DICOM/Core/ctkDICOMServer.cpp @@ -34,7 +34,7 @@ static ctkLogger logger("org.commontk.dicom.DICOMServer"); //------------------------------------------------------------------------------ -class ctkDICOMServerPrivate: public QObject +class ctkDICOMServerPrivate : public QObject { Q_DECLARE_PUBLIC(ctkDICOMServer); @@ -136,7 +136,7 @@ bool ctkDICOMServer::storageEnabled() const } //------------------------------------------------------------------------------ -void ctkDICOMServer::setCallingAETitle( const QString& callingAETitle ) +void ctkDICOMServer::setCallingAETitle(const QString& callingAETitle) { Q_D(ctkDICOMServer); d->CallingAETitle = callingAETitle; @@ -150,14 +150,14 @@ QString ctkDICOMServer::callingAETitle() const } //------------------------------------------------------------------------------ -void ctkDICOMServer::setCalledAETitle( const QString& calledAETitle ) +void ctkDICOMServer::setCalledAETitle(const QString& calledAETitle) { Q_D(ctkDICOMServer); d->CalledAETitle = calledAETitle; } //------------------------------------------------------------------------------ -QString ctkDICOMServer::calledAETitle()const +QString ctkDICOMServer::calledAETitle() const { Q_D(const ctkDICOMServer); return d->CalledAETitle; @@ -258,7 +258,7 @@ void ctkDICOMServer::setMoveDestinationAETitle(const QString& moveDestinationAET } } //------------------------------------------------------------------------------ -QString ctkDICOMServer::moveDestinationAETitle()const +QString ctkDICOMServer::moveDestinationAETitle() const { Q_D(const ctkDICOMServer); return d->MoveDestinationAETitle; @@ -315,7 +315,7 @@ QSharedPointer ctkDICOMServer::proxyServerShared() const } //---------------------------------------------------------------------------- -void ctkDICOMServer::setProxyServer(ctkDICOMServer &proxyServer) +void ctkDICOMServer::setProxyServer(ctkDICOMServer& proxyServer) { Q_D(ctkDICOMServer); d->ProxyServer = QSharedPointer(&proxyServer, skipDelete); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index 192e031cc0..a4785e61f0 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -32,9 +32,9 @@ #include "ctkDICOMStorageListener.h" // DCMTK includes -#include /* for DcmStorageSCP */ +#include /* for DcmStorageSCP */ -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListener" ); +static ctkLogger logger("org.commontk.dicom.ctkDICOMStorageListener"); //------------------------------------------------------------------------------ // A customized implementation so that Qt signals can be emitted @@ -42,116 +42,128 @@ static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListener" ); class ctkDICOMStorageListenerSCUPrivate : public DcmStorageSCP { public: - ctkDICOMStorageListener *listener; - ctkDICOMStorageListenerSCUPrivate() - { - this->listener = 0; - }; - ~ctkDICOMStorageListenerSCUPrivate() = default; + ctkDICOMStorageListener* listener; + ctkDICOMStorageListenerSCUPrivate() + { + this->listener = 0; + }; + ~ctkDICOMStorageListenerSCUPrivate() = default; - virtual OFCondition acceptAssociations() - { + virtual OFCondition acceptAssociations() + { return DcmSCP::acceptAssociations(); - } + } + + virtual OFBool stopAfterCurrentAssociation(); + virtual OFBool stopAfterConnectionTimeout(); + + virtual OFCondition handleIncomingCommand(T_DIMSE_Message* incomingMsg, + const DcmPresentationContextInfo& presInfo); +}; + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerSCUPrivate methods - virtual OFBool stopAfterCurrentAssociation() +//------------------------------------------------------------------------------ +OFBool ctkDICOMStorageListenerSCUPrivate::stopAfterCurrentAssociation() +{ + if (!this->listener || this->listener->wasCanceled()) { - if (!this->listener || this->listener->wasCanceled()) - { - return OFTrue; - } + return OFTrue; + } + return OFFalse; +} - return OFFalse; +//------------------------------------------------------------------------------ +OFBool ctkDICOMStorageListenerSCUPrivate::stopAfterConnectionTimeout() +{ + if (!this->listener || this->listener->wasCanceled()) + { + return OFTrue; } + return OFFalse; +} - virtual OFBool stopAfterConnectionTimeout() +//------------------------------------------------------------------------------ +OFCondition ctkDICOMStorageListenerSCUPrivate::handleIncomingCommand(T_DIMSE_Message* incomingMsg, + const DcmPresentationContextInfo& presInfo) +{ + OFCondition status = EC_IllegalParameter; + if (incomingMsg != NULL && !this->listener->wasCanceled()) { - if (!this->listener || this->listener->wasCanceled()) + // check whether we've received a supported command + if (incomingMsg->CommandField == DIMSE_C_ECHO_RQ) + { + // handle incoming C-ECHO request + status = handleECHORequest(incomingMsg->msg.CEchoRQ, presInfo.presentationContextID); + } + else if (incomingMsg->CommandField == DIMSE_C_STORE_RQ) + { + // handle incoming C-STORE request + T_DIMSE_C_StoreRQ& storeReq = incomingMsg->msg.CStoreRQ; + Uint16 rspStatusCode = STATUS_STORE_Error_CannotUnderstand; + DcmDataset* reqDataset = new DcmDataset; + // receive dataset in memory + status = receiveSTORERequest(storeReq, presInfo.presentationContextID, reqDataset); + if (status.good()) { - return OFTrue; + rspStatusCode = STATUS_Success; } - return OFFalse; - } - - virtual OFCondition handleIncomingCommand(T_DIMSE_Message *incomingMsg, - const DcmPresentationContextInfo &presInfo) - { - OFCondition status = EC_IllegalParameter; - if (incomingMsg != NULL && !this->listener->wasCanceled()) + OFString instanceUID; + reqDataset->findAndGetOFString(DCM_SOPInstanceUID, instanceUID); + OFString seriesUID; + reqDataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUID); + OFString studyUID; + reqDataset->findAndGetOFString(DCM_StudyInstanceUID, studyUID); + emit this->listener->progress( + ctkDICOMStorageListener::tr("Got STORE request for %1").arg(instanceUID.c_str())); + emit this->listener->progress(0); + if (!this->listener->jobUID().isEmpty() && !this->listener->wasCanceled()) { - // check whether we've received a supported command - if (incomingMsg->CommandField == DIMSE_C_ECHO_RQ) - { - // handle incoming C-ECHO request - status = handleECHORequest(incomingMsg->msg.CEchoRQ, presInfo.presentationContextID); - } - else if (incomingMsg->CommandField == DIMSE_C_STORE_RQ) - { - // handle incoming C-STORE request - T_DIMSE_C_StoreRQ &storeReq = incomingMsg->msg.CStoreRQ; - Uint16 rspStatusCode = STATUS_STORE_Error_CannotUnderstand; - DcmDataset *reqDataset = new DcmDataset; - // receive dataset in memory - status = receiveSTORERequest(storeReq, presInfo.presentationContextID, reqDataset); - if (status.good()) - { - rspStatusCode = STATUS_Success; - } - - OFString instanceUID; - reqDataset->findAndGetOFString(DCM_SOPInstanceUID, instanceUID); - OFString seriesUID; - reqDataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUID); - OFString studyUID; - reqDataset->findAndGetOFString(DCM_StudyInstanceUID, studyUID); - emit this->listener->progress( - ctkDICOMStorageListener::tr("Got STORE request for %1").arg(instanceUID.c_str()) - ); - emit this->listener->progress(0); - if (!this->listener->jobUID().isEmpty() && !this->listener->wasCanceled()) - { - QSharedPointer jobResponseSet = - QSharedPointer(new ctkDICOMJobResponseSet); - jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); - jobResponseSet->setStudyInstanceUID(studyUID.c_str()); - jobResponseSet->setSeriesInstanceUID(seriesUID.c_str()); - jobResponseSet->setSOPInstanceUID(instanceUID.c_str()); - jobResponseSet->setConnectionName(this->listener->AETitle()); - jobResponseSet->setDataset(reqDataset); - jobResponseSet->setJobUID(this->listener->jobUID()); - jobResponseSet->setCopyFile(true); - - this->listener->addJobResponseSet(jobResponseSet); - - emit this->listener->progressJobDetail(jobResponseSet->toVariant()); - } - // send C-STORE response (with DIMSE status code) - if (status.good()) - { - status = sendSTOREResponse(presInfo.presentationContextID, storeReq, rspStatusCode); - } - else if (status == DIMSE_OUTOFRESOURCES) - { - // do not overwrite the previous error status - sendSTOREResponse(presInfo.presentationContextID, storeReq, STATUS_STORE_Refused_OutOfResources); - } - } - else - { - // unsupported command - OFString tempStr; - DCMNET_ERROR("cannot handle this kind of DIMSE command (0x" - << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4) - << OFstatic_cast(unsigned int, incomingMsg->CommandField) - << "), we are a Storage SCP only"); - DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, *incomingMsg, DIMSE_INCOMING)); - status = DIMSE_BADCOMMANDTYPE; - } + QSharedPointer jobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); + jobResponseSet->setStudyInstanceUID(studyUID.c_str()); + jobResponseSet->setSeriesInstanceUID(seriesUID.c_str()); + jobResponseSet->setSOPInstanceUID(instanceUID.c_str()); + jobResponseSet->setConnectionName(this->listener->AETitle()); + jobResponseSet->setDataset(reqDataset); + jobResponseSet->setJobUID(this->listener->jobUID()); + jobResponseSet->setCopyFile(true); + + this->listener->addJobResponseSet(jobResponseSet); + + emit this->listener->progressJobDetail(jobResponseSet->toVariant()); } - return status; + // send C-STORE response (with DIMSE status code) + if (status.good()) + { + status = sendSTOREResponse(presInfo.presentationContextID, storeReq, rspStatusCode); + } + else if (status == DIMSE_OUTOFRESOURCES) + { + // do not overwrite the previous error status + sendSTOREResponse(presInfo.presentationContextID, storeReq, STATUS_STORE_Refused_OutOfResources); + } + } + else + { + // unsupported command + OFString tempStr; + DCMNET_ERROR("cannot handle this kind of DIMSE command (0x" + << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4) + << OFstatic_cast(unsigned int, incomingMsg->CommandField) + << "), we are a Storage SCP only"); + DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, *incomingMsg, DIMSE_INCOMING)); + status = DIMSE_BADCOMMANDTYPE; + } } -}; + return status; +} + +//------------------------------------------------------------------------------ +// ctkDICOMStorageListenerPrivate //------------------------------------------------------------------------------ class ctkDICOMStorageListenerPrivate @@ -160,7 +172,7 @@ class ctkDICOMStorageListenerPrivate ctkDICOMStorageListenerPrivate(); ~ctkDICOMStorageListenerPrivate() = default; - QString findFile(const QStringList& nameFilters, const QString& subDir)const; + QString findFile(const QStringList& nameFilters, const QString& subDir) const; QString defaultConfigFile() const; QString ConnectionName; @@ -198,11 +210,11 @@ QString ctkDICOMStorageListenerPrivate::defaultConfigFile() const QString fileName(":/dicom/storescp.cfg"); QFile readFile(fileName); - if(readFile.open(QIODevice::ReadOnly)) + if (readFile.open(QIODevice::ReadOnly)) { data = readFile.readAll(); } - else + else { logger.error("Failed to find listener configuration file"); return ""; @@ -214,7 +226,7 @@ QString ctkDICOMStorageListenerPrivate::defaultConfigFile() const QString configFile = tmpDir + "/storescp.cfg"; QFile writeFile(configFile); - if(writeFile.open(QFile::WriteOnly | QFile::Text)) + if (writeFile.open(QFile::WriteOnly | QFile::Text)) { QTextStream stream(&writeFile); stream << data; @@ -261,7 +273,8 @@ bool ctkDICOMStorageListener::listen() if (status.bad() || d->Canceled) { logger.error(QString("SCP stopped, it was listening on port %1 : %2 ") - .arg(QString::number(d->Port)).arg(status.text())); + .arg(QString::number(d->Port)) + .arg(status.text())); return false; } return true; @@ -290,7 +303,7 @@ bool ctkDICOMStorageListener::initializeSCU() /* load association negotiation profile from configuration file (if specified) */ OFCondition status = d->SCU.loadAssociationConfiguration( - OFString(d->defaultConfigFile().toStdString().c_str()), "alldicom"); + OFString(d->defaultConfigFile().toStdString().c_str()), "alldicom"); if (status.bad()) { logger.error(QString("Cannot load association configuration: %1").arg(status.text())); @@ -315,14 +328,14 @@ QString ctkDICOMStorageListener::AETitle() const } //------------------------------------------------------------------------------ -void ctkDICOMStorageListener::setPort (int port) +void ctkDICOMStorageListener::setPort(int port) { Q_D(ctkDICOMStorageListener); d->Port = port; } //------------------------------------------------------------------------------ -int ctkDICOMStorageListener::port()const +int ctkDICOMStorageListener::port() const { Q_D(const ctkDICOMStorageListener); return d->Port; @@ -344,14 +357,14 @@ int ctkDICOMStorageListener::connectionTimeout() } //------------------------------------------------------------------------------ -QList ctkDICOMStorageListener::jobResponseSets() const +QList ctkDICOMStorageListener::jobResponseSets() const { Q_D(const ctkDICOMStorageListener); - QList jobResponseSets; - foreach(QSharedPointer jobResponseSet, d->JobResponseSets) - { + QList jobResponseSets; + foreach (QSharedPointer jobResponseSet, d->JobResponseSets) + { jobResponseSets.append(jobResponseSet.data()); - } + } return jobResponseSets; } @@ -372,7 +385,7 @@ static void skipDelete(QObject* obj) } //------------------------------------------------------------------------------ -void ctkDICOMStorageListener::addJobResponseSet(ctkDICOMJobResponseSet &jobResponseSet) +void ctkDICOMStorageListener::addJobResponseSet(ctkDICOMJobResponseSet& jobResponseSet) { this->addJobResponseSet(QSharedPointer(&jobResponseSet, skipDelete)); } @@ -392,7 +405,7 @@ void ctkDICOMStorageListener::removeJobResponseSet(QSharedPointerJobUID = jobUID; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.h b/Libs/DICOM/Core/ctkDICOMStorageListener.h index 59e2fc9d91..d7f3c19af3 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.h +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.h @@ -86,22 +86,22 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMStorageListener : public QObject Q_INVOKABLE bool wasCanceled(); Q_SIGNALS: - /// Signal is emitted inside the listener() function. It ranges from 0 to 100. - /// In case of an error, you are assured that the progress value 100 is fired - void progress(int progress); - /// Signal is emitted inside the listener() function. It sends the different step - /// the function is at. - void progress(const QString& message); - /// Signal is emitted inside the listener() function. It sends - /// detailed feedback for debugging - void debug(const QString& message); - /// Signal is emitted inside the listener() function. It send any error messages - void error(const QString& message); - /// Signal is emitted inside the listener() function when finished with value - /// true for success or false for error - void done(bool error); - /// Signal is emitted inside the listener() function when a frame has been fetched - void progressJobDetail(QVariant); + /// Signal is emitted inside the listener() function. It ranges from 0 to 100. + /// In case of an error, you are assured that the progress value 100 is fired + void progress(int progress); + /// Signal is emitted inside the listener() function. It sends the different step + /// the function is at. + void progress(const QString& message); + /// Signal is emitted inside the listener() function. It sends + /// detailed feedback for debugging + void debug(const QString& message); + /// Signal is emitted inside the listener() function. It send any error messages + void error(const QString& message); + /// Signal is emitted inside the listener() function when finished with value + /// true for success or false for error + void done(bool error); + /// Signal is emitted inside the listener() function when a frame has been fetched + void progressJobDetail(QVariant); public Q_SLOTS: void cancel(); diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp index 6b08978981..c1715ee4ad 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerJob.cpp @@ -28,7 +28,7 @@ #include "ctkDICOMStorageListenerJob_p.h" #include "ctkDICOMStorageListenerWorker.h" -static ctkLogger logger ( "org.commontk.dicom.ctkDICOMStorageListenerJob" ); +static ctkLogger logger("org.commontk.dicom.ctkDICOMStorageListenerJob"); //------------------------------------------------------------------------------ // ctkDICOMStorageListenerJobPrivate methods @@ -79,7 +79,7 @@ int ctkDICOMStorageListenerJob::port() const } //---------------------------------------------------------------------------- -void ctkDICOMStorageListenerJob::setAETitle(const QString &AETitle) +void ctkDICOMStorageListenerJob::setAETitle(const QString& AETitle) { Q_D(ctkDICOMStorageListenerJob); d->AETitle = AETitle; @@ -107,7 +107,7 @@ int ctkDICOMStorageListenerJob::connectionTimeout() const } //---------------------------------------------------------------------------- -QString ctkDICOMStorageListenerJob::loggerReport(const QString &status) const +QString ctkDICOMStorageListenerJob::loggerReport(const QString& status) const { return QString("ctkDICOMStorageListenerJob: listener job %1.\n" "JobUID: %2\n") @@ -132,7 +132,7 @@ ctkAbstractJob* ctkDICOMStorageListenerJob::clone() const } //------------------------------------------------------------------------------ -ctkAbstractWorker *ctkDICOMStorageListenerJob::createWorker() +ctkAbstractWorker* ctkDICOMStorageListenerJob::createWorker() { ctkDICOMStorageListenerWorker* worker = new ctkDICOMStorageListenerWorker; diff --git a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp index e1140ccda7..2641e81419 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListenerWorker.cpp @@ -33,7 +33,7 @@ #include "ctkDICOMStorageListenerJob.h" #include "ctkDICOMStorageListenerWorker_p.h" -static ctkLogger logger ("org.commontk.dicom.ctkDICOMStorageListenerWorker"); +static ctkLogger logger("org.commontk.dicom.ctkDICOMStorageListenerWorker"); //------------------------------------------------------------------------------ // ctkDICOMStorageListenerWorkerPrivate methods @@ -80,7 +80,7 @@ void ctkDICOMStorageListenerWorkerPrivate::init() // i.e. the slot should be connected to progressJobDetail from this->StorageListener. // The slot should have a counter. When the counter > batchLimit -> insert // NOTE: the memory release should happen as soon as we insert the response. - QTimer *timer = new QTimer(this); + QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), q, SLOT(onInsertJobDetail())); timer->start(1000); } @@ -171,7 +171,7 @@ void ctkDICOMStorageListenerWorker::setJob(QSharedPointer job) } //---------------------------------------------------------------------------- -ctkDICOMStorageListener *ctkDICOMStorageListenerWorker::storageListener() const +ctkDICOMStorageListener* ctkDICOMStorageListenerWorker::storageListener() const { Q_D(const ctkDICOMStorageListenerWorker); return d->StorageListener.data(); diff --git a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp index acc2b3574c..f3a00a3a83 100644 --- a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp +++ b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.cpp @@ -32,7 +32,7 @@ ctkDICOMVisualBrowserWidgetPlugin::ctkDICOMVisualBrowserWidgetPlugin(QObject* pl } //----------------------------------------------------------------------------- -QWidget *ctkDICOMVisualBrowserWidgetPlugin::createWidget(QWidget *parentForWidget) +QWidget* ctkDICOMVisualBrowserWidgetPlugin::createWidget(QWidget* parentForWidget) { ctkDICOMVisualBrowserWidget* newWidget = new ctkDICOMVisualBrowserWidget(parentForWidget); return newWidget; @@ -43,7 +43,7 @@ QString ctkDICOMVisualBrowserWidgetPlugin::domXml() const { return "\n" - "\n"; + "\n"; } // -------------------------------------------------------------------------- diff --git a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h index 95385270f5..4059bc44c0 100644 --- a/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h +++ b/Libs/DICOM/Widgets/Plugins/ctkDICOMVisualBrowserWidgetPlugin.h @@ -34,9 +34,9 @@ class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMVisualBrowserWidgetPlugin Q_OBJECT public: - ctkDICOMVisualBrowserWidgetPlugin(QObject *_parent = 0); + ctkDICOMVisualBrowserWidgetPlugin(QObject* _parent = 0); - QWidget *createWidget(QWidget *_parent); + QWidget* createWidget(QWidget* _parent); QString domXml() const; QIcon icon() const; QString includeFile() const; diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp index cbdbf7b269..00f1351953 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp @@ -34,7 +34,7 @@ #include "ctkDICOMPatientItemWidget.h" // Test visual browser import functionality -int ctkDICOMPatientItemWidgetTest1( int argc, char * argv [] ) +int ctkDICOMPatientItemWidgetTest1(int argc, char* argv[]) { QApplication app(argc, argv); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp index b72f164590..4c42bf1e6a 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMSeriesItemWidgetTest1.cpp @@ -34,7 +34,7 @@ #include "ctkDICOMSeriesItemWidget.h" // Test visual browser import functionality -int ctkDICOMSeriesItemWidgetTest1( int argc, char * argv [] ) +int ctkDICOMSeriesItemWidgetTest1(int argc, char* argv[]) { QApplication app(argc, argv); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp index 8af3cd6fdc..02d292a704 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMServerNodeWidget2Test1.cpp @@ -36,7 +36,7 @@ #include "ctkDICOMServer.h" #include "ctkDICOMServerNodeWidget2.h" -int ctkDICOMServerNodeWidget2Test1( int argc, char * argv [] ) +int ctkDICOMServerNodeWidget2Test1(int argc, char* argv[]) { QApplication app(argc, argv); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp index 62d897e011..39f0870c34 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMStudyItemWidgetTest1.cpp @@ -34,7 +34,7 @@ #include "ctkDICOMStudyItemWidget.h" // Test visual browser import functionality -int ctkDICOMStudyItemWidgetTest1( int argc, char * argv [] ) +int ctkDICOMStudyItemWidgetTest1(int argc, char* argv[]) { QApplication app(argc, argv); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index 01fc201d50..66b30f6e68 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -34,7 +34,7 @@ // ctkDICOMWidget includes #include "ctkDICOMVisualBrowserWidget.h" -int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) +int ctkDICOMVisualBrowserWidgetTest1(int argc, char* argv[]) { QApplication app(argc, argv); @@ -71,13 +71,16 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) // Test visual browser import functionality QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMVisualBrowserWidgetTest1-db")); QString dbDir = tempFileInfo.absoluteFilePath(); - qDebug().noquote() << "\n\n" << testName << ": Using directory: " << dbDir; + qDebug().noquote() << "\n\n" + << testName << ": Using directory: " << dbDir; if (tempFileInfo.exists()) { - qDebug().noquote() << "\n\n" << testName << ": Removing directory: " << dbDir; + qDebug().noquote() << "\n\n" + << testName << ": Removing directory: " << dbDir; ctk::removeDirRecursively(dbDir); } - qDebug().noquote() << "\n\n" << testName << ": Making directory: " << dbDir; + qDebug().noquote() << "\n\n" + << testName << ": Making directory: " << dbDir; QDir dir(dbDir); dir.mkdir(dbDir); @@ -100,10 +103,10 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.waitForImportFinished(); qDebug().noquote() << testName << ":" - << " " << browser.patientsAddedDuringImport() - << " " << browser.studiesAddedDuringImport() - << " " << browser.seriesAddedDuringImport() - << " " << browser.instancesAddedDuringImport(); + << " " << browser.patientsAddedDuringImport() + << " " << browser.studiesAddedDuringImport() + << " " << browser.seriesAddedDuringImport() + << " " << browser.instancesAddedDuringImport(); CHECK_INT(browser.patientsAddedDuringImport(), 1); CHECK_INT(browser.studiesAddedDuringImport(), 1); @@ -113,14 +116,16 @@ int ctkDICOMVisualBrowserWidgetTest1( int argc, char * argv [] ) browser.importDirectories(QStringList() << argv[1]); browser.waitForImportFinished(); - qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << files; + qDebug().noquote() << "\n\n" + << testName << ": Added to database directory: " << files; CHECK_INT(browser.patientsAddedDuringImport(), 1); CHECK_INT(browser.studiesAddedDuringImport(), 1); CHECK_INT(browser.seriesAddedDuringImport(), 1); CHECK_INT(browser.instancesAddedDuringImport(), 100); - qDebug().noquote() << "\n\n" << testName << ": Added to database directory: " << dbDir; + qDebug().noquote() << "\n\n" + << testName << ": Added to database directory: " << dbDir; // Test setting and getting browser.setStorageAETitle("storage"); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index 39ca650d58..0ca07b9ef3 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -21,7 +21,7 @@ =========================================================================*/ -//Qt includes +// Qt includes #include #include @@ -49,9 +49,9 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -class ctkDICOMPatientItemWidgetPrivate: public Ui_ctkDICOMPatientItemWidget +class ctkDICOMPatientItemWidgetPrivate : public Ui_ctkDICOMPatientItemWidget { - Q_DECLARE_PUBLIC( ctkDICOMPatientItemWidget ); + Q_DECLARE_PUBLIC(ctkDICOMPatientItemWidget); protected: ctkDICOMPatientItemWidget* const q_ptr; @@ -110,7 +110,7 @@ ctkDICOMPatientItemWidgetPrivate::ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatie //---------------------------------------------------------------------------- ctkDICOMPatientItemWidgetPrivate::~ctkDICOMPatientItemWidgetPrivate() { - QLayout *StudiesListWidgetLayout = this->StudiesListWidget->layout(); + QLayout* StudiesListWidgetLayout = this->StudiesListWidget->layout(); this->clearLayout(StudiesListWidgetLayout); } @@ -159,7 +159,7 @@ QString ctkDICOMPatientItemWidgetPrivate::formatDate(const QString& date) } //---------------------------------------------------------------------------- -bool ctkDICOMPatientItemWidgetPrivate::isStudyItemAlreadyAdded(const QString &studyItem) +bool ctkDICOMPatientItemWidgetPrivate::isStudyItemAlreadyAdded(const QString& studyItem) { bool alreadyAdded = false; foreach (ctkDICOMStudyItemWidget* studyItemWidget, this->StudyItemWidgetsList) @@ -180,7 +180,7 @@ bool ctkDICOMPatientItemWidgetPrivate::isStudyItemAlreadyAdded(const QString &st } //---------------------------------------------------------------------------- -void ctkDICOMPatientItemWidgetPrivate::clearLayout(QLayout *layout, bool deleteWidgets) +void ctkDICOMPatientItemWidgetPrivate::clearLayout(QLayout* layout, bool deleteWidgets) { Q_Q(ctkDICOMPatientItemWidget); @@ -232,7 +232,7 @@ void ctkDICOMPatientItemWidgetPrivate::createStudies() return; } - QLayout *studiesListWidgetLayout = this->StudiesListWidget->layout(); + QLayout* studiesListWidgetLayout = this->StudiesListWidget->layout(); if (this->PatientItem.isEmpty()) { this->PatientNameValueLabel->setText(""); @@ -341,7 +341,7 @@ ctkDICOMPatientItemWidget::~ctkDICOMPatientItemWidget() } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setPatientItem(const QString &patientItem) +void ctkDICOMPatientItemWidget::setPatientItem(const QString& patientItem) { Q_D(ctkDICOMPatientItemWidget); d->PatientItem = patientItem; @@ -355,7 +355,7 @@ QString ctkDICOMPatientItemWidget::patientItem() const } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setPatientID(const QString &patientID) +void ctkDICOMPatientItemWidget::setPatientID(const QString& patientID) { Q_D(ctkDICOMPatientItemWidget); d->PatientID = patientID; @@ -383,7 +383,7 @@ QString ctkDICOMPatientItemWidget::filteringStudyDescription() const } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType &filteringDate) +void ctkDICOMPatientItemWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType& filteringDate) { Q_D(ctkDICOMPatientItemWidget); d->FilteringDate = filteringDate; @@ -411,7 +411,7 @@ QString ctkDICOMPatientItemWidget::filteringSeriesDescription() const } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setFilteringModalities(const QStringList &filteringModalities) +void ctkDICOMPatientItemWidget::setFilteringModalities(const QStringList& filteringModalities) { Q_D(ctkDICOMPatientItemWidget); d->FilteringModalities = filteringModalities; @@ -439,7 +439,7 @@ int ctkDICOMPatientItemWidget::numberOfStudiesPerPatient() const } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) +void ctkDICOMPatientItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize) { Q_D(ctkDICOMPatientItemWidget); d->ThumbnailSize = thumbnailSize; @@ -453,14 +453,14 @@ ctkDICOMStudyItemWidget::ThumbnailSizeOption ctkDICOMPatientItemWidget::thumbnai } //---------------------------------------------------------------------------- -ctkDICOMScheduler* ctkDICOMPatientItemWidget::scheduler()const +ctkDICOMScheduler* ctkDICOMPatientItemWidget::scheduler() const { Q_D(const ctkDICOMPatientItemWidget); return d->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMPatientItemWidget::schedulerShared()const +QSharedPointer ctkDICOMPatientItemWidget::schedulerShared() const { Q_D(const ctkDICOMPatientItemWidget); return d->Scheduler; @@ -505,14 +505,14 @@ void ctkDICOMPatientItemWidget::setScheduler(QSharedPointer s } //---------------------------------------------------------------------------- -ctkDICOMDatabase* ctkDICOMPatientItemWidget::dicomDatabase()const +ctkDICOMDatabase* ctkDICOMPatientItemWidget::dicomDatabase() const { Q_D(const ctkDICOMPatientItemWidget); return d->DicomDatabase.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMPatientItemWidget::dicomDatabaseShared()const +QSharedPointer ctkDICOMPatientItemWidget::dicomDatabaseShared() const { Q_D(const ctkDICOMPatientItemWidget); return d->DicomDatabase; @@ -533,7 +533,7 @@ void ctkDICOMPatientItemWidget::setDicomDatabase(QSharedPointer ctkDICOMPatientItemWidget::studyItemWidgetsList() const +QList ctkDICOMPatientItemWidget::studyItemWidgetsList() const { Q_D(const ctkDICOMPatientItemWidget); return d->StudyItemWidgetsList; @@ -569,7 +569,7 @@ int ctkDICOMPatientItemWidget::getNDaysFromFilteringDate(DateType FilteringDate) } //---------------------------------------------------------------------------- -void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString &studyItem) +void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString& studyItem) { Q_D(ctkDICOMPatientItemWidget); @@ -640,13 +640,13 @@ void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString &studyItem) } //---------------------------------------------------------------------------- -void ctkDICOMPatientItemWidget::removeStudyItemWidget(const QString &studyItem) +void ctkDICOMPatientItemWidget::removeStudyItemWidget(const QString& studyItem) { Q_D(ctkDICOMPatientItemWidget); for (int studyIndex = 0; studyIndex < d->StudyItemWidgetsList.size(); ++studyIndex) { - ctkDICOMStudyItemWidget *studyItemWidget = + ctkDICOMStudyItemWidget* studyItemWidget = qobject_cast(d->StudyItemWidgetsList[studyIndex]); if (!studyItemWidget || studyItemWidget->studyItem() != studyItem) { @@ -719,17 +719,17 @@ void ctkDICOMPatientItemWidget::raiseSelectedSeriesJobsPriority() return; } - QList seriesWidgets; - QList selectedSeriesWidgets; - foreach (ctkDICOMStudyItemWidget *studyItemWidget, d->StudyItemWidgetsList) + QList seriesWidgets; + QList selectedSeriesWidgets; + foreach (ctkDICOMStudyItemWidget* studyItemWidget, d->StudyItemWidgetsList) { if (!studyItemWidget) { continue; } - QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); - for (int row = 0; row < seriesListTableWidget->rowCount(); row++) + QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + for (int row = 0; row < seriesListTableWidget->rowCount(); row++) { for (int column = 0; column < seriesListTableWidget->columnCount(); column++) { @@ -740,7 +740,7 @@ void ctkDICOMPatientItemWidget::raiseSelectedSeriesJobsPriority() } QList selectedItems = seriesListTableWidget->selectedItems(); - foreach (QTableWidgetItem *selectedItem, selectedItems) + foreach (QTableWidgetItem* selectedItem, selectedItems) { if (!selectedItem) { @@ -797,14 +797,14 @@ void ctkDICOMPatientItemWidget::onSeriesItemClicked() return; } - foreach (ctkDICOMStudyItemWidget *studyItemWidget, d->StudyItemWidgetsList) + foreach (ctkDICOMStudyItemWidget* studyItemWidget, d->StudyItemWidgetsList) { if (!studyItemWidget) { continue; } - QTableWidget *studySeriesTable = studyItemWidget->seriesListTableWidget(); + QTableWidget* studySeriesTable = studyItemWidget->seriesListTableWidget(); if (studySeriesTable == seriesTable) { continue; diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index eeb0b9793a..ee587401d0 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -119,7 +119,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget ///@{ /// Set the thumbnail size: small, medium, large /// medium by default - void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); + void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize); ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; ///@} @@ -146,15 +146,15 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget void setDicomDatabase(QSharedPointer dicomDatabase); /// Return all the study item widgets for the patient - Q_INVOKABLE QList studyItemWidgetsList()const; + Q_INVOKABLE QList studyItemWidgetsList() const; /// Return number of days from filtering date attribute Q_INVOKABLE static int getNDaysFromFilteringDate(ctkDICOMPatientItemWidget::DateType filteringDate); ///@{ /// Add/Remove study item widgets - Q_INVOKABLE void addStudyItemWidget(const QString &studyItem); - Q_INVOKABLE void removeStudyItemWidget(const QString &studyItem); + Q_INVOKABLE void addStudyItemWidget(const QString& studyItem); + Q_INVOKABLE void removeStudyItemWidget(const QString& studyItem); ///@} /// Set selection for all studies/series diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index f3540872c9..81e7bc5c4f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -21,7 +21,7 @@ =========================================================================*/ -//Qt includes +// Qt includes #include #include #include @@ -48,9 +48,9 @@ static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMSeriesItemWidget"); //---------------------------------------------------------------------------- -class ctkDICOMSeriesItemWidgetPrivate: public Ui_ctkDICOMSeriesItemWidget +class ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget { - Q_DECLARE_PUBLIC( ctkDICOMSeriesItemWidget ); + Q_DECLARE_PUBLIC(ctkDICOMSeriesItemWidget); protected: ctkDICOMSeriesItemWidget* const q_ptr; @@ -232,7 +232,7 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkDICOMJobDetail td) if (!this->IsCloud) { - if(this->DicomDatabase->visibleSeries().contains(this->SeriesInstanceUID)) + if (this->DicomDatabase->visibleSeries().contains(this->SeriesInstanceUID)) { this->IsVisible = true; } @@ -258,7 +258,7 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkDICOMJobDetail td) // In these cases, we check if a frame has been already fetched and we use the first found one. if (file.isEmpty() && numberOfFiles < numberOfFrames) { - foreach(QString newFile, filesList) + foreach (QString newFile, filesList) { if (file.isEmpty()) { @@ -362,7 +362,7 @@ void ctkDICOMSeriesItemWidgetPrivate::drawModalityThumbnail() } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int numberOfFrames) +void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString& file, int numberOfFrames) { if (!this->DicomDatabase) { @@ -431,7 +431,7 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString &file, int num { renderer.load(QString(":Icons/downloading.svg")); } - else + else { renderer.load(QString(":Icons/cloud.svg")); } @@ -568,7 +568,7 @@ ctkDICOMSeriesItemWidget::~ctkDICOMSeriesItemWidget() } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidget::setSeriesItem(const QString &seriesItem) +void ctkDICOMSeriesItemWidget::setSeriesItem(const QString& seriesItem) { Q_D(ctkDICOMSeriesItemWidget); d->SeriesItem = seriesItem; @@ -582,7 +582,7 @@ QString ctkDICOMSeriesItemWidget::seriesItem() const } //------------------------------------------------------------------------------ -void ctkDICOMSeriesItemWidget::setPatientID(const QString &patientID) +void ctkDICOMSeriesItemWidget::setPatientID(const QString& patientID) { Q_D(ctkDICOMSeriesItemWidget); d->PatientID = patientID; @@ -752,14 +752,14 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -ctkDICOMScheduler* ctkDICOMSeriesItemWidget::scheduler()const +ctkDICOMScheduler* ctkDICOMSeriesItemWidget::scheduler() const { Q_D(const ctkDICOMSeriesItemWidget); return d->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMSeriesItemWidget::schedulerShared()const +QSharedPointer ctkDICOMSeriesItemWidget::schedulerShared() const { Q_D(const ctkDICOMSeriesItemWidget); return d->Scheduler; @@ -812,14 +812,14 @@ void ctkDICOMSeriesItemWidget::setScheduler(QSharedPointer sc } //---------------------------------------------------------------------------- -ctkDICOMDatabase* ctkDICOMSeriesItemWidget::dicomDatabase()const +ctkDICOMDatabase* ctkDICOMSeriesItemWidget::dicomDatabase() const { Q_D(const ctkDICOMSeriesItemWidget); return d->DicomDatabase.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMSeriesItemWidget::dicomDatabaseShared()const +QSharedPointer ctkDICOMSeriesItemWidget::dicomDatabaseShared() const { Q_D(const ctkDICOMSeriesItemWidget); return d->DicomDatabase; diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index 1c0bd9c889..696c2e6d8b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -56,14 +56,14 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; - void paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override + void paint(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const override { QStyleOptionViewItem opt = option; - const QWidget *widget = option.widget; + const QWidget* widget = option.widget; initStyleOption(&opt, index); - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + QStyle* style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); if (opt.features & QStyleOptionViewItem::HasCheckIndicator) { @@ -107,10 +107,10 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate } } protected: - bool editorEvent(QEvent *event, - QAbstractItemModel *model, - const QStyleOptionViewItem &option, - const QModelIndex &index) override + bool editorEvent(QEvent* event, + QAbstractItemModel* model, + const QStyleOptionViewItem& option, + const QModelIndex& index) override { Q_ASSERT(event); Q_ASSERT(model); @@ -127,8 +127,8 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate { return false; } - const QWidget *widget = option.widget; - QStyle *style = option.widget ? widget->style() : QApplication::style(); + const QWidget* widget = option.widget; + QStyle* style = option.widget ? widget->style() : QApplication::style(); // make sure that we have the right event type if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonDblClick) || (event->type() == QEvent::MouseButtonPress)) @@ -137,7 +137,7 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate initStyleOption(&viewOpt, index); QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget); checkRect = QStyle::alignedRect(viewOpt.direction, Qt::AlignCenter, checkRect.size(), viewOpt.rect); - QMouseEvent *me = static_cast(event); + QMouseEvent* me = static_cast(event); if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) { return false; @@ -149,8 +149,8 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate } else if (event->type() == QEvent::KeyPress) { - if (static_cast(event)->key() != Qt::Key_Space && - static_cast(event)->key() != Qt::Key_Select) + if (static_cast(event)->key() != Qt::Key_Space && + static_cast(event)->key() != Qt::Key_Select) { return false; } @@ -173,7 +173,7 @@ class QCenteredStyledItemDelegate : public QStyledItemDelegate }; //---------------------------------------------------------------------------- -class ctkDICOMServerNodeWidget2Private: public Ui_ctkDICOMServerNodeWidget2 +class ctkDICOMServerNodeWidget2Private : public Ui_ctkDICOMServerNodeWidget2 { Q_DECLARE_PUBLIC(ctkDICOMServerNodeWidget2); @@ -189,15 +189,15 @@ class ctkDICOMServerNodeWidget2Private: public Ui_ctkDICOMServerNodeWidget2 void connectScheduler(); /// Utility function that returns the storageAETitle and /// storagePort in a map - QMap parameters()const; + QMap parameters() const; /// Return the list of server names - QStringList serverNodes()const; + QStringList serverNodes() const; /// Return all the information associated to a server defined by its name - QMap serverNodeParameters(const QString &connectionName) const; - QMap serverNodeParameters(int row) const; + QMap serverNodeParameters(const QString& connectionName) const; + QMap serverNodeParameters(int row) const; QStringList getAllNodesName() const; - int getServerNodeRowFromConnectionName(const QString &connectionName) const; + int getServerNodeRowFromConnectionName(const QString& connectionName) const; QString getServerNodeConnectionNameFromRow(int row) const; /// Add a server node with the given parameters @@ -205,12 +205,12 @@ class ctkDICOMServerNodeWidget2Private: public Ui_ctkDICOMServerNodeWidget2 int addServerNode(const QMap& parameters); int addServerNode(ctkDICOMServer* server); QSharedPointer createServerFromServerNode(const QMap& node); - void updateProxyComboBoxes(const QString &connectionName, int rowCount) const; + void updateProxyComboBoxes(const QString& connectionName, int rowCount) const; bool SettingsModified; QSharedPointer Scheduler; - QPushButton *SaveButton; - QPushButton *RestoreButton; + QPushButton* SaveButton; + QPushButton* RestoreButton; }; //---------------------------------------------------------------------------- @@ -255,7 +255,7 @@ void ctkDICOMServerNodeWidget2Private::init() this->NodeTable->setItemDelegateForColumn(ctkDICOMServerNodeWidget2::StorageColumn, new QCenteredStyledItemDelegate()); - QIntValidator *validator = new QIntValidator(0, INT_MAX); + QIntValidator* validator = new QIntValidator(0, INT_MAX); this->StoragePort->setValidator(validator); q->readSettings(); @@ -325,7 +325,7 @@ void ctkDICOMServerNodeWidget2Private::connectScheduler() } //---------------------------------------------------------------------------- -QMap ctkDICOMServerNodeWidget2Private::parameters()const +QMap ctkDICOMServerNodeWidget2Private::parameters() const { Q_Q(const ctkDICOMServerNodeWidget2); QMap parameters; @@ -337,7 +337,7 @@ QMap ctkDICOMServerNodeWidget2Private::parameters()const } //---------------------------------------------------------------------------- -QStringList ctkDICOMServerNodeWidget2Private::serverNodes()const +QStringList ctkDICOMServerNodeWidget2Private::serverNodes() const { QStringList nodes; int count = this->NodeTable->rowCount(); @@ -353,7 +353,7 @@ QStringList ctkDICOMServerNodeWidget2Private::serverNodes()const } //---------------------------------------------------------------------------- -QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(const QString &connectionName)const +QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(const QString& connectionName) const { QMap parameters; int count = this->NodeTable->rowCount(); @@ -393,22 +393,22 @@ QMap ctkDICOMServerNodeWidget2Private::serverNodeParameters(i this->NodeTable->item(row, ctkDICOMServerNodeWidget2::StorageColumn)->checkState() : static_cast(Qt::Unchecked); - QLineEdit *portLineEdit = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::PortColumn)); + QLineEdit* portLineEdit = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::PortColumn)); if (portLineEdit) { node["Port"] = portLineEdit->text(); } - QSpinBox *timeoutSpinBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::TimeoutColumn)); + QSpinBox* timeoutSpinBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::TimeoutColumn)); if (timeoutSpinBox) { node["Timeout"] = timeoutSpinBox->value(); } - QComboBox *protocolComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProtocolColumn)); + QComboBox* protocolComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProtocolColumn)); if (protocolComboBox) { node["Protocol"] = protocolComboBox->currentText(); } - QComboBox *proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); + QComboBox* proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); if (proxyComboBox) { node["Proxy"] = proxyComboBox->currentText(); @@ -431,7 +431,7 @@ QStringList ctkDICOMServerNodeWidget2Private::getAllNodesName() const } //---------------------------------------------------------------------------- -int ctkDICOMServerNodeWidget2Private::getServerNodeRowFromConnectionName(const QString &connectionName) const +int ctkDICOMServerNodeWidget2Private::getServerNodeRowFromConnectionName(const QString& connectionName) const { QMap parameters; int count = this->NodeTable->rowCount(); @@ -471,7 +471,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->rowCount(); this->NodeTable->setRowCount(rowCount + 1); - QTableWidgetItem *newItem; + QTableWidgetItem* newItem; QString serverName = node["Name"].toString(); newItem = new QTableWidgetItem(serverName); this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::NameColumn, newItem); @@ -494,8 +494,8 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QLineEdit *portLineEdit = new QLineEdit(); - QIntValidator *validator = new QIntValidator(0, INT_MAX); + QLineEdit* portLineEdit = new QLineEdit(); + QIntValidator* validator = new QIntValidator(0, INT_MAX); portLineEdit->setValidator(validator); portLineEdit->setObjectName("portLineEdit"); @@ -507,7 +507,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QComboBox *protocolComboBox = new QComboBox(); + QComboBox* protocolComboBox = new QComboBox(); protocolComboBox->setObjectName("protocolComboBox"); protocolComboBox->addItem("CGET"); protocolComboBox->addItem("CMOVE"); @@ -519,7 +519,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); newItem = new QTableWidgetItem(QString("")); - QSpinBox *timeoutSpinBox = new QSpinBox(); + QSpinBox* timeoutSpinBox = new QSpinBox(); timeoutSpinBox->setObjectName("timeoutSpinBox"); timeoutSpinBox->setValue(node["Timeout"].toInt()); timeoutSpinBox->setMinimum(1); @@ -533,7 +533,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QComboBox *proxyComboBox = new QComboBox(); + QComboBox* proxyComboBox = new QComboBox(); proxyComboBox->setObjectName("proxyComboBox"); QStringListModel* cbModel = new QStringListModel(); proxyComboBox->setModel(cbModel); @@ -561,7 +561,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(const QMapNodeTable->rowCount(); this->NodeTable->setRowCount(rowCount + 1); - QTableWidgetItem *newItem; + QTableWidgetItem* newItem; newItem = new QTableWidgetItem(server->connectionName()); this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::NameColumn, newItem); @@ -601,8 +601,8 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QLineEdit *portLineEdit = new QLineEdit(); - QIntValidator *validator = new QIntValidator(0, INT_MAX); + QLineEdit* portLineEdit = new QLineEdit(); + QIntValidator* validator = new QIntValidator(0, INT_MAX); portLineEdit->setValidator(validator); portLineEdit->setObjectName("portLineEdit"); @@ -614,7 +614,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) this->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QComboBox *protocolComboBox = new QComboBox(); + QComboBox* protocolComboBox = new QComboBox(); protocolComboBox->addItem("CGET"); protocolComboBox->addItem("CMOVE"); protocolComboBox->setCurrentIndex(protocolComboBox->findText(server->retrieveProtocolAsString())); @@ -625,7 +625,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) this->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); newItem = new QTableWidgetItem(QString("")); - QSpinBox *timeoutSpinBox = new QSpinBox(); + QSpinBox* timeoutSpinBox = new QSpinBox(); timeoutSpinBox->setObjectName("timeoutSpinBox"); timeoutSpinBox->setValue(server->connectionTimeout()); timeoutSpinBox->setMinimum(1); @@ -640,7 +640,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) newItem = new QTableWidgetItem(QString("")); - QComboBox *proxyComboBox = new QComboBox(); + QComboBox* proxyComboBox = new QComboBox(); QStringListModel* cbModel = new QStringListModel(); proxyComboBox->setModel(cbModel); @@ -682,7 +682,7 @@ int ctkDICOMServerNodeWidget2Private::addServerNode(ctkDICOMServer *server) } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMServerNodeWidget2Private::createServerFromServerNode(const QMap &node) +QSharedPointer ctkDICOMServerNodeWidget2Private::createServerFromServerNode(const QMap& node) { QSharedPointer server = QSharedPointer(new ctkDICOMServer); @@ -701,11 +701,11 @@ QSharedPointer ctkDICOMServerNodeWidget2Private::createServerFro } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2Private::updateProxyComboBoxes(const QString &connectionName, int rowCount) const +void ctkDICOMServerNodeWidget2Private::updateProxyComboBoxes(const QString& connectionName, int rowCount) const { for (int row = 0; row < rowCount; ++row) { - QComboBox *proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); + QComboBox* proxyComboBox = qobject_cast(this->NodeTable->cellWidget(row, ctkDICOMServerNodeWidget2::ProxyColumn)); if (proxyComboBox) { QStringListModel* cbModel = qobject_cast(proxyComboBox->model()); @@ -746,7 +746,7 @@ int ctkDICOMServerNodeWidget2::onAddServerNode() d->NodeTable->setRowCount(rowCount + 1); QString serverName = "server"; - QTableWidgetItem *newItem = new QTableWidgetItem(serverName); + QTableWidgetItem* newItem = new QTableWidgetItem(serverName); d->NodeTable->setItem(rowCount, NameColumn, newItem); newItem = new QTableWidgetItem(QString("")); @@ -767,8 +767,8 @@ int ctkDICOMServerNodeWidget2::onAddServerNode() d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::AddressColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QLineEdit *portLineEdit = new QLineEdit(); - QIntValidator *validator = new QIntValidator(0, INT_MAX); + QLineEdit* portLineEdit = new QLineEdit(); + QIntValidator* validator = new QIntValidator(0, INT_MAX); portLineEdit->setValidator(validator); portLineEdit->setObjectName("portLineEdit"); @@ -780,7 +780,7 @@ int ctkDICOMServerNodeWidget2::onAddServerNode() d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::PortColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QComboBox *protocolComboBox = new QComboBox(); + QComboBox* protocolComboBox = new QComboBox(); protocolComboBox->setObjectName("protocolComboBox"); protocolComboBox->addItem("CGET"); protocolComboBox->addItem("CMOVE"); @@ -792,7 +792,7 @@ int ctkDICOMServerNodeWidget2::onAddServerNode() d->NodeTable->setCellWidget(rowCount, ctkDICOMServerNodeWidget2::ProtocolColumn, protocolComboBox); newItem = new QTableWidgetItem(QString("")); - QSpinBox *timeoutSpinBox = new QSpinBox(); + QSpinBox* timeoutSpinBox = new QSpinBox(); timeoutSpinBox->setObjectName("timeoutSpinBox"); timeoutSpinBox->setValue(10); timeoutSpinBox->setMinimum(1); @@ -806,7 +806,7 @@ int ctkDICOMServerNodeWidget2::onAddServerNode() d->NodeTable->setItem(rowCount, ctkDICOMServerNodeWidget2::TimeoutColumn, newItem); newItem = new QTableWidgetItem(QString("")); - QComboBox *proxyComboBox = new QComboBox(); + QComboBox* proxyComboBox = new QComboBox(); proxyComboBox->setObjectName("proxyComboBox"); QStringListModel* cbModel = new QStringListModel(); proxyComboBox->setModel(cbModel); @@ -1106,7 +1106,7 @@ void ctkDICOMServerNodeWidget2::setStorageAETitle(const QString& storageAETitle) } //---------------------------------------------------------------------------- -QString ctkDICOMServerNodeWidget2::storageAETitle()const +QString ctkDICOMServerNodeWidget2::storageAETitle() const { Q_D(const ctkDICOMServerNodeWidget2); return d->StorageAETitle->text(); @@ -1121,7 +1121,7 @@ void ctkDICOMServerNodeWidget2::setStoragePort(int storagePort) } //---------------------------------------------------------------------------- -int ctkDICOMServerNodeWidget2::storagePort()const +int ctkDICOMServerNodeWidget2::storagePort() const { Q_D(const ctkDICOMServerNodeWidget2); bool ok = false; @@ -1139,14 +1139,14 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -ctkDICOMScheduler* ctkDICOMServerNodeWidget2::scheduler()const +ctkDICOMScheduler* ctkDICOMServerNodeWidget2::scheduler() const { Q_D(const ctkDICOMServerNodeWidget2); return d->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMServerNodeWidget2::schedulerShared()const +QSharedPointer ctkDICOMServerNodeWidget2::schedulerShared() const { Q_D(const ctkDICOMServerNodeWidget2); return d->Scheduler; diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index 69e867eb90..9f18490533 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -51,7 +51,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMServerNodeWidget2 : public QWidget public: typedef QWidget Superclass; - explicit ctkDICOMServerNodeWidget2(QWidget* parent=0); + explicit ctkDICOMServerNodeWidget2(QWidget* parent = 0); virtual ~ctkDICOMServerNodeWidget2(); ///@{ @@ -116,7 +116,8 @@ public Q_SLOTS: protected: QScopedPointer d_ptr; - enum ServerColumns{ + enum ServerColumns + { NameColumn = 0, QueryRetrieveColumn, StorageColumn, diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 6f3f5281a1..0ca136ba95 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -21,7 +21,7 @@ =========================================================================*/ -//Qt includes +// Qt includes #include #include #include @@ -52,7 +52,7 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget +class ctkDICOMStudyItemWidgetPrivate : public Ui_ctkDICOMStudyItemWidget { Q_DECLARE_PUBLIC(ctkDICOMStudyItemWidget); @@ -69,7 +69,7 @@ class ctkDICOMStudyItemWidgetPrivate: public Ui_ctkDICOMStudyItemWidget int getScreenWidth(); int getScreenHeight(); int calculateNumerOfSeriesPerRow(); - int calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); + int calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize); void addEmptySeriesItemWidget(int rowIndex, int columnIndex); bool isSeriesItemAlreadyAdded(const QString& seriesItem); @@ -113,7 +113,7 @@ ctkDICOMStudyItemWidgetPrivate::~ctkDICOMStudyItemWidgetPrivate() for (int row = 0; row < this->SeriesListTableWidget->rowCount(); row++) { - for (int column = 0 ; column < this->SeriesListTableWidget->columnCount(); column++) + for (int column = 0; column < this->SeriesListTableWidget->columnCount(); column++) { ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast(this->SeriesListTableWidget->cellWidget(row, column)); @@ -245,7 +245,7 @@ void ctkDICOMStudyItemWidgetPrivate::createSeries() //------------------------------------------------------------------------------ int ctkDICOMStudyItemWidgetPrivate::getScreenWidth() { - QList screens = QApplication::screens(); + QList screens = QApplication::screens(); int width = 1920; foreach (QScreen* screen, screens) { @@ -262,7 +262,7 @@ int ctkDICOMStudyItemWidgetPrivate::getScreenWidth() //------------------------------------------------------------------------------ int ctkDICOMStudyItemWidgetPrivate::getScreenHeight() { - QList screens = QApplication::screens(); + QList screens = QApplication::screens(); int height = 1080; foreach (QScreen* screen, screens) { @@ -287,7 +287,7 @@ int ctkDICOMStudyItemWidgetPrivate::calculateNumerOfSeriesPerRow() } //------------------------------------------------------------------------------ -int ctkDICOMStudyItemWidgetPrivate::calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) +int ctkDICOMStudyItemWidgetPrivate::calculateThumbnailSizeInPixel(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize) { int height = this->getScreenHeight(); int thumbnailSizeInPixel = 1; @@ -316,7 +316,7 @@ int ctkDICOMStudyItemWidgetPrivate::calculateThumbnailSizeInPixel(const ctkDICOM //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(int rowIndex, int columnIndex) { - QTableWidgetItem *tableItem = new QTableWidgetItem; + QTableWidgetItem* tableItem = new QTableWidgetItem; tableItem->setFlags(Qt::NoItemFlags); tableItem->setSizeHint(QSize(this->ThumbnailSizePixel, this->ThumbnailSizePixel)); @@ -324,12 +324,12 @@ void ctkDICOMStudyItemWidgetPrivate::addEmptySeriesItemWidget(int rowIndex, int } //------------------------------------------------------------------------------ -bool ctkDICOMStudyItemWidgetPrivate::isSeriesItemAlreadyAdded(const QString &seriesItem) +bool ctkDICOMStudyItemWidgetPrivate::isSeriesItemAlreadyAdded(const QString& seriesItem) { bool alreadyAdded = false; for (int i = 0; i < this->SeriesListTableWidget->rowCount(); i++) { - for (int j = 0 ; j < this->SeriesListTableWidget->columnCount(); j++) + for (int j = 0; j < this->SeriesListTableWidget->columnCount(); j++) { ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast(this->SeriesListTableWidget->cellWidget(i, j)); @@ -372,7 +372,7 @@ ctkDICOMStudyItemWidget::~ctkDICOMStudyItemWidget() } //---------------------------------------------------------------------------- -void ctkDICOMStudyItemWidget::setStudyItem(const QString &studyItem) +void ctkDICOMStudyItemWidget::setStudyItem(const QString& studyItem) { Q_D(ctkDICOMStudyItemWidget); d->StudyItem = studyItem; @@ -386,7 +386,7 @@ QString ctkDICOMStudyItemWidget::studyItem() const } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidget::setPatientID(const QString &patientID) +void ctkDICOMStudyItemWidget::setPatientID(const QString& patientID) { Q_D(ctkDICOMStudyItemWidget); d->PatientID = patientID; @@ -457,7 +457,7 @@ void ctkDICOMStudyItemWidget::setCollapsed(bool collapsed) } //------------------------------------------------------------------------------ -bool ctkDICOMStudyItemWidget::collapsed()const +bool ctkDICOMStudyItemWidget::collapsed() const { Q_D(const ctkDICOMStudyItemWidget); return d->StudyItemCollapsibleGroupBox->collapsed(); @@ -471,7 +471,7 @@ int ctkDICOMStudyItemWidget::numberOfSeriesPerRow() const } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) +void ctkDICOMStudyItemWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize) { Q_D(ctkDICOMStudyItemWidget); d->ThumbnailSize = thumbnailSize; @@ -518,14 +518,14 @@ bool ctkDICOMStudyItemWidget::selection() const } //---------------------------------------------------------------------------- -ctkDICOMScheduler* ctkDICOMStudyItemWidget::scheduler()const +ctkDICOMScheduler* ctkDICOMStudyItemWidget::scheduler() const { Q_D(const ctkDICOMStudyItemWidget); return d->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMStudyItemWidget::schedulerShared()const +QSharedPointer ctkDICOMStudyItemWidget::schedulerShared() const { Q_D(const ctkDICOMStudyItemWidget); return d->Scheduler; @@ -570,14 +570,14 @@ void ctkDICOMStudyItemWidget::setScheduler(QSharedPointer sch } //---------------------------------------------------------------------------- -ctkDICOMDatabase* ctkDICOMStudyItemWidget::dicomDatabase()const +ctkDICOMDatabase* ctkDICOMStudyItemWidget::dicomDatabase() const { Q_D(const ctkDICOMStudyItemWidget); return d->DicomDatabase.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMStudyItemWidget::dicomDatabaseShared()const +QSharedPointer ctkDICOMStudyItemWidget::dicomDatabaseShared() const { Q_D(const ctkDICOMStudyItemWidget); return d->DicomDatabase; @@ -612,7 +612,7 @@ QString ctkDICOMStudyItemWidget::filteringSeriesDescription() const } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidget::setFilteringModalities(const QStringList &filteringModalities) +void ctkDICOMStudyItemWidget::setFilteringModalities(const QStringList& filteringModalities) { Q_D(ctkDICOMStudyItemWidget); d->FilteringModalities = filteringModalities; @@ -626,21 +626,21 @@ QStringList ctkDICOMStudyItemWidget::filteringModalities() const } //------------------------------------------------------------------------------ -QTableWidget *ctkDICOMStudyItemWidget::seriesListTableWidget() +QTableWidget* ctkDICOMStudyItemWidget::seriesListTableWidget() { Q_D(ctkDICOMStudyItemWidget); return d->SeriesListTableWidget; } //------------------------------------------------------------------------------ -QList ctkDICOMStudyItemWidget::seriesItemWidgetsList() const +QList ctkDICOMStudyItemWidget::seriesItemWidgetsList() const { Q_D(const ctkDICOMStudyItemWidget); QList seriesItemWidgetsList; for (int row = 0; row < d->SeriesListTableWidget->rowCount(); row++) { - for (int column = 0 ; column < d->SeriesListTableWidget->columnCount(); column++) + for (int column = 0; column < d->SeriesListTableWidget->columnCount(); column++) { ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast(d->SeriesListTableWidget->cellWidget(row, column)); @@ -658,10 +658,10 @@ QList ctkDICOMStudyItemWidget::seriesItemWidgetsList //------------------------------------------------------------------------------ void ctkDICOMStudyItemWidget::addSeriesItemWidget(int tableIndex, - const QString &seriesItem, - const QString &seriesInstanceUID, - const QString &modality, - const QString &seriesDescription) + const QString& seriesItem, + const QString& seriesInstanceUID, + const QString& modality, + const QString& seriesDescription) { Q_D(ctkDICOMStudyItemWidget); if (!d->DicomDatabase) @@ -688,7 +688,7 @@ void ctkDICOMStudyItemWidget::addSeriesItemWidget(int tableIndex, this->connect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), d->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); - QTableWidgetItem *tableItem = new QTableWidgetItem; + QTableWidgetItem* tableItem = new QTableWidgetItem; tableItem->setSizeHint(QSize(d->ThumbnailSizePixel, d->ThumbnailSizePixel)); int rowIndex = floor(tableIndex / d->SeriesListTableWidget->columnCount()); @@ -710,7 +710,7 @@ void ctkDICOMStudyItemWidget::removeSeriesItemWidget(const QString& seriesItem) for (int row = 0; row < d->SeriesListTableWidget->rowCount(); row++) { - for (int column = 0 ; column < d->SeriesListTableWidget->columnCount(); column++) + for (int column = 0; column < d->SeriesListTableWidget->columnCount(); column++) { ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast(d->SeriesListTableWidget->cellWidget(row, column)); @@ -723,7 +723,7 @@ void ctkDICOMStudyItemWidget::removeSeriesItemWidget(const QString& seriesItem) this->disconnect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), d->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); delete seriesItemWidget; - QTableWidgetItem *tableItem = d->SeriesListTableWidget->item(row, column); + QTableWidgetItem* tableItem = d->SeriesListTableWidget->item(row, column); delete tableItem; d->addEmptySeriesItemWidget(row, column); @@ -733,7 +733,7 @@ void ctkDICOMStudyItemWidget::removeSeriesItemWidget(const QString& seriesItem) } //------------------------------------------------------------------------------ -ctkCollapsibleGroupBox *ctkDICOMStudyItemWidget::collapsibleGroupBox() +ctkCollapsibleGroupBox* ctkDICOMStudyItemWidget::collapsibleGroupBox() { Q_D(ctkDICOMStudyItemWidget); return d->StudyItemCollapsibleGroupBox; @@ -764,7 +764,7 @@ void ctkDICOMStudyItemWidget::updateGUIFromScheduler(const QVariant& data) Q_D(ctkDICOMStudyItemWidget); ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + if (td.JobUID.isEmpty()) { d->createSeries(); } diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index a183964dd0..135913021b 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -113,7 +113,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget ///@{ /// Set the thumbnail size: small, medium, large /// medium by default - void setThumbnailSize(const ThumbnailSizeOption &thumbnailSize); + void setThumbnailSize(const ThumbnailSizeOption& thumbnailSize); ThumbnailSizeOption thumbnailSize() const; ///@} @@ -165,12 +165,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget Q_INVOKABLE QTableWidget* seriesListTableWidget(); /// Return all the series item widgets for the study - Q_INVOKABLE QList seriesItemWidgetsList()const; + Q_INVOKABLE QList seriesItemWidgetsList() const; /// Add/Remove Series item widget Q_INVOKABLE void addSeriesItemWidget(int tableIndex, - const QString &seriesItem, - const QString &seriesInstanceUID, + const QString& seriesItem, + const QString& seriesInstanceUID, const QString& modality, const QString& seriesDescription); Q_INVOKABLE void removeSeriesItemWidget(const QString& seriesItem); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 6b76311b68..c2533c97ac 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -63,13 +63,13 @@ static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMVisualBrowserWidget" class ctkDICOMMetadataDialog : public QDialog { public: - ctkDICOMMetadataDialog(QWidget *parent = 0) + ctkDICOMMetadataDialog(QWidget* parent = 0) : QDialog(parent) { this->setWindowFlags(Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::Window); this->setModal(true); this->setSizeGripEnabled(true); - QVBoxLayout *layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); this->tagListWidget = new ctkDICOMObjectListWidget(); layout->addWidget(this->tagListWidget); @@ -84,14 +84,14 @@ class ctkDICOMMetadataDialog : public QDialog this->tagListWidget->setFileList(fileList); } - void closeEvent(QCloseEvent *evt) + void closeEvent(QCloseEvent* evt) { // just hide the window when close button is clicked evt->ignore(); this->hide(); } - void showEvent(QShowEvent *event) + void showEvent(QShowEvent* event) { QDialog::showEvent(event); // QDialog would reset window position and size when shown. @@ -108,7 +108,7 @@ class ctkDICOMMetadataDialog : public QDialog } } - void hideEvent(QHideEvent *event) + void hideEvent(QHideEvent* event) { this->savedGeometry = this->saveGeometry(); QDialog::hideEvent(event); @@ -120,13 +120,13 @@ class ctkDICOMMetadataDialog : public QDialog }; //---------------------------------------------------------------------------- -class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget +class ctkDICOMVisualBrowserWidgetPrivate : public Ui_ctkDICOMVisualBrowserWidget { - Q_DECLARE_PUBLIC( ctkDICOMVisualBrowserWidget ); + Q_DECLARE_PUBLIC(ctkDICOMVisualBrowserWidget); protected: ctkDICOMVisualBrowserWidget* const q_ptr; - QToolButton *patientsTabMenuToolButton; + QToolButton* patientsTabMenuToolButton; public: ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMVisualBrowserWidget& obj); @@ -150,20 +150,20 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget bool isPatientTabAlreadyAdded(const QString& patientItem); void updateSeriesTablesSelection(ctkDICOMSeriesItemWidget* selectedSeriesItemWidget); QStringList getPatientUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets); + QList selectedWidgets); QStringList getStudyUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets); + QList selectedWidgets); QStringList getSeriesUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets); + QList selectedWidgets); QStringList filterPatientList(const QStringList& patientList, const QMap& filters); QStringList filterStudyList(const QStringList& patientList, const QMap& filters); QStringList filterSeriesList(const QStringList& patientList, const QMap& filters); - ctkDICOMStudyItemWidget* getCurrentPatientStudyWidgetByUIDs(QString studyInstanceUID); - ctkDICOMSeriesItemWidget* getCurrentPatientSeriesWidgetByUIDs(QString studyInstanceUID, - QString seriesInstanceUID); + ctkDICOMStudyItemWidget* getCurrentPatientStudyWidgetByUIDs(const QString& studyInstanceUID); + ctkDICOMSeriesItemWidget* getCurrentPatientSeriesWidgetByUIDs(const QString& studyInstanceUID, + const QString& seriesInstanceUID); // Return a sanitized version of the string that is safe to be used // as a filename component. @@ -174,17 +174,17 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget QString safeStr; const QString illegalChars("/\\<>:\"|?*"); foreach (const QChar& c, str) - { + { int asciiCode = c.toLatin1(); if (asciiCode >= 32 && asciiCode <= 127 && !illegalChars.contains(c)) - { + { safeStr.append(c); - } + } else - { + { safeStr.append("_"); + } } - } // remove leading/trailing whitespaces return safeStr.trimmed(); } @@ -231,8 +231,8 @@ class ctkDICOMVisualBrowserWidgetPrivate: public Ui_ctkDICOMVisualBrowserWidget bool IsLoading; ctkDICOMServerNodeWidget2* ServerNodeWidget; - QProgressDialog *UpdateSchemaProgress; - QProgressDialog *ExportProgress; + QProgressDialog* UpdateSchemaProgress; + QProgressDialog* ExportProgress; }; CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QString, databaseDirectoryBase, DatabaseDirectoryBase); @@ -245,15 +245,15 @@ CTK_SET_CPP(ctkDICOMVisualBrowserWidget, const QString&, setDatabaseDirectoryBas ctkDICOMVisualBrowserWidgetPrivate::ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMVisualBrowserWidget& obj) : q_ptr(&obj) { - this->DicomDatabase = QSharedPointer (new ctkDICOMDatabase); + this->DicomDatabase = QSharedPointer(new ctkDICOMDatabase); - this->Scheduler = QSharedPointer (new ctkDICOMScheduler); + this->Scheduler = QSharedPointer(new ctkDICOMScheduler); this->Scheduler->setDicomDatabase(this->DicomDatabase); - this->Indexer = QSharedPointer (new ctkDICOMIndexer); + this->Indexer = QSharedPointer(new ctkDICOMIndexer); this->Indexer->setDatabase(this->DicomDatabase.data()); - this->MetadataDialog = QSharedPointer (new ctkDICOMMetadataDialog()); + this->MetadataDialog = QSharedPointer(new ctkDICOMMetadataDialog()); this->MetadataDialog->setObjectName("DICOMMetadata"); this->MetadataDialog->setWindowTitle(ctkDICOMVisualBrowserWidget::tr("DICOM File Metadata")); @@ -368,7 +368,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() q, SLOT(onImport())); // Initialize directoryMode widget - QFormLayout *layout = new QFormLayout; + QFormLayout* layout = new QFormLayout; QComboBox* importDirectoryModeComboBox = new QComboBox(); importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Add Link"), static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryAddLink)); importDirectoryModeComboBox->addItem(ctkDICOMVisualBrowserWidget::tr("Copy"), static_cast(ctkDICOMVisualBrowserWidget::ImportDirectoryCopy)); @@ -384,7 +384,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() importDirectoryModeComboBox->setCurrentIndex( importDirectoryModeComboBox->findData(static_cast(q->importDirectoryMode()))); - //Initialize import widget + // Initialize import widget this->ImportDialog = new ctkFileDialog(); this->ImportDialog->setBottomWidget(importDirectoryBottomWidget); this->ImportDialog->setFileMode(QFileDialog::Directory); @@ -395,7 +395,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() this->ImportDialog->setWindowModality(Qt::ApplicationModal); q->connect(this->ImportDialog, SIGNAL(filesSelected(QStringList)), - q,SLOT(onImportDirectoriesSelected(QStringList))); + q, SLOT(onImportDirectoriesSelected(QStringList))); q->connect(importDirectoryModeComboBox, SIGNAL(currentIndexChanged(int)), q, SLOT(onImportDirectoryComboBoxCurrentIndexChanged(int))); @@ -417,9 +417,9 @@ void ctkDICOMVisualBrowserWidgetPrivate::disconnectScheduler() ctkDICOMVisualBrowserWidget::disconnect(this->Scheduler.data(), SIGNAL(taskFailed(QString, QString)), q, SLOT(onTaskFailed(QString, QString))); ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progress(int)), q, SLOT(onIndexingProgress(int))); - ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progressStep(QString)),q, SLOT(onIndexingProgressStep(QString))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progressStep(QString)), q, SLOT(onIndexingProgressStep(QString))); ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(progressDetail(QString)), q, SLOT(onIndexingProgressDetail(QString))); - ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(indexingComplete(int,int,int,int)), q, SLOT(onIndexingComplete(int,int,int,int))); + ctkDICOMVisualBrowserWidget::disconnect(this->Indexer.data(), SIGNAL(indexingComplete(int, int, int, int)), q, SLOT(onIndexingComplete(int, int, int, int))); } //---------------------------------------------------------------------------- @@ -436,9 +436,9 @@ void ctkDICOMVisualBrowserWidgetPrivate::connectScheduler() ctkDICOMVisualBrowserWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), q, SLOT(onTaskFailed(QVariant))); ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progress(int)), q, SLOT(onIndexingProgress(int))); - ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressStep(QString)),q, SLOT(onIndexingProgressStep(QString))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressStep(QString)), q, SLOT(onIndexingProgressStep(QString))); ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(progressDetail(QString)), q, SLOT(onIndexingProgressDetail(QString))); - ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(indexingComplete(int,int,int,int)), q, SLOT(onIndexingComplete(int,int,int,int))); + ctkDICOMVisualBrowserWidget::connect(this->Indexer.data(), SIGNAL(indexingComplete(int, int, int, int)), q, SLOT(onIndexingComplete(int, int, int, int))); } //---------------------------------------------------------------------------- @@ -466,7 +466,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::importDirectory(QString directory, ctkD } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidgetPrivate::importFiles(const QStringList &files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode) +void ctkDICOMVisualBrowserWidgetPrivate::importFiles(const QStringList& files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode) { if (!this->DicomDatabase) { @@ -567,7 +567,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateModalityCheckableComboBox() foreach (QString modality, this->FilteringModalities) { - QModelIndexList indexList = model->match(model->index(0,0), 0, modality); + QModelIndexList indexList = model->match(model->index(0, 0), 0, modality); if (indexList.length() == 0) { continue; @@ -653,7 +653,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::createPatients() continue; } - q->addPatientItemWidget(patientItem); + q->addPatientItemWidget(patientItem); } this->PatientsTabWidget->setCurrentIndex(0); @@ -784,7 +784,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToFilterWidgets(bool //---------------------------------------------------------------------------- void ctkDICOMVisualBrowserWidgetPrivate::setBackgroundColorToWidget(QColor color, - QWidget *widget) + QWidget* widget) { if (!widget) { @@ -831,20 +831,20 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() QList seriesWidgetsList; for (int patientIndex = 0; patientIndex < this->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(this->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget) { continue; } - QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); for (int row = 0; row < seriesListTableWidget->rowCount(); row++) { - for (int column = 0 ; column < seriesListTableWidget->columnCount(); column++) + for (int column = 0; column < seriesListTableWidget->columnCount(); column++) { ctkDICOMSeriesItemWidget* seriesItemWidget = qobject_cast(seriesListTableWidget->cellWidget(row, column)); @@ -860,7 +860,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() } QList selectedSeriesWidgetsList; - QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); @@ -917,7 +917,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() seriesInstanceUIDsToStop); bool wait = true; - while(wait) + while (wait) { this->Scheduler->waitForDone(300); wait = false; @@ -966,7 +966,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::removeAllPatientItemWidgets() int wasBlocking = this->PatientsTabWidget->blockSignals(true); for (int patientIndex = 0; patientIndex < this->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(this->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget) { @@ -977,10 +977,10 @@ void ctkDICOMVisualBrowserWidgetPrivate::removeAllPatientItemWidgets() q->disconnect(patientItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), q, SLOT(showPatientContextMenu(const QPoint&))); - QList studyItemWidgets = patientItemWidget->studyItemWidgetsList(); - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgets) + QList studyItemWidgets = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgets) { - q->disconnect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemDoubleClicked(QTableWidgetItem *)), + q->disconnect(studyItemWidget->seriesListTableWidget(), SIGNAL(itemDoubleClicked(QTableWidgetItem*)), q, SLOT(onLoad())); } @@ -991,7 +991,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::removeAllPatientItemWidgets() } //---------------------------------------------------------------------------- -bool ctkDICOMVisualBrowserWidgetPrivate::isPatientTabAlreadyAdded(const QString &patientItem) +bool ctkDICOMVisualBrowserWidgetPrivate::isPatientTabAlreadyAdded(const QString& patientItem) { bool alreadyAdded = false; for (int index = 0; index < this->PatientsTabWidget->count(); ++index) @@ -1021,16 +1021,16 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateSeriesTablesSelection(ctkDICOMSer return; } - QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { if (!studyItemWidget) { continue; } - QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); QList selectedItems = seriesListTableWidget->selectedItems(); - foreach (QTableWidgetItem *selectedItem, selectedItems) + foreach (QTableWidgetItem* selectedItem, selectedItems) { if (!selectedItem) { @@ -1053,16 +1053,16 @@ void ctkDICOMVisualBrowserWidgetPrivate::updateSeriesTablesSelection(ctkDICOMSer //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::getPatientUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets) + QList selectedWidgets) { QStringList selectedPatientUIDs; - if(!this->DicomDatabase) + if (!this->DicomDatabase) { return selectedPatientUIDs; } - foreach (QWidget *selectedWidget, selectedWidgets) + foreach (QWidget* selectedWidget, selectedWidgets) { if (!selectedWidget) { @@ -1071,7 +1071,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getPatientUIDsFromWidgets(ctkDIC if (level == ctkDICOMModel::PatientType) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(selectedWidget); if (patientItemWidget) { @@ -1086,16 +1086,16 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getPatientUIDsFromWidgets(ctkDIC //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::getStudyUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets) + QList selectedWidgets) { QStringList selectedStudyUIDs; - if(!this->DicomDatabase) + if (!this->DicomDatabase) { return selectedStudyUIDs; } - foreach (QWidget *selectedWidget, selectedWidgets) + foreach (QWidget* selectedWidget, selectedWidgets) { if (!selectedWidget) { @@ -1104,7 +1104,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getStudyUIDsFromWidgets(ctkDICOM if (level == ctkDICOMModel::PatientType) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(selectedWidget); if (patientItemWidget) { @@ -1127,17 +1127,17 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getStudyUIDsFromWidgets(ctkDICOM //---------------------------------------------------------------------------- QStringList ctkDICOMVisualBrowserWidgetPrivate::getSeriesUIDsFromWidgets(ctkDICOMModel::IndexType level, - QList selectedWidgets) + QList selectedWidgets) { QStringList selectedStudyUIDs; QStringList selectedSeriesUIDs; - if(!this->DicomDatabase) + if (!this->DicomDatabase) { return selectedSeriesUIDs; } - foreach (QWidget *selectedWidget, selectedWidgets) + foreach (QWidget* selectedWidget, selectedWidgets) { if (!selectedWidget) { @@ -1146,7 +1146,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getSeriesUIDsFromWidgets(ctkDICO if (level == ctkDICOMModel::PatientType) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(selectedWidget); if (patientItemWidget) { @@ -1174,7 +1174,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::getSeriesUIDsFromWidgets(ctkDICO } else { - foreach(const QString& uid, selectedStudyUIDs) + foreach (const QString& uid, selectedStudyUIDs) { selectedSeriesUIDs << this->DicomDatabase->seriesForStudy(uid); } @@ -1198,7 +1198,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterPatientList(const QStringL foreach (QString patientItem, patientList) { bool filtered = false; - for(QString key : filters.keys()) + for (QString key : filters.keys()) { QString filter = this->DicomDatabase->fieldForPatient(key, patientItem); QString filterValue = filters.value(key).toString(); @@ -1234,7 +1234,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterStudyList(const QStringLis foreach (QString studyItem, studyList) { bool filtered = false; - for(QString key : filters.keys()) + for (QString key : filters.keys()) { QString filter = this->DicomDatabase->fieldForStudy(key, studyItem); QString filterValue = filters.value(key).toString(); @@ -1286,7 +1286,7 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringLi foreach (QString seriesItem, seriesList) { bool filtered = false; - for(QString key : filters.keys()) + for (QString key : filters.keys()) { QString filter = this->DicomDatabase->fieldForSeries(key, seriesItem); if (key == "Modality") @@ -1321,17 +1321,17 @@ QStringList ctkDICOMVisualBrowserWidgetPrivate::filterSeriesList(const QStringLi } //---------------------------------------------------------------------------- -ctkDICOMStudyItemWidget *ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientStudyWidgetByUIDs(QString studyInstanceUID) +ctkDICOMStudyItemWidget* ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientStudyWidgetByUIDs(const QString& studyInstanceUID) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(this->PatientsTabWidget->currentWidget()); if (!patientItemWidget) { return nullptr; } - QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { if (!studyItemWidget || studyItemWidget->studyInstanceUID() != studyInstanceUID) { @@ -1345,17 +1345,17 @@ ctkDICOMStudyItemWidget *ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientSt } //---------------------------------------------------------------------------- -ctkDICOMSeriesItemWidget *ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientSeriesWidgetByUIDs(QString studyInstanceUID, - QString seriesInstanceUID) +ctkDICOMSeriesItemWidget* ctkDICOMVisualBrowserWidgetPrivate::getCurrentPatientSeriesWidgetByUIDs(const QString& studyInstanceUID, + const QString& seriesInstanceUID) { - ctkDICOMStudyItemWidget *studyItemWidget = this->getCurrentPatientStudyWidgetByUIDs(studyInstanceUID); + ctkDICOMStudyItemWidget* studyItemWidget = this->getCurrentPatientStudyWidgetByUIDs(studyInstanceUID); if (!studyItemWidget) { return nullptr; } - QList seriesItemWidgetsList = studyItemWidget->seriesItemWidgetsList(); - foreach (ctkDICOMSeriesItemWidget *seriesItemWidget, seriesItemWidgetsList) + QList seriesItemWidgetsList = studyItemWidget->seriesItemWidgetsList(); + foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, seriesItemWidgetsList) { if (!seriesItemWidget || seriesItemWidget->seriesInstanceUID() != seriesInstanceUID) { @@ -1402,7 +1402,7 @@ QString ctkDICOMVisualBrowserWidget::databaseDirectorySettingsKey() const } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidget::setDatabaseDirectorySettingsKey(const QString &key) +void ctkDICOMVisualBrowserWidget::setDatabaseDirectorySettingsKey(const QString& key) { Q_D(ctkDICOMVisualBrowserWidget); d->DatabaseDirectorySettingsKey = key; @@ -1421,14 +1421,14 @@ static void skipDelete(QObject* obj) } //---------------------------------------------------------------------------- -ctkDICOMScheduler* ctkDICOMVisualBrowserWidget::scheduler()const +ctkDICOMScheduler* ctkDICOMVisualBrowserWidget::scheduler() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->Scheduler.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMVisualBrowserWidget::schedulerShared()const +QSharedPointer ctkDICOMVisualBrowserWidget::schedulerShared() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->Scheduler; @@ -1455,14 +1455,14 @@ void ctkDICOMVisualBrowserWidget::setScheduler(QSharedPointer } //---------------------------------------------------------------------------- -ctkDICOMDatabase* ctkDICOMVisualBrowserWidget::dicomDatabase()const +ctkDICOMDatabase* ctkDICOMVisualBrowserWidget::dicomDatabase() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->DicomDatabase.data(); } //---------------------------------------------------------------------------- -QSharedPointer ctkDICOMVisualBrowserWidget::dicomDatabaseShared()const +QSharedPointer ctkDICOMVisualBrowserWidget::dicomDatabaseShared() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->DicomDatabase; @@ -1502,7 +1502,7 @@ void ctkDICOMVisualBrowserWidget::setStorageAETitle(const QString& storageAETitl } //---------------------------------------------------------------------------- -QString ctkDICOMVisualBrowserWidget::storageAETitle()const +QString ctkDICOMVisualBrowserWidget::storageAETitle() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget->storageAETitle(); @@ -1516,7 +1516,7 @@ void ctkDICOMVisualBrowserWidget::setStoragePort(int storagePort) } //---------------------------------------------------------------------------- -int ctkDICOMVisualBrowserWidget::storagePort()const +int ctkDICOMVisualBrowserWidget::storagePort() const { Q_D(const ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget->storagePort(); @@ -1586,14 +1586,14 @@ int ctkDICOMVisualBrowserWidget::getServerIndexFromName(const QString& connectio } //------------------------------------------------------------------------------ -ctkDICOMServerNodeWidget2 *ctkDICOMVisualBrowserWidget::serverSettingsWidget() +ctkDICOMServerNodeWidget2* ctkDICOMVisualBrowserWidget::serverSettingsWidget() { Q_D(ctkDICOMVisualBrowserWidget); return d->ServerNodeWidget; } //------------------------------------------------------------------------------ -ctkCollapsibleGroupBox *ctkDICOMVisualBrowserWidget::serverSettingsGroupBox() +ctkCollapsibleGroupBox* ctkDICOMVisualBrowserWidget::serverSettingsGroupBox() { Q_D(ctkDICOMVisualBrowserWidget); return d->ServersSettingsCollapsibleGroupBox; @@ -1645,7 +1645,7 @@ QString ctkDICOMVisualBrowserWidget::filteringStudyDescription() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType &filteringDate) +void ctkDICOMVisualBrowserWidget::setFilteringDate(const ctkDICOMPatientItemWidget::DateType& filteringDate) { Q_D(ctkDICOMVisualBrowserWidget); d->FilteringDate = filteringDate; @@ -1675,7 +1675,7 @@ QString ctkDICOMVisualBrowserWidget::filteringSeriesDescription() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setFilteringModalities(const QStringList &filteringModalities) +void ctkDICOMVisualBrowserWidget::setFilteringModalities(const QStringList& filteringModalities) { Q_D(ctkDICOMVisualBrowserWidget); d->FilteringModalities = filteringModalities; @@ -1704,7 +1704,7 @@ int ctkDICOMVisualBrowserWidget::numberOfStudiesPerPatient() const } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize) +void ctkDICOMVisualBrowserWidget::setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize) { Q_D(ctkDICOMVisualBrowserWidget); d->ThumbnailSize = thumbnailSize; @@ -1758,7 +1758,7 @@ void ctkDICOMVisualBrowserWidget::addPatientItemWidget(const QString& patientIte QString patientName = d->DicomDatabase->fieldForPatient("PatientsName", patientItem); QString patientID = d->DicomDatabase->fieldForPatient("PatientID", patientItem); - ctkDICOMPatientItemWidget *patientItemWidget = new ctkDICOMPatientItemWidget(this); + ctkDICOMPatientItemWidget* patientItemWidget = new ctkDICOMPatientItemWidget(this); patientItemWidget->setPatientItem(patientItem); patientItemWidget->setPatientID(patientID); patientItemWidget->setFilteringStudyDescription(d->FilteringStudyDescription); @@ -1779,13 +1779,13 @@ void ctkDICOMVisualBrowserWidget::addPatientItemWidget(const QString& patientIte } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidget::removePatientItemWidget(const QString &patientItem) +void ctkDICOMVisualBrowserWidget::removePatientItemWidget(const QString& patientItem) { Q_D(ctkDICOMVisualBrowserWidget); for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget || patientItemWidget->patientItem() != patientItem) @@ -1802,7 +1802,7 @@ void ctkDICOMVisualBrowserWidget::removePatientItemWidget(const QString &patient } //------------------------------------------------------------------------------ -ctkDICOMPatientItemWidget* ctkDICOMVisualBrowserWidget::getPatientItemWidgetByPatientName(const QString &patientName) +ctkDICOMPatientItemWidget* ctkDICOMVisualBrowserWidget::getPatientItemWidgetByPatientName(const QString& patientName) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -1812,7 +1812,7 @@ ctkDICOMPatientItemWidget* ctkDICOMVisualBrowserWidget::getPatientItemWidgetByPa for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget) { @@ -1882,10 +1882,10 @@ ctkDICOMVisualBrowserWidget::ImportDirectoryMode ctkDICOMVisualBrowserWidget::im } //------------------------------------------------------------------------------ -ctkFileDialog *ctkDICOMVisualBrowserWidget::importDialog() const +ctkFileDialog* ctkDICOMVisualBrowserWidget::importDialog() const { Q_D(const ctkDICOMVisualBrowserWidget); - return d->ImportDialog; + return d->ImportDialog; } //------------------------------------------------------------------------------ @@ -1901,14 +1901,14 @@ void ctkDICOMVisualBrowserWidget::setImportDirectoryMode(ImportDirectoryMode mod } if (!(d->ImportDialog->options() & QFileDialog::DontUseNativeDialog)) { - return; // Native dialog does not support modifying or getting widget elements. + return; // Native dialog does not support modifying or getting widget elements. } QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild(); comboBox->setCurrentIndex(comboBox->findData(static_cast(mode))); } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString &directory) +void ctkDICOMVisualBrowserWidget::setDatabaseDirectory(const QString& directory) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -2040,7 +2040,7 @@ void ctkDICOMVisualBrowserWidget::importDirectory(const QString& directory, Impo } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::importFiles(const QStringList &files, ImportDirectoryMode mode) +void ctkDICOMVisualBrowserWidget::importFiles(const QStringList& files, ImportDirectoryMode mode) { Q_D(ctkDICOMVisualBrowserWidget); d->importFiles(files, mode); @@ -2066,14 +2066,14 @@ void ctkDICOMVisualBrowserWidget::onIndexingProgress(int percent) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onIndexingProgressStep(const QString & step) +void ctkDICOMVisualBrowserWidget::onIndexingProgressStep(const QString& step) { Q_D(const ctkDICOMVisualBrowserWidget); d->ProgressLabel->setText(step); } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onIndexingProgressDetail(const QString & detail) +void ctkDICOMVisualBrowserWidget::onIndexingProgressDetail(const QString& detail) { Q_D(const ctkDICOMVisualBrowserWidget); if (detail.isEmpty()) @@ -2164,7 +2164,7 @@ void ctkDICOMVisualBrowserWidget::createNewDatabaseDirectory() // Try folder names, starting with the current one, // incrementing the original numerical suffix. int attemptsCount = 100; - for (int attempt=0; attempt& selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -2237,7 +2237,7 @@ QStringList ctkDICOMVisualBrowserWidget::fileListForCurrentSelection(ctkDICOMMod QStringList selectedSeriesUIDs = d->getSeriesUIDsFromWidgets(level, selectedWidgets); QStringList fileList; - foreach(const QString& selectedSeriesUID, selectedSeriesUIDs) + foreach (const QString& selectedSeriesUID, selectedSeriesUIDs) { fileList << d->DicomDatabase->filesForSeries(selectedSeriesUID); } @@ -2245,7 +2245,7 @@ QStringList ctkDICOMVisualBrowserWidget::fileListForCurrentSelection(ctkDICOMMod } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::showMetadata(const QStringList &fileList) +void ctkDICOMVisualBrowserWidget::showMetadata(const QStringList& fileList) { Q_D(const ctkDICOMVisualBrowserWidget); d->MetadataDialog->setFileList(fileList); @@ -2254,7 +2254,7 @@ void ctkDICOMVisualBrowserWidget::showMetadata(const QStringList &fileList) //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType level, - const QList& selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -2271,14 +2271,14 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l { for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget) { continue; } QString patientItem = patientItemWidget->patientItem(); - QString patientID = patientItemWidget->patientID(); + QString patientID = patientItemWidget->patientID(); selectedStudyUIDs << d->DicomDatabase->studiesForPatient(patientItem); selectedPatientUIDs << patientID; } @@ -2315,7 +2315,7 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l } } - foreach (QWidget *selectedWidget, selectedWidgets) + foreach (QWidget* selectedWidget, selectedWidgets) { if (!selectedWidget) { @@ -2323,7 +2323,7 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l } else if (level == ctkDICOMModel::PatientType) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(selectedWidget); if (patientItemWidget) { @@ -2339,7 +2339,7 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l qobject_cast(selectedWidget); if (studyItemWidget) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->currentWidget()); if (patientItemWidget) { @@ -2354,12 +2354,12 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l qobject_cast(selectedWidget); if (seriesItemWidget) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->currentWidget()); if (patientItemWidget) { - QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + QList studyItemWidgetsList = patientItemWidget->studyItemWidgetsList(); + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { if (!studyItemWidget || studyItemWidget->studyInstanceUID() != seriesItemWidget->studyInstanceUID()) { @@ -2374,7 +2374,7 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l } else { - foreach(const QString& uid, selectedStudyUIDs) + foreach (const QString& uid, selectedStudyUIDs) { selectedSeriesUIDs << d->DicomDatabase->seriesForStudy(uid); } @@ -2386,15 +2386,15 @@ void ctkDICOMVisualBrowserWidget::removeSelectedItems(ctkDICOMModel::IndexType l selectedStudyUIDs, selectedSeriesUIDs); - foreach(const QString & uid, selectedSeriesUIDs) + foreach (const QString& uid, selectedSeriesUIDs) { d->DicomDatabase->removeSeries(uid, false, level == ctkDICOMModel::RootType); } - foreach(const QString & uid, selectedStudyUIDs) + foreach (const QString& uid, selectedStudyUIDs) { d->DicomDatabase->removeStudy(uid, level == ctkDICOMModel::RootType); } - foreach(const QString & uid, selectedPatientUIDs) + foreach (const QString& uid, selectedPatientUIDs) { d->DicomDatabase->removePatient(uid, level == ctkDICOMModel::RootType); } @@ -2436,7 +2436,7 @@ void ctkDICOMVisualBrowserWidget::onFilteringModalityCheckableComboBoxChanged() d->PreviousFilteringModalities = d->FilteringModalities; d->FilteringModalities.clear(); QModelIndexList indexList = d->FilteringModalityCheckableComboBox->checkedIndexes(); - foreach(QModelIndex index, indexList) + foreach (QModelIndex index, indexList) { QVariant value = d->FilteringModalityCheckableComboBox->checkableModel()->data(index); d->FilteringModalities.append(value.toString()); @@ -2624,7 +2624,7 @@ void ctkDICOMVisualBrowserWidget::onTaskFailed(const QVariant& data) if (td.JobClass == "ctkDICOMRetrieveJob") { - ctkDICOMSeriesItemWidget *seriesItemWidget = + ctkDICOMSeriesItemWidget* seriesItemWidget = d->getCurrentPatientSeriesWidgetByUIDs(td.StudyInstanceUID, td.SeriesInstanceUID); if (seriesItemWidget) { @@ -2652,11 +2652,11 @@ void ctkDICOMVisualBrowserWidget::onPatientItemChanged(int index) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) +void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint& point) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->currentWidget()); if (!patientItemWidget) { @@ -2667,23 +2667,23 @@ void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) selectedWidgets.append(patientItemWidget); QPoint globalPos = patientItemWidget->mapToGlobal(point); - QMenu *patientMenu = new QMenu(); + QMenu* patientMenu = new QMenu(); QString loadString = tr("Load patient files"); - QAction *loadAction = new QAction(loadString, patientMenu); + QAction* loadAction = new QAction(loadString, patientMenu); patientMenu->addAction(loadAction); QString metadataString = tr("View patient DICOM metadata"); - QAction *metadataAction = new QAction(metadataString, patientMenu); + QAction* metadataAction = new QAction(metadataString, patientMenu); patientMenu->addAction(metadataAction); QString deleteString = tr("Delete patient from local database"); - QAction *deleteAction = new QAction(deleteString, patientMenu); + QAction* deleteAction = new QAction(deleteString, patientMenu); patientMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); QString exportString = tr("Export patient to file system"); - QAction *exportAction = new QAction(exportString, patientMenu); + QAction* exportAction = new QAction(exportString, patientMenu); patientMenu->addAction(exportAction); QString sendString = tr("Send patient to DICOM server"); @@ -2691,7 +2691,7 @@ void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) sendAction->setVisible(this->isSendActionVisible()); patientMenu->addAction(sendAction); - QAction *selectedAction = patientMenu->exec(globalPos); + QAction* selectedAction = patientMenu->exec(globalPos); if (selectedAction == loadAction) { // first select all the series for all studies @@ -2706,7 +2706,7 @@ void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) } else if (selectedAction == metadataAction) { - this->showMetadata(this->fileListForCurrentSelection(ctkDICOMModel::PatientType, selectedWidgets)); + this->showMetadata(this->fileListForCurrentSelection(ctkDICOMModel::PatientType, selectedWidgets)); } else if (selectedAction == deleteAction) { @@ -2723,7 +2723,7 @@ void ctkDICOMVisualBrowserWidget::showPatientContextMenu(const QPoint &point) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) +void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint& point) { Q_D(ctkDICOMVisualBrowserWidget); @@ -2743,9 +2743,9 @@ void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) return; } - QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); - QList selectedWidgets; - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + QList selectedWidgets; + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { if (!studyItemWidget || !studyItemWidget->selection()) { @@ -2758,27 +2758,27 @@ void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) int numberOfSelectedStudies = selectedWidgets.count(); QPoint globalPos = studyItemWidget->mapToGlobal(point); - QMenu *studyMenu = new QMenu(); + QMenu* studyMenu = new QMenu(); QString loadString = numberOfSelectedStudies == 1 ? tr("Load study") : tr("Load %1 studies").arg(numberOfSelectedStudies); - QAction *loadAction = new QAction(loadString, studyMenu); + QAction* loadAction = new QAction(loadString, studyMenu); studyMenu->addAction(loadAction); QString metadataString = numberOfSelectedStudies == 1 ? tr("View study DICOM metadata") : tr("View %1 studies DICOM metadata").arg(numberOfSelectedStudies); - QAction *metadataAction = new QAction(metadataString, studyMenu); + QAction* metadataAction = new QAction(metadataString, studyMenu); studyMenu->addAction(metadataAction); QString deleteString = numberOfSelectedStudies == 1 ? tr("Delete study from local database") : tr("Delete %1 studies from local database").arg(numberOfSelectedStudies); - QAction *deleteAction = new QAction(deleteString, studyMenu); + QAction* deleteAction = new QAction(deleteString, studyMenu); studyMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); QString exportString = numberOfSelectedStudies == 1 ? tr("Export study to file system") : tr("Export %1 studies to file system").arg(numberOfSelectedStudies); - QAction *exportAction = new QAction(exportString, studyMenu); + QAction* exportAction = new QAction(exportString, studyMenu); studyMenu->addAction(exportAction); QString sendString = numberOfSelectedStudies == 1 ? tr("Send study to DICOM server") : @@ -2787,7 +2787,7 @@ void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) sendAction->setVisible(this->isSendActionVisible()); studyMenu->addAction(sendAction); - QAction *selectedAction = studyMenu->exec(globalPos); + QAction* selectedAction = studyMenu->exec(globalPos); if (selectedAction == loadAction) { this->onLoad(); @@ -2811,7 +2811,7 @@ void ctkDICOMVisualBrowserWidget::showStudyContextMenu(const QPoint &point) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) +void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint& point) { Q_D(ctkDICOMVisualBrowserWidget); @@ -2830,17 +2830,17 @@ void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) } d->updateSeriesTablesSelection(selectedSeriesItemWidget); - QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); - QList selectedWidgets; - foreach (ctkDICOMStudyItemWidget *studyItemWidget, studyItemWidgetsList) + QList studyItemWidgetsList = currentPatientItemWidget->studyItemWidgetsList(); + QList selectedWidgets; + foreach (ctkDICOMStudyItemWidget* studyItemWidget, studyItemWidgetsList) { if (!studyItemWidget) { continue; } - QTableWidget *seriesListTableWidget = studyItemWidget->seriesListTableWidget(); + QTableWidget* seriesListTableWidget = studyItemWidget->seriesListTableWidget(); QList selectedItems = seriesListTableWidget->selectedItems(); - foreach (QTableWidgetItem *selectedItem, selectedItems) + foreach (QTableWidgetItem* selectedItem, selectedItems) { if (!selectedItem) { @@ -2859,7 +2859,7 @@ void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) int numberOfSelectedSeries = selectedWidgets.count(); QPoint globalPos = selectedSeriesItemWidget->mapToGlobal(point); - QMenu *seriesMenu = new QMenu(); + QMenu* seriesMenu = new QMenu(); QString loadString = numberOfSelectedSeries == 1 ? tr("Load series") : tr("Load %1 series").arg(numberOfSelectedSeries); @@ -2888,7 +2888,7 @@ void ctkDICOMVisualBrowserWidget::showSeriesContextMenu(const QPoint &point) sendAction->setVisible(this->isSendActionVisible()); seriesMenu->addAction(sendAction); - QAction *selectedAction = seriesMenu->exec(globalPos); + QAction* selectedAction = seriesMenu->exec(globalPos); if (selectedAction == loadAction) { this->onLoad(); @@ -2920,11 +2920,11 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() globalPos.setY(globalPos.y() + d->patientsTabMenuToolButton->height() * 3); globalPos.setX(globalPos.x()); - QMenu *patientMenu = new QMenu(); + QMenu* patientMenu = new QMenu(); patientMenu->move(globalPos); for (int patientIndex = 0; patientIndex < d->PatientsTabWidget->count(); ++patientIndex) { - ctkDICOMPatientItemWidget *patientItemWidget = + ctkDICOMPatientItemWidget* patientItemWidget = qobject_cast(d->PatientsTabWidget->widget(patientIndex)); if (!patientItemWidget) { @@ -2934,7 +2934,7 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() QString patientItem = patientItemWidget->patientItem(); QString patientName = d->DicomDatabase->fieldForPatient("PatientsName", patientItem); patientName.replace(R"(^)", R"( )"); - QAction *changePatientAction = new QAction(patientName, patientMenu); + QAction* changePatientAction = new QAction(patientName, patientMenu); if (patientItemWidget == d->PatientsTabWidget->currentWidget()) { changePatientAction->setIcon(QIcon(":Icons/patient.svg")); @@ -2947,12 +2947,12 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() patientMenu->addSeparator(); QString deleteString = tr("Delete all Patients from local database"); - QAction *deleteAction = new QAction(deleteString, patientMenu); + QAction* deleteAction = new QAction(deleteString, patientMenu); deleteAction->setIcon(QIcon(":Icons/delete.svg")); patientMenu->addAction(deleteAction); deleteAction->setVisible(this->isDeleteActionVisible()); - QAction *selectedAction = patientMenu->exec(globalPos); + QAction* selectedAction = patientMenu->exec(globalPos); if (selectedAction == deleteAction) { this->removeSelectedItems(ctkDICOMModel::RootType); @@ -2961,7 +2961,7 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() else if (selectedAction) { QString patientName = selectedAction->text(); - ctkDICOMPatientItemWidget *patientItemWidget = this->getPatientItemWidgetByPatientName(patientName); + ctkDICOMPatientItemWidget* patientItemWidget = this->getPatientItemWidgetByPatientName(patientName); if (patientItemWidget) { d->PatientsTabWidget->setCurrentWidget(patientItemWidget); @@ -2971,7 +2971,7 @@ void ctkDICOMVisualBrowserWidget::onPatientsTabMenuToolButtonClicked() //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::exportSelectedItems(ctkDICOMModel::IndexType level, - const QList& selectedWidgets) + const QList& selectedWidgets) { Q_D(ctkDICOMVisualBrowserWidget); if (!d->DicomDatabase) @@ -3015,16 +3015,16 @@ void ctkDICOMVisualBrowserWidget::exportSeries(const QString& dirPath, const QSt // Use the first file to get the overall series information QString firstFilePath = filesForSeries[0]; - QHash descriptions (d->DicomDatabase->descriptionsForFile(firstFilePath)); + QHash descriptions(d->DicomDatabase->descriptionsForFile(firstFilePath)); QString patientName = descriptions["PatientsName"]; QString patientIDTag = QString("0010,0020"); QString patientID = d->DicomDatabase->fileValue(firstFilePath, patientIDTag); QString studyDescription = descriptions["StudyDescription"]; QString seriesDescription = descriptions["SeriesDescription"]; QString studyDateTag = QString("0008,0020"); - QString studyDate = d->DicomDatabase->fileValue(firstFilePath,studyDateTag); + QString studyDate = d->DicomDatabase->fileValue(firstFilePath, studyDateTag); QString seriesNumberTag = QString("0020,0011"); - QString seriesNumber = d->DicomDatabase->fileValue(firstFilePath,seriesNumberTag); + QString seriesNumber = d->DicomDatabase->fileValue(firstFilePath, seriesNumberTag); QString sep = "/"; QString nameSep = "-"; @@ -3070,7 +3070,7 @@ void ctkDICOMVisualBrowserWidget::exportSeries(const QString& dirPath, const QSt d->ExportProgress->setWindowModality(Qt::ApplicationModal); d->ExportProgress->setMinimumDuration(0); } - QLabel *exportLabel = new QLabel( + QLabel* exportLabel = new QLabel( //: %1 is the series number tr("Exporting series %1").arg(seriesNumber) ); @@ -3153,7 +3153,7 @@ void ctkDICOMVisualBrowserWidget::onImportDirectoryComboBoxCurrentIndexChanged(i Q_UNUSED(index); if (!(d->ImportDialog->options() & QFileDialog::DontUseNativeDialog)) { - return; // Native dialog does not support modifying or getting widget elements. + return; // Native dialog does not support modifying or getting widget elements. } QComboBox* comboBox = d->ImportDialog->bottomWidget()->findChild(); ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode = @@ -3229,14 +3229,14 @@ void ctkDICOMVisualBrowserWidget::onStop(bool stopPersistentTasks) } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::setCurrentTabWidget(ctkDICOMPatientItemWidget *patientItemWidget) +void ctkDICOMVisualBrowserWidget::setCurrentTabWidget(ctkDICOMPatientItemWidget* patientItemWidget) { Q_D(ctkDICOMVisualBrowserWidget); d->PatientsTabWidget->setCurrentWidget(patientItemWidget); } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::closeEvent(QCloseEvent *event) +void ctkDICOMVisualBrowserWidget::closeEvent(QCloseEvent* event) { this->onStop(); event->accept(); diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index b0d439f22b..bcf8aaa70f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -220,7 +220,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget ///@{ /// Set the thumbnail size: small, medium, large /// medium by default - void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption &thumbnailSize); + void setThumbnailSize(const ctkDICOMStudyItemWidget::ThumbnailSizeOption& thumbnailSize); ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize() const; ///@} @@ -267,12 +267,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget /// \brief Get value of ImportDirectoryMode settings. /// /// \sa setImportDirectoryMode(ctkDICOMBrowser::ImportDirectoryMode) - ImportDirectoryMode importDirectoryMode()const; + ImportDirectoryMode importDirectoryMode() const; /// \brief Return instance of import dialog. /// /// \internal - Q_INVOKABLE ctkFileDialog* importDialog()const; + Q_INVOKABLE ctkFileDialog* importDialog() const; public Q_SLOTS: /// \brief Set value of ImportDirectoryMode settings. @@ -357,7 +357,7 @@ public Q_SLOTS: void onLoad(); void onImport(); void onStop(bool stopPersistentTasks = false); - void setCurrentTabWidget(ctkDICOMPatientItemWidget *patientItemWidget); + void setCurrentTabWidget(ctkDICOMPatientItemWidget* patientItemWidget); Q_SIGNALS: /// Emitted when directory is changed @@ -370,7 +370,7 @@ public Q_SLOTS: void directoryImported(); protected: - void closeEvent(QCloseEvent *); + void closeEvent(QCloseEvent*); QScopedPointer d_ptr; /// Confirm with the user that they wish to delete the selected uids. @@ -382,13 +382,13 @@ public Q_SLOTS: bool confirmDeleteSelectedUIDs(const QStringList& uids); /// Get file list for right click selection - QStringList fileListForCurrentSelection(ctkDICOMModel::IndexType level, const QList& selectedWidget); + QStringList fileListForCurrentSelection(ctkDICOMModel::IndexType level, const QList& selectedWidget); /// Show window that displays DICOM fields of all selected items void showMetadata(const QStringList& fileList); /// Remove items (both database and widget) - void removeSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets = QList()); + void removeSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets = QList()); /// Export the items associated with the selected widget - void exportSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets); + void exportSelectedItems(ctkDICOMModel::IndexType level, const QList& selectedWidgets); /// Export the series associated with the selected UIDs void exportSeries(const QString& dirPath, const QStringList& uids);