From e433090fc785de0eec7e15977dee7d295f4b46b2 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Fri, 30 Aug 2024 12:26:49 +0200 Subject: [PATCH] ENH: Generate thumbnails in workers --- Libs/DICOM/Core/CMakeLists.txt | 15 +- .../Core/ctkDICOMAbstractThumbnailGenerator.h | 6 +- Libs/DICOM/Core/ctkDICOMDatabase.cpp | 6 +- Libs/DICOM/Core/ctkDICOMDatabase.h | 3 +- Libs/DICOM/Core/ctkDICOMEchoJob.cpp | 2 +- Libs/DICOM/Core/ctkDICOMIndexer_p.h | 1 - Libs/DICOM/Core/ctkDICOMJob.h | 1 - Libs/DICOM/Core/ctkDICOMJobResponseSet.h | 3 +- Libs/DICOM/Core/ctkDICOMScheduler.cpp | 79 +++++++++- Libs/DICOM/Core/ctkDICOMScheduler.h | 10 ++ Libs/DICOM/Core/ctkDICOMScheduler_p.h | 2 + .../ctkDICOMThumbnailGenerator.cpp | 14 +- .../ctkDICOMThumbnailGenerator.h | 12 +- .../Core/ctkDICOMThumbnailGeneratorJob.cpp | 129 +++++++++++++++ .../Core/ctkDICOMThumbnailGeneratorJob.h | 112 +++++++++++++ .../Core/ctkDICOMThumbnailGeneratorJob_p.h | 54 +++++++ .../Core/ctkDICOMThumbnailGeneratorWorker.cpp | 149 ++++++++++++++++++ .../Core/ctkDICOMThumbnailGeneratorWorker.h | 75 +++++++++ .../Core/ctkDICOMThumbnailGeneratorWorker_p.h | 51 ++++++ Libs/DICOM/Widgets/CMakeLists.txt | 3 - Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp | 6 +- .../Widgets/ctkDICOMSeriesItemWidget.cpp | 33 ++-- 22 files changed, 723 insertions(+), 43 deletions(-) rename Libs/DICOM/{Widgets => Core}/ctkDICOMThumbnailGenerator.cpp (94%) rename Libs/DICOM/{Widgets => Core}/ctkDICOMThumbnailGenerator.h (88%) create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.h create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob_p.h create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.cpp create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.h create mode 100644 Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker_p.h diff --git a/Libs/DICOM/Core/CMakeLists.txt b/Libs/DICOM/Core/CMakeLists.txt index 5a67ae3b48..7ac2f12981 100644 --- a/Libs/DICOM/Core/CMakeLists.txt +++ b/Libs/DICOM/Core/CMakeLists.txt @@ -77,6 +77,14 @@ set(KIT_SRCS ctkDICOMStorageListenerWorker_p.h ctkDICOMTester.cpp ctkDICOMTester.h + ctkDICOMThumbnailGenerator.cpp + ctkDICOMThumbnailGenerator.h + ctkDICOMThumbnailGeneratorJob.cpp + ctkDICOMThumbnailGeneratorJob.h + ctkDICOMThumbnailGeneratorJob_p.h + ctkDICOMThumbnailGeneratorWorker.cpp + ctkDICOMThumbnailGeneratorWorker.h + ctkDICOMThumbnailGeneratorWorker_p.h ctkDICOMUtil.cpp ctkDICOMUtil.h ctkDICOMDisplayedFieldGeneratorRuleFactory.h @@ -144,6 +152,11 @@ set(KIT_MOC_SRCS ctkDICOMStorageListenerWorker.h ctkDICOMStorageListenerWorker_p.h ctkDICOMTester.h + ctkDICOMThumbnailGenerator.h + ctkDICOMThumbnailGeneratorJob.h + ctkDICOMThumbnailGeneratorJob_p.h + ctkDICOMThumbnailGeneratorWorker.h + ctkDICOMThumbnailGeneratorWorker_p.h ) # UI files @@ -159,7 +172,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 Qt${CTK_QT_VERSION}::Sql) +list(APPEND KIT_target_libraries Qt${CTK_QT_VERSION}::Sql Qt${CTK_QT_VERSION}::Svg) # create a dcm query/retrieve service config file that points to the build dir set (DCMQRSCP_STORE_DIR ${CMAKE_CURRENT_BINARY_DIR}/Testing) diff --git a/Libs/DICOM/Core/ctkDICOMAbstractThumbnailGenerator.h b/Libs/DICOM/Core/ctkDICOMAbstractThumbnailGenerator.h index dd3e6d30e3..ae27a6e2ef 100644 --- a/Libs/DICOM/Core/ctkDICOMAbstractThumbnailGenerator.h +++ b/Libs/DICOM/Core/ctkDICOMAbstractThumbnailGenerator.h @@ -24,7 +24,7 @@ // Qt includes #include -#include +#include #include "ctkDICOMCoreExport.h" @@ -45,9 +45,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMAbstractThumbnailGenerator : public QObject virtual ~ctkDICOMAbstractThumbnailGenerator(); virtual bool generateThumbnail(DicomImage* dcmImage, const QString& path, - QVector color = QVector{169, 169, 169}) = 0; + QColor backgroundColor = Qt::darkGray) = 0; virtual void generateDocumentThumbnail(const QString &thumbnailPath, - QVector color = QVector{169, 169, 169}) = 0; + QColor backgroundColor = Qt::darkGray) = 0; protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.cpp b/Libs/DICOM/Core/ctkDICOMDatabase.cpp index 725a69b066..f4c3f159f5 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.cpp +++ b/Libs/DICOM/Core/ctkDICOMDatabase.cpp @@ -2374,7 +2374,7 @@ bool ctkDICOMDatabase::storeThumbnailFile(const QString &originalFilePath, const QString &seriesInstanceUID, const QString &sopInstanceUID, const QString& modality, - QVector color) + QColor backgroundColor) { Q_D(ctkDICOMDatabase); if (!d->ThumbnailGenerator) @@ -2403,13 +2403,13 @@ bool ctkDICOMDatabase::storeThumbnailFile(const QString &originalFilePath, // NOTE: currently SEG objects are not fully supported by ctkDICOMThumbnailGenerator, // The rendering will fail and in addition SEG object can be very large and // loading the file can be slow. For now, we will just create a blank thumbnail with a document svg. - d->ThumbnailGenerator->generateDocumentThumbnail(thumbnailPath, color); + d->ThumbnailGenerator->generateDocumentThumbnail(thumbnailPath, backgroundColor); return true; } else { DicomImage dcmImage(QDir::toNativeSeparators(originalFilePath).toUtf8()); - return d->ThumbnailGenerator->generateThumbnail(&dcmImage, thumbnailPath, color); + return d->ThumbnailGenerator->generateThumbnail(&dcmImage, thumbnailPath, backgroundColor); } } diff --git a/Libs/DICOM/Core/ctkDICOMDatabase.h b/Libs/DICOM/Core/ctkDICOMDatabase.h index d074967f3d..b8abc7a88d 100644 --- a/Libs/DICOM/Core/ctkDICOMDatabase.h +++ b/Libs/DICOM/Core/ctkDICOMDatabase.h @@ -22,6 +22,7 @@ #define __ctkDICOMDatabase_h // Qt includes +#include #include #include #include @@ -216,7 +217,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject const QString& seriesInstanceUID, const QString& sopInstanceUID, const QString& modality = "", - QVector color = QVector{169, 169, 169}); + QColor backgroundColor = Qt::darkGray); Q_INVOKABLE int patientsCount(); Q_INVOKABLE int studiesCount(); diff --git a/Libs/DICOM/Core/ctkDICOMEchoJob.cpp b/Libs/DICOM/Core/ctkDICOMEchoJob.cpp index 584098889d..ec0d48282a 100644 --- a/Libs/DICOM/Core/ctkDICOMEchoJob.cpp +++ b/Libs/DICOM/Core/ctkDICOMEchoJob.cpp @@ -30,7 +30,7 @@ #include "ctkDICOMEchoWorker.h" #include "ctkDICOMServer.h" -static ctkLogger logger ( "org.commontk.dicom.DICOMRetrieveJob" ); +static ctkLogger logger ( "org.commontk.dicom.DICOMEchoJob" ); //------------------------------------------------------------------------------ // ctkDICOMEchoJobPrivate methods diff --git a/Libs/DICOM/Core/ctkDICOMIndexer_p.h b/Libs/DICOM/Core/ctkDICOMIndexer_p.h index 3357a046e6..7ffdb5050b 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: DICOMIndexingQueue RequestQueue; QThread WorkerThread; diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 47aa796aa9..433241d29a 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -119,7 +119,6 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob Q_SIGNALS: void progressJobDetail(QVariant); - void finishedJobDetail(QVariant); protected: QString PatientID; diff --git a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h index 17aae531fd..1aba0605be 100644 --- a/Libs/DICOM/Core/ctkDICOMJobResponseSet.h +++ b/Libs/DICOM/Core/ctkDICOMJobResponseSet.h @@ -89,7 +89,8 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJobResponseSet : public QObject RetrieveSOPInstance, StoreSOPInstance, Inserter, - Echo + Echo, + ThumbnailGenerator, }; void setJobType(JobType jobType); JobType jobType() const; diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.cpp b/Libs/DICOM/Core/ctkDICOMScheduler.cpp index dc7edade1c..440af83d9a 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.cpp +++ b/Libs/DICOM/Core/ctkDICOMScheduler.cpp @@ -28,6 +28,7 @@ // ctkDICOMCore includes #include "ctkDICOMEchoJob.h" +#include "ctkDICOMThumbnailGeneratorJob.h" #include "ctkDICOMInserterJob.h" #include "ctkDICOMJobResponseSet.h" #include "ctkDICOMQueryJob.h" @@ -246,6 +247,8 @@ void ctkDICOMSchedulerPrivate::handleJobByType(const ctkDICOMJobDetail& jd, break; case ctkDICOMJobResponseSet::JobType::Echo: break; + case ctkDICOMJobResponseSet::JobType::ThumbnailGenerator: + break; case ctkDICOMJobResponseSet::JobType::QueryPatients: jd.JobUID.isEmpty() ? q->queryPatients() : q->queryPatients(QThread::NormalPriority, jd.ConnectionName, jd.JobUID); break; @@ -276,6 +279,44 @@ void ctkDICOMSchedulerPrivate::handleJobByType(const ctkDICOMJobDetail& jd, } } +//------------------------------------------------------------------------------ +bool ctkDICOMSchedulerPrivate::isJobDuplicate(ctkDICOMJob *referenceJob) +{ + bool duplicate = false; + { + // The QMutexLocker is enclosed within brackets to restrict its scope and + // prevent conflicts with other QMutexLockers within the scheduler's methods. + QMutexLocker locker(&this->QueueMutex); + foreach (QSharedPointer job, this->JobsQueue) + { + if (!job) + { + continue; + } + + ctkDICOMJob* dicomJob = qobject_cast(job.data()); + if (!dicomJob) + { + logger.debug("ctkDICOMScheduler::getJobsByDICOMUIDs: unexpected type of job."); + continue; + } + + if (dicomJob->className() == referenceJob->className() && + dicomJob->patientID() == referenceJob->patientID() && + dicomJob->studyInstanceUID() == referenceJob->studyInstanceUID() && + dicomJob->seriesInstanceUID() == referenceJob->seriesInstanceUID() && + dicomJob->sopInstanceUID() == referenceJob->sopInstanceUID() && + dicomJob->dicomLevel() == referenceJob->dicomLevel()) + { + duplicate = true; + break; + } + } + } + + return duplicate; +} + //------------------------------------------------------------------------------ // ctkDICOMScheduler methods @@ -510,6 +551,7 @@ void ctkDICOMScheduler::retrieveSeries(const QString& patientID, { continue; } + qDebug() << allowedSeversForPatient; if (!d->isServerAllowed(server.data(), allowedSeversForPatient)) { continue; @@ -635,6 +677,37 @@ void ctkDICOMScheduler::echo(ctkDICOMServer &server, QThread::Priority priority) d->insertJob(job); } +//---------------------------------------------------------------------------- +void ctkDICOMScheduler::generateThumbnail(const QString &originalFilePath, + const QString &patientID, + const QString &studyInstanceUID, + const QString &seriesInstanceUID, + const QString &sopInstanceUID, + const QString &modality, + QColor backgroundColor, + QThread::Priority priority) +{ + Q_D(ctkDICOMScheduler); + + QSharedPointer job = + QSharedPointer(new ctkDICOMThumbnailGeneratorJob); + job->setDatabaseFilename(d->DicomDatabase->databaseFilename()); + job->setDicomFilePath(originalFilePath); + job->setModality(modality); + job->setBackgroundColor(backgroundColor); + job->setPatientID(patientID); + job->setStudyInstanceUID(studyInstanceUID); + job->setSeriesInstanceUID(seriesInstanceUID); + job->setSOPInstanceUID(sopInstanceUID); + job->setMaximumNumberOfRetry(0); + job->setPriority(priority); + + if (!d->isJobDuplicate(job.data())) + { + d->insertJob(job); + } +} + //---------------------------------------------------------------------------- QString ctkDICOMScheduler::insertJobResponseSet(const QSharedPointer& jobResponseSet, QThread::Priority priority) @@ -1096,15 +1169,15 @@ void ctkDICOMScheduler::runJob(const ctkDICOMJobDetail& jd, const QStringList& a if (jd.JobClass == "ctkDICOMQueryJob") { - d->handleQueryJob(jd, allowedSeversForPatient); + d->handleQueryJob(jd, allowedSevers); } else if (jd.JobClass == "ctkDICOMRetrieveJob") { - d->handleRetrieveJob(jd, allowedSeversForPatient); + d->handleRetrieveJob(jd, allowedSevers); } else { - d->handleJobByType(jd, allowedSeversForPatient); + d->handleJobByType(jd, allowedSevers); } } diff --git a/Libs/DICOM/Core/ctkDICOMScheduler.h b/Libs/DICOM/Core/ctkDICOMScheduler.h index 65256ea8a2..059fd55cc1 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler.h @@ -127,6 +127,16 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMScheduler : public ctkJobScheduler Q_INVOKABLE void echo(ctkDICOMServer& server, QThread::Priority priority = QThread::LowPriority); + /// Generate thumbnail and save it as png on local disk + Q_INVOKABLE void generateThumbnail(const QString &originalFilePath, + const QString &patientID, + const QString &studyInstanceUID, + const QString &seriesInstanceUID, + const QString &sopInstanceUID, + const QString& modality, + QColor backgroundColor, + QThread::Priority priority = QThread::LowPriority); + ///@{ /// Insert results from a job QString insertJobResponseSet(const QSharedPointer& jobResponseSet, diff --git a/Libs/DICOM/Core/ctkDICOMScheduler_p.h b/Libs/DICOM/Core/ctkDICOMScheduler_p.h index 8829b761d7..3799501a6c 100644 --- a/Libs/DICOM/Core/ctkDICOMScheduler_p.h +++ b/Libs/DICOM/Core/ctkDICOMScheduler_p.h @@ -69,6 +69,8 @@ class ctkDICOMSchedulerPrivate : public ctkJobSchedulerPrivate void handleJobByType(const ctkDICOMJobDetail& jd, const QStringList& allowedServers = QStringList()); + bool isJobDuplicate(ctkDICOMJob* job); + QSharedPointer DicomDatabase; QList> Servers; QMap Filters; diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp b/Libs/DICOM/Core/ctkDICOMThumbnailGenerator.cpp similarity index 94% rename from Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp rename to Libs/DICOM/Core/ctkDICOMThumbnailGenerator.cpp index c57819d6c7..03cfd924c8 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGenerator.cpp @@ -191,7 +191,8 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, QImage& } //------------------------------------------------------------------------------ -bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const QString &thumbnailPath, QVector color) +bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const QString &thumbnailPath, + QColor backgroundColor) { QImage image; if (this->generateThumbnail(dcmImage, image)) @@ -199,7 +200,7 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const Q return image.save(thumbnailPath, "PNG"); } - this->generateDocumentThumbnail(thumbnailPath, color); + this->generateDocumentThumbnail(thumbnailPath, backgroundColor); return false; } @@ -218,21 +219,22 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(const QString& dcmImagePath, } //------------------------------------------------------------------------------ -void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image, QColor color) +void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image, QColor backgroundColor) { Q_D(ctkDICOMThumbnailGenerator); if (image.width() != d->Width || image.height() != d->Height) { image = QImage(d->Width, d->Height, QImage::Format_RGB32); } - image.fill(color); + image.fill(backgroundColor); } //------------------------------------------------------------------------------ -void ctkDICOMThumbnailGenerator::generateDocumentThumbnail(const QString &thumbnailPath, QVector color) +void ctkDICOMThumbnailGenerator::generateDocumentThumbnail(const QString &thumbnailPath, + QColor backgroundColor) { QImage image; - this->generateBlankThumbnail(image, QColor(color[0], color[1], color[2])); + this->generateBlankThumbnail(image, backgroundColor); QPixmap pixmap = QPixmap::fromImage(image); QPainter painter; if (painter.begin(&pixmap)) diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h b/Libs/DICOM/Core/ctkDICOMThumbnailGenerator.h similarity index 88% rename from Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h rename to Libs/DICOM/Core/ctkDICOMThumbnailGenerator.h index 6881972bfb..b00f99b400 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGenerator.h @@ -28,16 +28,16 @@ class QImage; // ctkDICOMWidgets includes #include "ctkDICOMAbstractThumbnailGenerator.h" -#include "ctkDICOMWidgetsExport.h" +#include "ctkDICOMCoreExport.h" class ctkDICOMThumbnailGeneratorPrivate; // DCMTK includes class DicomImage; -/// \ingroup DICOM_Widgets +/// \ingroup DICOM_Core /// /// \brief Thumbnail generator class -class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstractThumbnailGenerator +class CTK_DICOM_CORE_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstractThumbnailGenerator { Q_OBJECT Q_PROPERTY(int width READ width WRITE setWidth) @@ -50,7 +50,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstr virtual ~ctkDICOMThumbnailGenerator(); virtual bool generateThumbnail(DicomImage* dcmImage, const QString& thumbnailPath, - QVector color = QVector{169, 169, 169}); + QColor backgroundColor = Qt::darkGray); Q_INVOKABLE bool generateThumbnail(DicomImage *dcmImage, QImage& image); Q_INVOKABLE bool generateThumbnail(const QString& dcmImagePath, QImage& image); @@ -58,9 +58,9 @@ 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, QColor color = Qt::darkGray); + Q_INVOKABLE void generateBlankThumbnail(QImage& image, QColor backgroundColor = Qt::darkGray); Q_INVOKABLE virtual void generateDocumentThumbnail(const QString &thumbnailPath, - QVector color = QVector{169, 169, 169}); + QColor backgroundColor = Qt::darkGray); /// Set thumbnail width void setWidth(int width); diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.cpp b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.cpp new file mode 100644 index 0000000000..269db0fa2d --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.cpp @@ -0,0 +1,129 @@ +/*========================================================================= + + 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). + +=========================================================================*/ + +// ctkCore includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMJobResponseSet.h" // For ctkDICOMJobDetail +#include "ctkDICOMThumbnailGeneratorJob_p.h" +#include "ctkDICOMThumbnailGeneratorWorker.h" + +static ctkLogger logger ( "org.commontk.dicom.DICOMThumbnailGeneratorJob" ); + +//------------------------------------------------------------------------------ +// ctkDICOMThumbnailGeneratorJobPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorJobPrivate::ctkDICOMThumbnailGeneratorJobPrivate(ctkDICOMThumbnailGeneratorJob* object) + : q_ptr(object) +{ + this->DatabaseFilename = ""; + this->DicomFilePath = ""; + this->Modality = ""; + this->BackgroundColor = Qt::darkGray; +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorJobPrivate::~ctkDICOMThumbnailGeneratorJobPrivate() +{ +} + +//------------------------------------------------------------------------------ +CTK_GET_CPP(ctkDICOMThumbnailGeneratorJob, QString, databaseFilename, DatabaseFilename); +CTK_SET_CPP(ctkDICOMThumbnailGeneratorJob, QString, setDatabaseFilename, DatabaseFilename); +CTK_GET_CPP(ctkDICOMThumbnailGeneratorJob, QString, dicomFilePath, DicomFilePath); +CTK_SET_CPP(ctkDICOMThumbnailGeneratorJob, QString, setDicomFilePath, DicomFilePath); +CTK_GET_CPP(ctkDICOMThumbnailGeneratorJob, QString, modality, Modality); +CTK_SET_CPP(ctkDICOMThumbnailGeneratorJob, QString, setModality, Modality); +CTK_GET_CPP(ctkDICOMThumbnailGeneratorJob, QColor, backgroundColor, BackgroundColor); +CTK_SET_CPP(ctkDICOMThumbnailGeneratorJob, QColor, setBackgroundColor, BackgroundColor); + +//------------------------------------------------------------------------------ +// ctkDICOMThumbnailGeneratorJob methods + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorJob::ctkDICOMThumbnailGeneratorJob() + : d_ptr(new ctkDICOMThumbnailGeneratorJobPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorJob::ctkDICOMThumbnailGeneratorJob(ctkDICOMThumbnailGeneratorJobPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorJob::~ctkDICOMThumbnailGeneratorJob() = default; + +//---------------------------------------------------------------------------- +QString ctkDICOMThumbnailGeneratorJob::loggerReport(const QString& status) +{ + QString fullLogMsg = QString("ctkDICOMThumbnailGeneratorJob: thumbnail generator job %1.\n") + .arg(status); + QString logMsg = QString("Thumbnail generator job %1.\n") + .arg(status); + QString currentDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz"); + QString logHeader = currentDateTime + " INFO: "; + this->LoggedText += logHeader; + this->LoggedText += logMsg; + return fullLogMsg; +} +//------------------------------------------------------------------------------ +ctkAbstractJob* ctkDICOMThumbnailGeneratorJob::clone() const +{ + ctkDICOMThumbnailGeneratorJob* newThumbnailGeneratorJob = new ctkDICOMThumbnailGeneratorJob; + newThumbnailGeneratorJob->setMaximumNumberOfRetry(this->maximumNumberOfRetry()); + newThumbnailGeneratorJob->setRetryDelay(this->retryDelay()); + newThumbnailGeneratorJob->setRetryCounter(this->retryCounter()); + newThumbnailGeneratorJob->setIsPersistent(this->isPersistent()); + newThumbnailGeneratorJob->setMaximumConcurrentJobsPerType(this->maximumConcurrentJobsPerType()); + newThumbnailGeneratorJob->setPriority(this->priority()); + newThumbnailGeneratorJob->setBackgroundColor(this->backgroundColor()); + newThumbnailGeneratorJob->setModality(this->modality()); + newThumbnailGeneratorJob->setDicomFilePath(this->dicomFilePath()); + + return newThumbnailGeneratorJob; +} + +//------------------------------------------------------------------------------ +ctkAbstractWorker* ctkDICOMThumbnailGeneratorJob::createWorker() +{ + ctkDICOMThumbnailGeneratorWorker* worker = + new ctkDICOMThumbnailGeneratorWorker; + worker->setJob(*this); + return worker; +} + +//------------------------------------------------------------------------------ +QVariant ctkDICOMThumbnailGeneratorJob::toVariant() +{ + return QVariant::fromValue(ctkDICOMJobDetail(*this)); +} + +//------------------------------------------------------------------------------ +ctkDICOMJobResponseSet::JobType ctkDICOMThumbnailGeneratorJob::getJobType() const +{ + return ctkDICOMJobResponseSet::JobType::ThumbnailGenerator; +} diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.h b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.h new file mode 100644 index 0000000000..3636cf800f --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob.h @@ -0,0 +1,112 @@ +/*========================================================================= + + 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 __ctkDICOMThumbnailGeneratorJob_h +#define __ctkDICOMThumbnailGeneratorJob_h + +// Qt includes +#include +#include +#include + +// ctkCore includes +class ctkAbstractWorker; + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkDICOMJob.h" +class ctkDICOMThumbnailGeneratorJobPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMThumbnailGeneratorJob : public ctkDICOMJob +{ + Q_OBJECT + Q_PROPERTY(QString databaseFilename READ databaseFilename WRITE setDatabaseFilename); + Q_PROPERTY(QString dicomFilePath READ dicomFilePath WRITE setDicomFilePath); + Q_PROPERTY(QString modality READ modality WRITE setModality); + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor); + +public: + typedef ctkDICOMJob Superclass; + explicit ctkDICOMThumbnailGeneratorJob(); + virtual ~ctkDICOMThumbnailGeneratorJob(); + + ///@{ + /// Database Filename + void setDatabaseFilename(QString databaseFilename); + QString databaseFilename() const; + ///}@ + + ///@{ + /// Dicom file path + void setDicomFilePath(QString dicomFilePath); + QString dicomFilePath() const; + ///@} + + ///@{ + /// Modality + void setModality(QString modality); + QString modality() const; + ///@} + + ///@{ + /// Background Color + void setBackgroundColor(QColor backgroundColor); + QColor backgroundColor() const; + ///@} + + /// Logger report string formatting for specific task + Q_INVOKABLE QString loggerReport(const QString& status) override; + + /// \see ctkAbstractJob::clone() + Q_INVOKABLE ctkAbstractJob* clone() const override; + + /// Generate worker for job + Q_INVOKABLE ctkAbstractWorker* 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; + + /// Return job type. + Q_INVOKABLE virtual ctkDICOMJobResponseSet::JobType getJobType() const override; + +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. + ctkDICOMThumbnailGeneratorJob(ctkDICOMThumbnailGeneratorJobPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMThumbnailGeneratorJob); + Q_DISABLE_COPY(ctkDICOMThumbnailGeneratorJob); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob_p.h b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob_p.h new file mode 100644 index 0000000000..e07bb78bb3 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorJob_p.h @@ -0,0 +1,54 @@ +/*========================================================================= + + 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 __ctkDICOMThumbnailGeneratorJobPrivate_h +#define __ctkDICOMThumbnailGeneratorJobPrivate_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMThumbnailGeneratorJob.h" + +//------------------------------------------------------------------------------ +class ctkDICOMThumbnailGeneratorJobPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMThumbnailGeneratorJob) + +protected: + ctkDICOMThumbnailGeneratorJob* const q_ptr; + +public: + ctkDICOMThumbnailGeneratorJobPrivate(ctkDICOMThumbnailGeneratorJob* object); + virtual ~ctkDICOMThumbnailGeneratorJobPrivate(); + +public: + QString DatabaseFilename; + QString DicomFilePath; + QString Modality; + QColor BackgroundColor; +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.cpp b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.cpp new file mode 100644 index 0000000000..e053937117 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.cpp @@ -0,0 +1,149 @@ +/*========================================================================= + + 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). + +=========================================================================*/ + +// ctkCore includes +#include + +// ctkDICOMCore includes +#include "ctkDICOMThumbnailGenerator.h" +#include "ctkDICOMThumbnailGeneratorWorker_p.h" +#include "ctkDICOMThumbnailGeneratorJob.h" +#include "ctkDICOMScheduler.h" + +static ctkLogger logger ("org.commontk.dicom.DICOMRetrieveWorker"); + +//------------------------------------------------------------------------------ +// ctkDICOMThumbnailGeneratorWorkerPrivate methods + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorWorkerPrivate::ctkDICOMThumbnailGeneratorWorkerPrivate(ctkDICOMThumbnailGeneratorWorker* object) + : q_ptr(object) +{ + this->wasCancelled = false; +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorWorkerPrivate::~ctkDICOMThumbnailGeneratorWorkerPrivate() = default; + +//------------------------------------------------------------------------------ +// ctkDICOMThumbnailGeneratorWorker methods + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorWorker::ctkDICOMThumbnailGeneratorWorker() + : d_ptr(new ctkDICOMThumbnailGeneratorWorkerPrivate(this)) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorWorker::ctkDICOMThumbnailGeneratorWorker(ctkDICOMThumbnailGeneratorWorkerPrivate* pimpl) + : d_ptr(pimpl) +{ +} + +//------------------------------------------------------------------------------ +ctkDICOMThumbnailGeneratorWorker::~ctkDICOMThumbnailGeneratorWorker() = default; + +//---------------------------------------------------------------------------- +void ctkDICOMThumbnailGeneratorWorker::requestCancel() +{ + Q_D(ctkDICOMThumbnailGeneratorWorker); + d->wasCancelled = true; +} + +//---------------------------------------------------------------------------- +void ctkDICOMThumbnailGeneratorWorker::run() +{ + Q_D(const ctkDICOMThumbnailGeneratorWorker); + QSharedPointer thumbnailGeneratorJob = + qSharedPointerObjectCast(this->Job); + if (!thumbnailGeneratorJob) + { + return; + } + + QSharedPointer scheduler = + qSharedPointerObjectCast(this->Scheduler); + if (!scheduler) + { + this->onJobCanceled(d->wasCancelled); + return; + } + + if (d->wasCancelled) + { + this->onJobCanceled(d->wasCancelled); + return; + } + + thumbnailGeneratorJob->setStatus(ctkAbstractJob::JobStatus::Running); + + logger.debug(QString("ctkDICOMThumbnailGeneratorWorker : running job %1 in thread %2.\n") + .arg(thumbnailGeneratorJob->jobUID()) + .arg(QString::number(reinterpret_cast(QThread::currentThreadId())), 16)); + + ctkDICOMDatabase database; + QString dbConnectionName = + "db_" + QString::number(reinterpret_cast(QThread::currentThreadId()), 16); + database.openDatabase(thumbnailGeneratorJob->databaseFilename(), dbConnectionName); + QSharedPointer thumbnailGenerator = + QSharedPointer(new ctkDICOMThumbnailGenerator); + database.setThumbnailGenerator(thumbnailGenerator.data()); + database.storeThumbnailFile(thumbnailGeneratorJob->dicomFilePath(), + thumbnailGeneratorJob->studyInstanceUID(), + thumbnailGeneratorJob->seriesInstanceUID(), + thumbnailGeneratorJob->sopInstanceUID(), + thumbnailGeneratorJob->modality(), + thumbnailGeneratorJob->backgroundColor()); + database.closeDatabase(); + + if (d->wasCancelled) + { + this->onJobCanceled(d->wasCancelled); + return; + } + QSharedPointer jobResponseSet = + QSharedPointer(new ctkDICOMJobResponseSet); + + jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::ThumbnailGenerator); + jobResponseSet->setPatientID(thumbnailGeneratorJob->patientID()); + jobResponseSet->setStudyInstanceUID(thumbnailGeneratorJob->studyInstanceUID()); + jobResponseSet->setSeriesInstanceUID(thumbnailGeneratorJob->seriesInstanceUID()); + jobResponseSet->setSOPInstanceUID(thumbnailGeneratorJob->sopInstanceUID()); + jobResponseSet->setJobUID(thumbnailGeneratorJob->jobUID()); + + thumbnailGeneratorJob->progressJobDetail(jobResponseSet->toVariant()); + thumbnailGeneratorJob->setStatus(ctkAbstractJob::JobStatus::Finished); +} + +//---------------------------------------------------------------------------- +void ctkDICOMThumbnailGeneratorWorker::setJob(QSharedPointer job) +{ + QSharedPointer ThumbnailGeneratorJob = + qSharedPointerObjectCast(job); + if (!ThumbnailGeneratorJob) + { + return; + } + + this->Superclass::setJob(job); +} diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.h b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.h new file mode 100644 index 0000000000..4b14314998 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker.h @@ -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). + +=========================================================================*/ + +#ifndef __ctkDICOMThumbnailGeneratorWorker_h +#define __ctkDICOMThumbnailGeneratorWorker_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMCoreExport.h" +#include "ctkAbstractWorker.h" +class ctkDICOMThumbnailGenerator; +class ctkDICOMThumbnailGeneratorWorkerPrivate; + +/// \ingroup DICOM_Core +class CTK_DICOM_CORE_EXPORT ctkDICOMThumbnailGeneratorWorker : public ctkAbstractWorker +{ + Q_OBJECT + +public: + typedef ctkAbstractWorker Superclass; + explicit ctkDICOMThumbnailGeneratorWorker(); + virtual ~ctkDICOMThumbnailGeneratorWorker(); + + /// Execute worker. This method is run by the QThreadPool and is thread safe + void run() override; + + /// Cancel worker. This method is thread safe + void requestCancel() override; + + ///@{ + /// Job. + /// These methods are not thread safe + void setJob(QSharedPointer job) override; + using ctkAbstractWorker::setJob; + ///@} + +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. + ctkDICOMThumbnailGeneratorWorker(ctkDICOMThumbnailGeneratorWorkerPrivate* pimpl); + +private: + Q_DECLARE_PRIVATE(ctkDICOMThumbnailGeneratorWorker); + Q_DISABLE_COPY(ctkDICOMThumbnailGeneratorWorker); +}; + +#endif diff --git a/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker_p.h b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker_p.h new file mode 100644 index 0000000000..601e4636c3 --- /dev/null +++ b/Libs/DICOM/Core/ctkDICOMThumbnailGeneratorWorker_p.h @@ -0,0 +1,51 @@ +/*========================================================================= + + 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 __ctkDICOMThumbnailGeneratorWorkerPrivate_h +#define __ctkDICOMThumbnailGeneratorWorkerPrivate_h + +// Qt includes +#include +#include + +// ctkDICOMCore includes +#include "ctkDICOMThumbnailGeneratorWorker.h" + +//------------------------------------------------------------------------------ +class ctkDICOMThumbnailGeneratorWorkerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ctkDICOMThumbnailGeneratorWorker) + +protected: + ctkDICOMThumbnailGeneratorWorker* const q_ptr; + +public: + ctkDICOMThumbnailGeneratorWorkerPrivate(ctkDICOMThumbnailGeneratorWorker* object); + virtual ~ctkDICOMThumbnailGeneratorWorkerPrivate(); + +public: + bool wasCancelled; +}; + +#endif diff --git a/Libs/DICOM/Widgets/CMakeLists.txt b/Libs/DICOM/Widgets/CMakeLists.txt index df50cfd71f..d5604cb055 100644 --- a/Libs/DICOM/Widgets/CMakeLists.txt +++ b/Libs/DICOM/Widgets/CMakeLists.txt @@ -48,8 +48,6 @@ set(KIT_SRCS ctkDICOMTableManager.cpp ctkDICOMTableView.cpp ctkDICOMTableView.h - ctkDICOMThumbnailGenerator.cpp - ctkDICOMThumbnailGenerator.h ctkDICOMThumbnailListWidget.cpp ctkDICOMThumbnailListWidget.h ctkDICOMVisualBrowserWidget.cpp @@ -76,7 +74,6 @@ set(KIT_MOC_SRCS ctkDICOMStudyItemWidget.h ctkDICOMTableManager.h ctkDICOMTableView.h - ctkDICOMThumbnailGenerator.h ctkDICOMThumbnailListWidget.h ctkDICOMVisualBrowserWidget.h ) diff --git a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp index 0407ae4c22..673655aa20 100644 --- a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp @@ -187,6 +187,10 @@ QString QCenteredItemModel::getJobTypeAsString(QString jobClass, ctkDICOMJob::DI { return ctkDICOMJobListWidget::tr("Inserter"); } + else if (jobClass == "ctkDICOMThumbnailGeneratorJob") + { + return ctkDICOMJobListWidget::tr("Thumbnail generator"); + } return QString(); } @@ -503,7 +507,7 @@ QCenteredItemModel::Columns QCenteredItemModel::getColumnIndexFromString(QString { return Columns::SeriesInstanceUID; } - else if (columnString == ctkDICOMJobListWidget::tr("SOP UID")) + else if (columnString == ctkDICOMJobListWidget::tr("SOPInstance UID")) { return Columns::SOPInstanceUID; } diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 113b1d10dd..350e1c7b48 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -75,6 +75,7 @@ class ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget void createThumbnail(ctkDICOMJobDetail td); QImage drawModalityThumbnail(); void drawThumbnail(const QString& dicomFilePath, + const QString& patientID, const QString& studyInstanceUID, const QString& seriesInstanceUID, const QString& sopInstanceUID, @@ -418,11 +419,13 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkDICOMJobDetail td) if (jobSopInstanceUID.isEmpty() || ((jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || - jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance) && + jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance || + jobType == ctkDICOMJobResponseSet::JobType::ThumbnailGenerator) && jobSopInstanceUID == this->CentralFrameSOPInstanceUID) || renderThumbnail) { this->drawThumbnail(this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID), + this->PatientID, this->StudyInstanceUID, this->SeriesInstanceUID, this->CentralFrameSOPInstanceUID, @@ -474,6 +477,7 @@ QImage ctkDICOMSeriesItemWidgetPrivate::drawModalityThumbnail() //---------------------------------------------------------------------------- void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString& dicomFilePath, + const QString& patientID, const QString& studyInstanceUID, const QString& seriesInstanceUID, const QString& sopInstanceUID, @@ -494,6 +498,12 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString& dicomFilePath return; } + if (patientID.isEmpty()) + { + logger.debug("drawThumbnail failed, patientID is empty. \n"); + return; + } + if (studyInstanceUID.isEmpty()) { logger.debug("drawThumbnail failed, studyInstanceUID is empty. \n"); @@ -519,7 +529,6 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString& dicomFilePath } qreal scalingFactor = q->devicePixelRatioF(); - int scaledThumbnailSizePixel = this->ThumbnailSizePixel * scalingFactor; int margin = floor(this->ThumbnailSizePixel / 50.); int iconSize = floor(this->ThumbnailSizePixel / 6.); int textSize = floor(this->ThumbnailSizePixel / 12.); @@ -529,18 +538,14 @@ void ctkDICOMSeriesItemWidgetPrivate::drawThumbnail(const QString& dicomFilePath if (this->ThumbnailImage.isNull()) { - ctkDICOMThumbnailGenerator thumbnailGenerator; - thumbnailGenerator.setWidth(scaledThumbnailSizePixel); - thumbnailGenerator.setHeight(scaledThumbnailSizePixel); - QString thumbnailPath = this->DicomDatabase->thumbnailPathForInstance(studyInstanceUID, seriesInstanceUID, sopInstanceUID); if (thumbnailPath.isEmpty()) { QColor backgroundColor = this->SeriesThumbnail->palette().color(QPalette::Normal, QPalette::Window); - QVector colorVector = {backgroundColor.red(), backgroundColor.green(), backgroundColor.blue()}; - this->DicomDatabase->storeThumbnailFile(dicomFilePath, studyInstanceUID, seriesInstanceUID, - sopInstanceUID, modality, colorVector); - thumbnailPath = this->DicomDatabase->thumbnailPathForInstance(studyInstanceUID, seriesInstanceUID, sopInstanceUID); + this->Scheduler->generateThumbnail(dicomFilePath, patientID, studyInstanceUID, seriesInstanceUID, + sopInstanceUID, modality, backgroundColor, + this->RaiseJobsPriority ? QThread::HighPriority : QThread::LowPriority); + return; } if (!this->ThumbnailImage.load(thumbnailPath)) @@ -703,6 +708,7 @@ void ctkDICOMSeriesItemWidgetPrivate::updateThumbnailProgressBar() // change icons QString file = this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID); this->drawThumbnail(file, + this->PatientID, this->StudyInstanceUID, this->SeriesInstanceUID, this->CentralFrameSOPInstanceUID, @@ -742,6 +748,7 @@ void ctkDICOMSeriesItemWidgetPrivate::updateRetrieveUIOnFailed() int numberOfFrames = instancesList.count(); this->drawThumbnail(file, + this->PatientID, this->StudyInstanceUID, this->SeriesInstanceUID, this->CentralFrameSOPInstanceUID, @@ -759,7 +766,7 @@ void ctkDICOMSeriesItemWidgetPrivate::updateRetrieveUIOnFinished() filesList.removeAll(QString("")); int numberOfFiles = filesList.count(); - if (numberOfFrames > 0 && numberOfFiles < numberOfFrames) + if (numberOfFrames > 1 && numberOfFiles < numberOfFrames) { this->RetrieveFailed = true; this->IsCloud = false; @@ -772,6 +779,7 @@ void ctkDICOMSeriesItemWidgetPrivate::updateRetrieveUIOnFinished() } this->drawThumbnail(this->DicomDatabase->fileForInstance(this->CentralFrameSOPInstanceUID), + this->PatientID, this->StudyInstanceUID, this->SeriesInstanceUID, this->CentralFrameSOPInstanceUID, @@ -961,7 +969,8 @@ void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(const QVariant& data) if ((td.JobType != ctkDICOMJobResponseSet::JobType::QueryInstances && td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance && - td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance ) || + td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance && + td.JobType != ctkDICOMJobResponseSet::JobType::ThumbnailGenerator) || td.SeriesInstanceUID != d->SeriesInstanceUID) { return;