diff --git a/Libs/Core/ctkJobScheduler.cpp b/Libs/Core/ctkJobScheduler.cpp index 9080fee7ee..e67311b85d 100644 --- a/Libs/Core/ctkJobScheduler.cpp +++ b/Libs/Core/ctkJobScheduler.cpp @@ -53,11 +53,17 @@ ctkJobSchedulerPrivate::~ctkJobSchedulerPrivate() = default; //--------------------------------------------------------------------------- void ctkJobSchedulerPrivate::init() { + Q_Q(ctkJobScheduler); QObject::connect(this, SIGNAL(queueJobsInThreadPool()), this, SLOT(onQueueJobsInThreadPool())); - this->ThreadPool = QSharedPointer(new QThreadPool()); + this->ThreadPool = QSharedPointer(new QThreadPool(this)); this->ThreadPool->setMaxThreadCount(20); + this->ThrottleTimer = QSharedPointer(new QTimer(this)); + this->ThrottleTimer->setSingleShot(true); + + QObject::connect(this->ThrottleTimer.data(), SIGNAL(timeout()), + q, SLOT(emitThrottledSignals())); } //------------------------------------------------------------------------------ @@ -144,15 +150,19 @@ bool ctkJobSchedulerPrivate::insertJob(QSharedPointer job) QMetaObject::Connection failedConnection = QObject::connect(job.data(), &ctkAbstractJob::failed, q, [q, job](){ q->onJobFailed(job.data()); }); - QMap connections = { + QMetaObject::Connection progressConnection = + QObject::connect(job.data(), SIGNAL(progressJobDetail(QVariant)), + q, SLOT(onProgressJobDetail(QVariant))); + + QMap connections = + { {"started", startedConnection}, {"userStopped", userStoppedConnection}, {"finished", finishedConnection}, {"attemptFailed", attemptFailedConnection}, - {"failed", failedConnection} + {"failed", failedConnection}, + {"progress", progressConnection}, }; - QObject::connect(job.data(), SIGNAL(progressJobDetail(QVariant)), - q, SIGNAL(progressJobDetail(QVariant))); { QMutexLocker locker(&this->QueueMutex); @@ -179,8 +189,6 @@ bool ctkJobSchedulerPrivate::insertJob(QSharedPointer job) //------------------------------------------------------------------------------ bool ctkJobSchedulerPrivate::removeJob(const QString& jobUID) { - Q_Q(ctkJobScheduler); - logger.debug(QString("ctkJobScheduler: deleting job object %1 in thread %2.\n") .arg(jobUID) .arg(QString::number(reinterpret_cast(QThread::currentThreadId()), 16))); @@ -199,8 +207,7 @@ bool ctkJobSchedulerPrivate::removeJob(const QString& jobUID) QObject::disconnect(connections.value("finished")); QObject::disconnect(connections.value("attemptFailed")); QObject::disconnect(connections.value("failed")); - QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), - q, SIGNAL(progressJobDetail(QVariant))); + QObject::disconnect(connections.value("progress")); this->JobsConnections.remove(jobUID); this->JobsQueue.remove(jobUID); @@ -237,24 +244,19 @@ void ctkJobSchedulerPrivate::removeJobs(const QStringList &jobUIDs) QObject::disconnect(connections.value("finished")); QObject::disconnect(connections.value("attemptFailed")); QObject::disconnect(connections.value("failed")); - QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), q, SIGNAL(progressJobDetail(QVariant))); + QObject::disconnect(connections.value("progress")); this->JobsConnections.remove(jobUID); this->JobsQueue.remove(jobUID); } } - foreach (QVariant data, datas) - { - emit q->jobUserStopped(data); - } + emit q->jobUserStopped(datas); } //------------------------------------------------------------------------------ void ctkJobSchedulerPrivate::removeAllJobs() { - Q_Q(ctkJobScheduler); - { // The QMutexLocker is enclosed within brackets to restrict its scope and // prevent conflicts with other QMutexLockers within the scheduler's methods. @@ -278,7 +280,7 @@ void ctkJobSchedulerPrivate::removeAllJobs() QObject::disconnect(connections.value("finished")); QObject::disconnect(connections.value("attemptFailed")); QObject::disconnect(connections.value("failed")); - QObject::disconnect(job.data(), SIGNAL(progressJobDetail(QVariant)), q, SIGNAL(progressJobDetail(QVariant))); + QObject::disconnect(connections.value("progress")); this->JobsConnections.remove(jobUID); this->JobsQueue.remove(jobUID); @@ -314,6 +316,17 @@ QString ctkJobSchedulerPrivate::generateUniqueJobUID() return QUuid::createUuid().toString(QUuid::StringFormat::WithoutBraces); } +//------------------------------------------------------------------------------ +void ctkJobSchedulerPrivate::clearBactchedJobsLists() +{ + this->BatchedJobsStarted.clear(); + this->BatchedJobsUserStopped.clear(); + this->BatchedJobsFinished.clear(); + this->BatchedJobsAttemptFailed.clear(); + this->BatchedJobsFailed.clear(); + this->BatchedJobsProgress.clear(); +} + //--------------------------------------------------------------------------- // ctkJobScheduler methods @@ -592,6 +605,8 @@ void ctkJobScheduler::stopAllJobs(bool stopPersistentJobs) worker->requestCancel(); } + + d->clearBactchedJobsLists(); } //---------------------------------------------------------------------------- @@ -722,18 +737,25 @@ QSharedPointer ctkJobScheduler::threadPoolShared() const //---------------------------------------------------------------------------- void ctkJobScheduler::onJobStarted(ctkAbstractJob* job) { + Q_D(ctkJobScheduler); if (!job) { return; } logger.debug(job->loggerReport(tr("started"))); - emit this->jobStarted(job->toVariant()); + + d->BatchedJobsStarted.append(job->toVariant()); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } } //---------------------------------------------------------------------------- void ctkJobScheduler::onJobUserStopped(ctkAbstractJob* job) { + Q_D(ctkJobScheduler); if (!job) { return; @@ -746,12 +768,17 @@ void ctkJobScheduler::onJobUserStopped(ctkAbstractJob* job) this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobUserStopped(data); + d->BatchedJobsUserStopped.append(job->toVariant()); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } } //---------------------------------------------------------------------------- void ctkJobScheduler::onJobFinished(ctkAbstractJob* job) { + Q_D(ctkJobScheduler); if (!job) { return; @@ -764,12 +791,17 @@ void ctkJobScheduler::onJobFinished(ctkAbstractJob* job) this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobFinished(data); + d->BatchedJobsFinished.append(job->toVariant()); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } } //---------------------------------------------------------------------------- void ctkJobScheduler::onJobAttemptFailed(ctkAbstractJob* job) { + Q_D(ctkJobScheduler); if (!job) { return; @@ -782,12 +814,17 @@ void ctkJobScheduler::onJobAttemptFailed(ctkAbstractJob* job) this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobAttemptFailed(data); + d->BatchedJobsAttemptFailed.append(job->toVariant()); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } } //---------------------------------------------------------------------------- void ctkJobScheduler::onJobFailed(ctkAbstractJob* job) { + Q_D(ctkJobScheduler); if (!job) { return; @@ -800,5 +837,36 @@ void ctkJobScheduler::onJobFailed(ctkAbstractJob* job) this->deleteWorker(jobUID); this->deleteJob(jobUID); - emit this->jobFailed(data); + d->BatchedJobsFailed.append(job->toVariant()); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } +} + +//---------------------------------------------------------------------------- +void ctkJobScheduler::onProgressJobDetail(QVariant data) +{ + Q_D(ctkJobScheduler); + + d->BatchedJobsProgress.append(data); + if (!d->ThrottleTimer->isActive()) + { + d->ThrottleTimer->start(d->ThrottleTimeInterval); + } +} + +//---------------------------------------------------------------------------- +void ctkJobScheduler::emitThrottledSignals() +{ + Q_D(ctkJobScheduler); + + emit this->jobStarted(d->BatchedJobsStarted); + emit this->jobUserStopped(d->BatchedJobsUserStopped); + emit this->jobFinished(d->BatchedJobsFinished); + emit this->jobAttemptFailed(d->BatchedJobsAttemptFailed); + emit this->jobFailed(d->BatchedJobsFailed); + emit this->progressJobDetail(d->BatchedJobsProgress); + + d->clearBactchedJobsLists(); } diff --git a/Libs/Core/ctkJobScheduler.h b/Libs/Core/ctkJobScheduler.h index 76241893d9..4ff945c054 100644 --- a/Libs/Core/ctkJobScheduler.h +++ b/Libs/Core/ctkJobScheduler.h @@ -106,21 +106,23 @@ class CTK_CORE_EXPORT ctkJobScheduler : public QObject QSharedPointer threadPoolShared() const; Q_SIGNALS: - void jobInitialized(QVariant data); - void jobQueued(QVariant data); - void jobStarted(QVariant data); - void jobUserStopped(QVariant data); - void jobFinished(QVariant data); - void jobAttemptFailed(QVariant data); - void jobFailed(QVariant data); - void progressJobDetail(QVariant data); + void jobInitialized(QVariant); + void jobQueued(QVariant); + void jobStarted(QList); + void jobUserStopped(QList); + void jobFinished(QList); + void jobAttemptFailed(QList); + void jobFailed(QList); + void progressJobDetail(QList); public Q_SLOTS: - virtual void onJobStarted(ctkAbstractJob* job); - virtual void onJobUserStopped(ctkAbstractJob* job); - virtual void onJobFinished(ctkAbstractJob* job); - virtual void onJobAttemptFailed(ctkAbstractJob* job); - virtual void onJobFailed(ctkAbstractJob* job); + virtual void onJobStarted(ctkAbstractJob*); + virtual void onJobUserStopped(ctkAbstractJob*); + virtual void onJobFinished(ctkAbstractJob*); + virtual void onJobAttemptFailed(ctkAbstractJob*); + virtual void onJobFailed(ctkAbstractJob*); + virtual void onProgressJobDetail(QVariant); + virtual void emitThrottledSignals(); protected: QScopedPointer d_ptr; diff --git a/Libs/Core/ctkJobScheduler_p.h b/Libs/Core/ctkJobScheduler_p.h index 53285cbcd7..2df195d66a 100644 --- a/Libs/Core/ctkJobScheduler_p.h +++ b/Libs/Core/ctkJobScheduler_p.h @@ -24,6 +24,7 @@ // Qt includes #include #include +#include class QThreadPool; // ctkCore includes @@ -62,6 +63,7 @@ public Q_SLOTS: virtual void removeAllJobs(); int getSameTypeJobsInThreadPoolQueueOrRunning(QSharedPointer job); QString generateUniqueJobUID(); + void clearBactchedJobsLists(); QMutex QueueMutex; @@ -73,6 +75,14 @@ public Q_SLOTS: QMap> JobsQueue; QMap> JobsConnections; QMap> Workers; + QList BatchedJobsStarted; + QList BatchedJobsUserStopped; + QList BatchedJobsFinished; + QList BatchedJobsAttemptFailed; + QList BatchedJobsFailed; + QList BatchedJobsProgress; + QSharedPointer ThrottleTimer; + int ThrottleTimeInterval{300}; }; #endif diff --git a/Libs/DICOM/Core/ctkDICOMJob.h b/Libs/DICOM/Core/ctkDICOMJob.h index 821a1ad9f8..47aa796aa9 100644 --- a/Libs/DICOM/Core/ctkDICOMJob.h +++ b/Libs/DICOM/Core/ctkDICOMJob.h @@ -41,6 +41,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMJob : public ctkAbstractJob { Q_OBJECT Q_ENUMS(DICOMLevel) + 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); diff --git a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp index 20cda68ef8..78898348c8 100644 --- a/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp +++ b/Libs/DICOM/Core/ctkDICOMRetrieveWorker.cpp @@ -250,6 +250,7 @@ void ctkDICOMRetrieveWorker::run() newJob->setRetryCounter(0); newJob->setServer(*proxyServer); scheduler->addJob(newJob); + retrieveJob->setReferenceInserterJobUID("Proxy"); } else if (d->Retrieve->jobResponseSetsShared().count() > 0 && server->retrieveProtocol() == ctkDICOMServer::RetrieveProtocol::CGET) diff --git a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp index e113273443..46b5481aa9 100644 --- a/Libs/DICOM/Core/ctkDICOMStorageListener.cpp +++ b/Libs/DICOM/Core/ctkDICOMStorageListener.cpp @@ -123,6 +123,8 @@ OFCondition ctkDICOMStorageListenerSCUPrivate::handleIncomingCommand(T_DIMSE_Mes reqDataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesUID); OFString studyUID; reqDataset->findAndGetOFString(DCM_StudyInstanceUID, studyUID); + OFString patientID; + reqDataset->findAndGetOFString(DCM_PatientID, patientID); emit this->listener->progress( ctkDICOMStorageListener::tr("Got STORE request for %1").arg(instanceUID.c_str())); emit this->listener->progress(0); @@ -131,6 +133,7 @@ OFCondition ctkDICOMStorageListenerSCUPrivate::handleIncomingCommand(T_DIMSE_Mes QSharedPointer jobResponseSet = QSharedPointer(new ctkDICOMJobResponseSet); jobResponseSet->setJobType(ctkDICOMJobResponseSet::JobType::StoreSOPInstance); + jobResponseSet->setPatientID(patientID.c_str()); jobResponseSet->setStudyInstanceUID(studyUID.c_str()); jobResponseSet->setSeriesInstanceUID(seriesUID.c_str()); jobResponseSet->setSOPInstanceUID(instanceUID.c_str()); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp index 159c2e5a35..5e87546d02 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMPatientItemWidgetTest1.cpp @@ -52,7 +52,7 @@ int ctkDICOMPatientItemWidgetTest1(int argc, char* argv[]) CHECK_QSTRING(widget.filteringStudyDescription(), ""); CHECK_QSTRING(widget.filteringSeriesDescription(), ""); CHECK_INT(widget.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); - CHECK_INT(widget.numberOfStudiesPerPatient(), 2); + CHECK_INT(widget.numberOfOpenedStudiesPerPatient(), 2); CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); // Test setting and getting @@ -66,8 +66,8 @@ int ctkDICOMPatientItemWidgetTest1(int argc, char* argv[]) 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.setNumberOfOpenedStudiesPerPatient(6); + CHECK_INT(widget.numberOfOpenedStudiesPerPatient(), 6); widget.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); CHECK_INT(widget.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); diff --git a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp index ad58c9c7d8..017256b0d9 100644 --- a/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp +++ b/Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMVisualBrowserWidgetTest1.cpp @@ -63,7 +63,7 @@ int ctkDICOMVisualBrowserWidgetTest1(int argc, char* argv[]) CHECK_QSTRING(browser.filteringSeriesDescription(), ""); CHECK_QSTRING(browser.filteringModalities().at(0), "Any"); CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::Any); - CHECK_INT(browser.numberOfStudiesPerPatient(), 2); + CHECK_INT(browser.numberOfOpenedStudiesPerPatient(), 2); CHECK_INT(browser.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium); CHECK_BOOL(browser.isSendActionVisible(), false); CHECK_BOOL(browser.isDeleteActionVisible(), true); @@ -145,8 +145,8 @@ int ctkDICOMVisualBrowserWidgetTest1(int argc, char* argv[]) CHECK_QSTRING(browser.filteringModalities().at(0), "CT"); browser.setFilteringDate(ctkDICOMPatientItemWidget::DateType::LastYear); CHECK_INT(browser.filteringDate(), ctkDICOMPatientItemWidget::DateType::LastYear); - browser.setNumberOfStudiesPerPatient(6); - CHECK_INT(browser.numberOfStudiesPerPatient(), 6); + browser.setNumberOfOpenedStudiesPerPatient(6); + CHECK_INT(browser.numberOfOpenedStudiesPerPatient(), 6); browser.setThumbnailSize(ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); CHECK_INT(browser.thumbnailSize(), ctkDICOMStudyItemWidget::ThumbnailSizeOption::Small); browser.setSendActionVisible(true); diff --git a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp index 6258077acc..66e97c9cd8 100644 --- a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.cpp @@ -720,18 +720,18 @@ void ctkDICOMJobListWidgetPrivate::disconnectScheduler() q, SLOT(onJobInitialized(QVariant))); ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobQueued(QVariant)), q, SLOT(onJobQueued(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobAttemptFailed(QVariant)), - q, SLOT(onJobAttemptFailed(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(onProgressJobDetail(QVariant))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobAttemptFailed(QList)), + q, SLOT(onJobAttemptFailed(QList))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + ctkDICOMJobListWidget::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(onProgressJobDetail(QList))); } //---------------------------------------------------------------------------- @@ -747,18 +747,18 @@ void ctkDICOMJobListWidgetPrivate::connectScheduler() q, SLOT(onJobInitialized(QVariant))); ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobQueued(QVariant)), q, SLOT(onJobQueued(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobAttemptFailed(QVariant)), - q, SLOT(onJobAttemptFailed(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(onProgressJobDetail(QVariant))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobAttemptFailed(QList)), + q, SLOT(onJobAttemptFailed(QList))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + ctkDICOMJobListWidget::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(onProgressJobDetail(QList))); } //---------------------------------------------------------------------------- @@ -1108,95 +1108,113 @@ void ctkDICOMJobListWidget::onJobQueued(QVariant data) ctkDICOMJobDetail td = data.value(); if(td.JobClass.isEmpty()) - { + { return; - } + } d->dataModel->updateJobStatus(td, QCenteredItemModel::Queued); } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onJobStarted(QVariant data) +void ctkDICOMJobListWidget::onJobStarted(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobClass.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + + if(td.JobClass.isEmpty()) + { + continue; + } - d->dataModel->updateJobStatus(td, QCenteredItemModel::Running); + d->dataModel->updateJobStatus(td, QCenteredItemModel::Running); + } } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onJobFinished(QVariant data) +void ctkDICOMJobListWidget::onJobFinished(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobClass.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + + if(td.JobClass.isEmpty()) + { + continue; + } - d->dataModel->updateJobStatus(td, QCenteredItemModel::Completed); + d->dataModel->updateJobStatus(td, QCenteredItemModel::Completed); + } } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onProgressJobDetail(QVariant data) +void ctkDICOMJobListWidget::onProgressJobDetail(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobType == ctkDICOMJobResponseSet::JobType::None) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + + if(td.JobType == ctkDICOMJobResponseSet::JobType::None) + { + continue; + } - d->dataModel->updateProgressBar(td, d->Scheduler->dicomDatabase()); + d->dataModel->updateProgressBar(td, d->Scheduler->dicomDatabase()); + } } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onJobAttemptFailed(QVariant data) +void ctkDICOMJobListWidget::onJobAttemptFailed(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobClass.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + + if(td.JobClass.isEmpty()) + { + continue; + } - d->dataModel->updateJobStatus(td, QCenteredItemModel::AttemptFailed); + d->dataModel->updateJobStatus(td, QCenteredItemModel::AttemptFailed); + } } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onJobFailed(QVariant data) +void ctkDICOMJobListWidget::onJobFailed(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobClass.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); - d->dataModel->updateJobStatus(td, QCenteredItemModel::Failed); + if(td.JobClass.isEmpty()) + { + continue; + } + + d->dataModel->updateJobStatus(td, QCenteredItemModel::Failed); + } } //---------------------------------------------------------------------------- -void ctkDICOMJobListWidget::onJobUserStopped(QVariant data) +void ctkDICOMJobListWidget::onJobUserStopped(QList datas) { Q_D(ctkDICOMJobListWidget); - ctkDICOMJobDetail td = data.value(); - - if(td.JobClass.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); - d->dataModel->updateJobStatus(td, QCenteredItemModel::UserStopped); + if(td.JobClass.isEmpty()) + { + continue; + } + + d->dataModel->updateJobStatus(td, QCenteredItemModel::UserStopped); + } } //---------------------------------------------------------------------------- diff --git a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.h b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.h index 613e91d119..33cb6cc517 100644 --- a/Libs/DICOM/Widgets/ctkDICOMJobListWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMJobListWidget.h @@ -57,12 +57,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMJobListWidget : public QWidget public Q_SLOTS: void onJobInitialized(QVariant); void onJobQueued(QVariant); - void onJobStarted(QVariant); - void onJobAttemptFailed(QVariant); - void onJobFailed(QVariant); - void onJobUserStopped(QVariant); - void onJobFinished(QVariant); - void onProgressJobDetail(QVariant); + void onJobStarted(QList); + void onJobAttemptFailed(QList); + void onJobFailed(QList); + void onJobUserStopped(QList); + void onJobFinished(QList); + void onProgressJobDetail(QList); void onFilterTextChanged(QString); void onFilterColumnChanged(QString); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index 3879a024a0..810ce15808 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -65,7 +65,8 @@ class ctkDICOMPatientItemWidgetPrivate : public Ui_ctkDICOMPatientItemWidget ~ctkDICOMPatientItemWidgetPrivate(); void init(QWidget* parentWidget); - + void connectScheduler(); + void disconnectScheduler(); QString getPatientItemFromPatientID(const QString& patientID); QString formatDate(const QString&); bool isStudyItemAlreadyAdded(const QString& studyItem); @@ -80,7 +81,7 @@ class ctkDICOMPatientItemWidgetPrivate : public Ui_ctkDICOMPatientItemWidget QSharedPointer Scheduler; QSharedPointer VisualDICOMBrowser; - int NumberOfStudiesPerPatient; + int NumberOfOpenedStudiesPerPatient; ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; QString PatientItem; @@ -115,7 +116,7 @@ ctkDICOMPatientItemWidgetPrivate::ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatie : q_ptr(&obj) { this->FilteringDate = ctkDICOMPatientItemWidget::DateType::Any; - this->NumberOfStudiesPerPatient = 2; + this->NumberOfOpenedStudiesPerPatient = 2; this->ThumbnailSize = ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium; this->PatientItem = ""; this->PatientID = ""; @@ -164,6 +165,48 @@ void ctkDICOMPatientItemWidgetPrivate::init(QWidget* parentWidget) q, SLOT(onPatientServersCheckableComboBoxChanged())); } +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidgetPrivate::connectScheduler() +{ + Q_Q(ctkDICOMPatientItemWidget); + if (!this->Scheduler) + { + return; + } + + QObject::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(updateGUIFromScheduler(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); +} + +//---------------------------------------------------------------------------- +void ctkDICOMPatientItemWidgetPrivate::disconnectScheduler() +{ + Q_Q(ctkDICOMPatientItemWidget); + if (!this->Scheduler) + { + return; + } + + QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(updateGUIFromScheduler(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); +} + //---------------------------------------------------------------------------- QString ctkDICOMPatientItemWidgetPrivate::getPatientItemFromPatientID(const QString& patientID) { @@ -382,7 +425,7 @@ void ctkDICOMPatientItemWidgetPrivate::createStudies() foreach (ctkDICOMStudyItemWidget* studyItemWidget, studiesMap) { studiesListWidgetLayout->addWidget(studyItemWidget); - if (cont < this->NumberOfStudiesPerPatient) + if (cont < this->NumberOfOpenedStudiesPerPatient) { studyItemWidget->setCollapsed(false); studyItemWidget->generateSeries(this->QueryOn, this->RetrieveOn); @@ -603,8 +646,8 @@ CTK_SET_CPP(ctkDICOMPatientItemWidget, const QString&, setFilteringSeriesDescrip CTK_GET_CPP(ctkDICOMPatientItemWidget, QString, filteringSeriesDescription, FilteringSeriesDescription); CTK_SET_CPP(ctkDICOMPatientItemWidget, const QStringList&, setFilteringModalities, FilteringModalities); CTK_GET_CPP(ctkDICOMPatientItemWidget, QStringList, filteringModalities, FilteringModalities); -CTK_SET_CPP(ctkDICOMPatientItemWidget, int, setNumberOfStudiesPerPatient, NumberOfStudiesPerPatient); -CTK_GET_CPP(ctkDICOMPatientItemWidget, int, numberOfStudiesPerPatient, NumberOfStudiesPerPatient); +CTK_SET_CPP(ctkDICOMPatientItemWidget, int, setNumberOfOpenedStudiesPerPatient, NumberOfOpenedStudiesPerPatient); +CTK_GET_CPP(ctkDICOMPatientItemWidget, int, numberOfOpenedStudiesPerPatient, NumberOfOpenedStudiesPerPatient); CTK_SET_CPP(ctkDICOMPatientItemWidget, const ctkDICOMStudyItemWidget::ThumbnailSizeOption&, setThumbnailSize, ThumbnailSize); CTK_GET_CPP(ctkDICOMPatientItemWidget, ctkDICOMStudyItemWidget::ThumbnailSizeOption, thumbnailSize, ThumbnailSize); @@ -634,38 +677,18 @@ QSharedPointer ctkDICOMPatientItemWidget::schedulerShared() c void ctkDICOMPatientItemWidget::setScheduler(ctkDICOMScheduler& scheduler) { Q_D(ctkDICOMPatientItemWidget); - if (d->Scheduler) - { - QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - this, SLOT(updateGUIFromScheduler(QVariant))); - } - + d->disconnectScheduler(); d->Scheduler = QSharedPointer(&scheduler, skipDelete); - - if (d->Scheduler) - { - QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - this, SLOT(updateGUIFromScheduler(QVariant))); - } + d->connectScheduler(); } //---------------------------------------------------------------------------- void ctkDICOMPatientItemWidget::setScheduler(QSharedPointer scheduler) { Q_D(ctkDICOMPatientItemWidget); - if (d->Scheduler) - { - QObject::disconnect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - this, SLOT(updateGUIFromScheduler(QVariant))); - } - + d->disconnectScheduler(); d->Scheduler = scheduler; - - if (d->Scheduler) - { - QObject::connect(d->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - this, SLOT(updateGUIFromScheduler(QVariant))); - } + d->connectScheduler(); } //---------------------------------------------------------------------------- @@ -752,7 +775,8 @@ void ctkDICOMPatientItemWidget::addStudyItemWidget(const QString& studyItem) { studyDescription = this->tr("UNDEFINED"); } - ctkDICOMStudyItemWidget* studyItemWidget = new ctkDICOMStudyItemWidget(d->VisualDICOMBrowser.data()); + ctkDICOMStudyItemWidget* studyItemWidget = + new ctkDICOMStudyItemWidget(this, d->VisualDICOMBrowser.data()); studyItemWidget->setStudyItem(studyItem); studyItemWidget->setPatientID(d->PatientID); studyItemWidget->setStudyInstanceUID(studyInstanceUID); @@ -896,24 +920,142 @@ void ctkDICOMPatientItemWidget::generateSeriesAtToggle(bool toggled, const QStri } //------------------------------------------------------------------------------ -void ctkDICOMPatientItemWidget::updateGUIFromScheduler(const QVariant& data) +void ctkDICOMPatientItemWidget::updateGUIFromScheduler(QList datas) { Q_D(ctkDICOMPatientItemWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + bool updateStudies = false; + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + d->createStudies(); + continue; + } + + if (td.PatientID != d->PatientID) + { + continue; + } + + emit this->progressJobDetail(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QueryStudies) + { + continue; + } + + updateStudies = true; + } + + if (updateStudies) { d->createStudies(); } +} - if (td.JobUID.isEmpty() || - td.JobType != ctkDICOMJobResponseSet::JobType::QueryStudies || - td.PatientID != d->PatientID) +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::onJobStarted(QList datas) +{ + Q_D(ctkDICOMPatientItemWidget); + foreach (QVariant data, datas) { - return; + ctkDICOMJobDetail td = data.value(); + + if (td.JobUID.isEmpty() || + td.PatientID != d->PatientID) + { + continue; + } + + emit this->jobStarted(data); } +} - d->createStudies(); +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::onJobUserStopped(QList datas) +{ + Q_D(ctkDICOMPatientItemWidget); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + + if (td.JobUID.isEmpty() || + td.PatientID != d->PatientID) + { + continue; + } + + emit this->jobUserStopped(data); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::onJobFailed(QList datas) +{ + Q_D(ctkDICOMPatientItemWidget); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + + if (td.JobUID.isEmpty() || + td.PatientID != d->PatientID) + { + continue; + } + + emit this->jobFailed(data); + } +} + +//------------------------------------------------------------------------------ +void ctkDICOMPatientItemWidget::onJobFinished(QList datas) +{ + Q_D(ctkDICOMPatientItemWidget); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + if (td.JobType == ctkDICOMJobResponseSet::JobType::Inserter) + { + 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)); + if (!seriesItemWidget) + { + continue; + } + if (seriesItemWidget->referenceSeriesInserterJobUID() == td.JobUID || + seriesItemWidget->referenceInstanceInserterJobUID() == td.JobUID || + seriesItemWidget->referenceSeriesInserterJobUID() == "StorageListener" || + seriesItemWidget->referenceInstanceInserterJobUID() == "StorageListener") + { + seriesItemWidget->onJobFinished(data); + } + } + } + } + } + + if (td.JobUID.isEmpty() || + td.PatientID != d->PatientID) + { + continue; + } + + emit this->jobFinished(data); + } } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index cdfad76798..b95fd308ba 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -47,7 +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(QString patientName READ patientName WRITE setPatientName); - Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); + Q_PROPERTY(int numberOfOpenedStudiesPerPatient READ numberOfOpenedStudiesPerPatient WRITE setNumberOfOpenedStudiesPerPatient); Q_PROPERTY(ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize READ thumbnailSize WRITE setThumbnailSize); Q_PROPERTY(QStringList allowedServers READ allowedServers WRITE setAllowedServers); Q_PROPERTY(OperationStatus operationStatus READ operationStatus WRITE setOperationStatus); @@ -133,8 +133,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget ///@{ /// Number of non collapsed studies per patient /// 2 by default - void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); - int numberOfStudiesPerPatient() const; + void setNumberOfOpenedStudiesPerPatient(int numberOfOpenedStudiesPerPatient); + int numberOfOpenedStudiesPerPatient() const; ///@} ///@{ @@ -208,7 +208,11 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget public Q_SLOTS: void generateStudies(bool query = true, bool retrieve = true); void generateSeriesAtToggle(bool toggled = true, const QString& studyItem = ""); - void updateGUIFromScheduler(const QVariant& data); + void updateGUIFromScheduler(QList); + void onJobStarted(QList); + void onJobUserStopped(QList); + void onJobFailed(QList); + void onJobFinished(QList); void onSeriesItemClicked(); void raiseSelectedSeriesJobsPriority(); void onPatientServersCheckableComboBoxChanged(); @@ -216,6 +220,12 @@ public Q_SLOTS: Q_SIGNALS: /// Emitted when the GUI finished to update after a studies query. void updateGUIFinished(); + /// Propagate jobs signals to the tree + void jobStarted(QVariant); + void jobUserStopped(QVariant); + void jobFinished(QVariant); + void jobFailed(QVariant); + void progressJobDetail(QVariant); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 77ae9ca404..f6a5cf685a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -40,6 +40,7 @@ #include "ctkDICOMJob.h" #include "ctkDICOMJobResponseSet.h" #include "ctkDICOMScheduler.h" +#include "ctkDICOMStudyItemWidget.h" #include "ctkDICOMThumbnailGenerator.h" // ctkDICOMWidgets includes @@ -48,6 +49,14 @@ static ctkLogger logger("org.commontk.DICOM.Widgets.DICOMSeriesItemWidget"); +//---------------------------------------------------------------------------- +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 ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget { @@ -60,9 +69,9 @@ class ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesItemWidget& obj); ~ctkDICOMSeriesItemWidgetPrivate(); - void init(); - void connectScheduler(); - void disconnectScheduler(); + void init(ctkDICOMStudyItemWidget* top); + void connectToTop(); + void disconnectFromTop(); QString getDICOMCenterFrameFromInstances(QStringList instancesList); void createThumbnail(ctkDICOMJobDetail td); void drawModalityThumbnail(); @@ -81,6 +90,8 @@ class ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget QSharedPointer DicomDatabase; QSharedPointer Scheduler; + QSharedPointer StudyWidget; + QMap Connections; QStringList AllowedServers; QString PatientID; @@ -96,6 +107,7 @@ class ctkDICOMSeriesItemWidgetPrivate : public Ui_ctkDICOMSeriesItemWidget bool RaiseJobsPriority; bool IsCloud; bool RetrieveFailed; + bool RetrieveSeries; bool IsLoaded; bool IsVisible; int ThumbnailSizePixel; @@ -126,6 +138,7 @@ ctkDICOMSeriesItemWidgetPrivate::ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesI this->IsCloud = false; this->RetrieveFailed = false; + this->RetrieveSeries = false; this->IsLoaded = false; this->IsVisible = false; this->StopJobs = false; @@ -146,17 +159,74 @@ ctkDICOMSeriesItemWidgetPrivate::ctkDICOMSeriesItemWidgetPrivate(ctkDICOMSeriesI //---------------------------------------------------------------------------- ctkDICOMSeriesItemWidgetPrivate::~ctkDICOMSeriesItemWidgetPrivate() { + this->disconnectFromTop(); } //---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidgetPrivate::init() +void ctkDICOMSeriesItemWidgetPrivate::init(ctkDICOMStudyItemWidget* top) { Q_Q(ctkDICOMSeriesItemWidget); this->setupUi(q); + this->StudyWidget = QSharedPointer(top, skipDelete); + this->connectToTop(); + this->SeriesThumbnail->setTransformationMode(Qt::TransformationMode::SmoothTransformation); this->SeriesThumbnail->textPushButton()->setElideMode(Qt::ElideRight); this->SeriesThumbnail->setSelectedColor(QColor::Invalid); + + QObject::connect(this->SeriesThumbnail, SIGNAL(statusPushButtonClicked(bool)), + q, SLOT(onStatusPushButtonClicked(bool))); +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::connectToTop() +{ + Q_Q(ctkDICOMSeriesItemWidget); + if (!this->StudyWidget) + { + return; + } + + QMetaObject::Connection progressConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(progressJobDetail(QVariant)), + q, SLOT(updateGUIFromScheduler(QVariant))); + QMetaObject::Connection progressBarConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(progressJobDetail(QVariant)), + q, SLOT(updateSeriesProgressBar(QVariant))); + QMetaObject::Connection startedConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(jobStarted(QVariant)), + q, SLOT(onJobStarted(QVariant))); + QMetaObject::Connection userStoppedConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(jobUserStopped(QVariant)), + q, SLOT(onJobUserStopped(QVariant))); + QMetaObject::Connection failedConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(jobFailed(QVariant)), + q, SLOT(onJobFailed(QVariant))); + QMetaObject::Connection finishedConnection = + QObject::connect(this->StudyWidget.data(), SIGNAL(jobFinished(QVariant)), + q, SLOT(onJobFinished(QVariant))); + + this->Connections = + { + {"progress", progressConnection}, + {"progressBar", progressBarConnection}, + {"started", startedConnection}, + {"userStopped", userStoppedConnection}, + {"finished", finishedConnection}, + {"failed", failedConnection} + }; +} + +//---------------------------------------------------------------------------- +void ctkDICOMSeriesItemWidgetPrivate::disconnectFromTop() +{ + QObject::disconnect(this->Connections.value("progress")); + QObject::disconnect(this->Connections.value("progressBar")); + QObject::disconnect(this->Connections.value("started")); + QObject::disconnect(this->Connections.value("userStopped")); + QObject::disconnect(this->Connections.value("finished")); + QObject::disconnect(this->Connections.value("failed")); } //---------------------------------------------------------------------------- @@ -307,41 +377,31 @@ void ctkDICOMSeriesItemWidgetPrivate::createThumbnail(ctkDICOMJobDetail td) if (file.isEmpty() && (this->IsCloud || this->RetrieveFailed) && (jobType == ctkDICOMJobResponseSet::JobType::None || - jobType == ctkDICOMJobResponseSet::JobType::QueryInstances)) + jobType == ctkDICOMJobResponseSet::JobType::QueryInstances) && + this->RetrieveOn) { - if (this->RetrieveOn) - { - this->Scheduler->retrieveSOPInstance(this->PatientID, - this->StudyInstanceUID, - this->SeriesInstanceUID, - this->CentralFrameSOPInstanceUID, - this->RaiseJobsPriority ? QThread::HighestPriority : QThread::HighPriority, - this->AllowedServers); - } + this->Scheduler->retrieveSOPInstance(this->PatientID, + this->StudyInstanceUID, + this->SeriesInstanceUID, + this->CentralFrameSOPInstanceUID, + this->RaiseJobsPriority ? QThread::HighestPriority : QThread::HighPriority, + this->AllowedServers); + this->RetrieveSeries = true; return; } // Get series if (numberOfFrames > 1 && - (this->IsCloud || this->RetrieveFailed) && - ((jobSopInstanceUID == this->CentralFrameSOPInstanceUID && - (jobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || - jobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance)) || - (jobType == ctkDICOMJobResponseSet::JobType::None || - jobType == ctkDICOMJobResponseSet::JobType::QueryInstances))) - { - QList> jobs = - this->Scheduler->getJobsByDICOMUIDs({}, - {}, - {this->SeriesInstanceUID}); - if (jobs.count() == 0 && this->RetrieveOn) + this->IsCloud && + this->RetrieveSeries && + this->RetrieveOn) { - this->Scheduler->retrieveSeries(this->PatientID, - this->StudyInstanceUID, - this->SeriesInstanceUID, - this->RaiseJobsPriority ? QThread::HighestPriority : QThread::LowPriority, - this->AllowedServers); - } + this->RetrieveSeries = false; + this->Scheduler->retrieveSeries(this->PatientID, + this->StudyInstanceUID, + this->SeriesInstanceUID, + this->RaiseJobsPriority ? QThread::HighestPriority : QThread::LowPriority, + this->AllowedServers); } } @@ -680,69 +740,25 @@ void ctkDICOMSeriesItemWidgetPrivate::updateRetrieveUIOnFinished() this->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/accept.svg")); } -//---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidgetPrivate::connectScheduler() -{ - Q_Q(ctkDICOMSeriesItemWidget); - if (!this->Scheduler) - { - return; - } - - QObject::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateSeriesProgressBar(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); - QObject::connect(this->SeriesThumbnail, SIGNAL(statusPushButtonClicked(bool)), - q, SLOT(onStatusPushButtonClicked(bool))); -} - -//---------------------------------------------------------------------------- -void ctkDICOMSeriesItemWidgetPrivate::disconnectScheduler() -{ - Q_Q(ctkDICOMSeriesItemWidget); - if (!this->Scheduler) - { - return; - } - - QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateSeriesProgressBar(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); -} - //---------------------------------------------------------------------------- // ctkDICOMSeriesItemWidget methods //---------------------------------------------------------------------------- -ctkDICOMSeriesItemWidget::ctkDICOMSeriesItemWidget(QWidget* parentWidget) - : Superclass(parentWidget) +ctkDICOMSeriesItemWidget::ctkDICOMSeriesItemWidget(ctkDICOMStudyItemWidget* top, QWidget* parent) + : Superclass(parent) , d_ptr(new ctkDICOMSeriesItemWidgetPrivate(*this)) { Q_D(ctkDICOMSeriesItemWidget); - d->init(); + d->init(top); } //---------------------------------------------------------------------------- ctkDICOMSeriesItemWidget::~ctkDICOMSeriesItemWidget() { + Q_D(ctkDICOMSeriesItemWidget); + + QObject::disconnect(d->SeriesThumbnail, SIGNAL(statusPushButtonClicked(bool)), + this, SLOT(onStatusPushButtonClicked(bool))); } //------------------------------------------------------------------------------ @@ -769,6 +785,8 @@ CTK_GET_CPP(ctkDICOMSeriesItemWidget, bool, isLoaded, IsLoaded); CTK_GET_CPP(ctkDICOMSeriesItemWidget, bool, isVisible, IsVisible); CTK_SET_CPP(ctkDICOMSeriesItemWidget, bool, setRetrieveFailed, RetrieveFailed); CTK_GET_CPP(ctkDICOMSeriesItemWidget, bool, retrieveFailed, RetrieveFailed); +CTK_GET_CPP(ctkDICOMSeriesItemWidget, QString, referenceSeriesInserterJobUID, ReferenceSeriesInserterJobUID); +CTK_GET_CPP(ctkDICOMSeriesItemWidget, QString, referenceInstanceInserterJobUID, ReferenceInstanceInserterJobUID); CTK_SET_CPP(ctkDICOMSeriesItemWidget, int, setThumbnailSizePixel, ThumbnailSizePixel); CTK_GET_CPP(ctkDICOMSeriesItemWidget, int, thumbnailSizePixel, ThumbnailSizePixel); @@ -799,14 +817,6 @@ void ctkDICOMSeriesItemWidget::forceRetrieve() this->generateInstances(true); } -//---------------------------------------------------------------------------- -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 { @@ -825,18 +835,14 @@ QSharedPointer ctkDICOMSeriesItemWidget::schedulerShared() co void ctkDICOMSeriesItemWidget::setScheduler(ctkDICOMScheduler& scheduler) { Q_D(ctkDICOMSeriesItemWidget); - d->disconnectScheduler(); d->Scheduler = QSharedPointer(&scheduler, skipDelete); - d->connectScheduler(); } //---------------------------------------------------------------------------- void ctkDICOMSeriesItemWidget::setScheduler(QSharedPointer scheduler) { Q_D(ctkDICOMSeriesItemWidget); - d->disconnectScheduler(); d->Scheduler = scheduler; - d->connectScheduler(); } //---------------------------------------------------------------------------- @@ -903,12 +909,14 @@ void ctkDICOMSeriesItemWidget::updateGUIFromScheduler(const QVariant& data) Q_D(ctkDICOMSeriesItemWidget); ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty() || - (td.JobType != ctkDICOMJobResponseSet::JobType::QueryInstances && - td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance&& - td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || - td.PatientID != d->PatientID || - td.StudyInstanceUID != d->StudyInstanceUID || + if (td.JobUID.isEmpty()) + { + return; + } + + if ((td.JobType != ctkDICOMJobResponseSet::JobType::QueryInstances && + td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance && + td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance ) || td.SeriesInstanceUID != d->SeriesInstanceUID) { return; @@ -923,23 +931,19 @@ void ctkDICOMSeriesItemWidget::updateSeriesProgressBar(const QVariant& data) Q_D(ctkDICOMSeriesItemWidget); ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty() || - (td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && + if (td.JobUID.isEmpty()) + { + return; + } + + if ((td.JobType != ctkDICOMJobResponseSet::JobType::RetrieveSeries && td.JobType != ctkDICOMJobResponseSet::JobType::StoreSOPInstance) || - td.PatientID != d->PatientID || - td.StudyInstanceUID != d->StudyInstanceUID || td.SeriesInstanceUID != d->SeriesInstanceUID) { return; } d->updateThumbnailProgressBar(); - - if (td.JobType == ctkDICOMJobResponseSet::JobType::StoreSOPInstance && - d->ReferenceSeriesInserterJobUID == "StorageListener") - { - d->updateRetrieveUIOnFinished(); - } } //---------------------------------------------------------------------------- @@ -952,8 +956,6 @@ void ctkDICOMSeriesItemWidget::onJobStarted(const QVariant &data) (td.JobType == ctkDICOMJobResponseSet::JobType::QueryInstances || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries) && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID && td.SeriesInstanceUID == d->SeriesInstanceUID) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::InProgress); @@ -981,8 +983,6 @@ void ctkDICOMSeriesItemWidget::onJobUserStopped(const QVariant &data) (td.JobType == ctkDICOMJobResponseSet::JobType::QueryInstances || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries) && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID && td.SeriesInstanceUID == d->SeriesInstanceUID) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::Failed); @@ -1010,8 +1010,6 @@ void ctkDICOMSeriesItemWidget::onJobFailed(const QVariant &data) (td.JobType == ctkDICOMJobResponseSet::JobType::QueryInstances || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance || td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries) && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID && td.SeriesInstanceUID == d->SeriesInstanceUID) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::Failed); @@ -1039,11 +1037,9 @@ void ctkDICOMSeriesItemWidget::onJobFinished(const QVariant &data) return; } - if ((td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || - td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID && - td.SeriesInstanceUID == d->SeriesInstanceUID) + if (td.SeriesInstanceUID == d->SeriesInstanceUID && + (td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSeries || + td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance)) { if (td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { @@ -1071,19 +1067,23 @@ void ctkDICOMSeriesItemWidget::onJobFinished(const QVariant &data) return; } + if (td.JobType != ctkDICOMJobResponseSet::JobType::Inserter) + { + return; + } + QStringList instancesList = d->DicomDatabase->instancesForSeries(d->SeriesInstanceUID); int numberOfFrames = instancesList.count(); - if (!d->ReferenceInstanceInserterJobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::Inserter && - td.JobUID == d->ReferenceInstanceInserterJobUID && + if ((d->ReferenceInstanceInserterJobUID == td.JobUID || + d->ReferenceInstanceInserterJobUID == "StorageListener") && numberOfFrames == 1) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::Completed); d->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/accept.svg")); + d->updateRetrieveUIOnFinished(); } - else if (!d->ReferenceSeriesInserterJobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::Inserter && - td.JobUID == d->ReferenceSeriesInserterJobUID) + else if (d->ReferenceSeriesInserterJobUID == td.JobUID || + d->ReferenceSeriesInserterJobUID == "StorageListener") { d->ReferenceSeriesInserterJobUID = ""; d->updateRetrieveUIOnFinished(); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h index 596bb77074..f57fe7bbf4 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.h @@ -34,7 +34,9 @@ class ctkDICOMScheduler; // ctkDICOMWidgets includes #include "ctkDICOMWidgetsExport.h" + class ctkDICOMSeriesItemWidgetPrivate; +class ctkDICOMStudyItemWidget; /// \ingroup DICOM_Widgets class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget @@ -49,6 +51,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget Q_PROPERTY(QString seriesDescription READ seriesDescription WRITE setSeriesDescription); Q_PROPERTY(bool isCloud READ isCloud); Q_PROPERTY(bool retrieveFailed READ retrieveFailed WRITE setRetrieveFailed); + Q_PROPERTY(QString referenceSeriesInserterJobUID READ referenceSeriesInserterJobUID); + Q_PROPERTY(QString referenceInstanceInserterJobUID READ referenceInstanceInserterJobUID); Q_PROPERTY(int thumbnailSizePixel READ thumbnailSizePixel WRITE setThumbnailSizePixel); Q_PROPERTY(bool stopJobs READ stopJobs WRITE setStopJobs); Q_PROPERTY(bool raiseJobsPriority READ raiseJobsPriority WRITE setRaiseJobsPriority); @@ -56,7 +60,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget public: typedef QWidget Superclass; - explicit ctkDICOMSeriesItemWidget(QWidget* parent = nullptr); + explicit ctkDICOMSeriesItemWidget(ctkDICOMStudyItemWidget* top = nullptr, + QWidget* parent = nullptr); virtual ~ctkDICOMSeriesItemWidget(); ///@{ @@ -126,6 +131,12 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMSeriesItemWidget : public QWidget bool retrieveFailed() const; ///@} + ///@{ + /// Return the referenceInserterJobUID + QString referenceSeriesInserterJobUID() const; + QString referenceInstanceInserterJobUID() const; + ///@} + /// Series has been loaded by the parent widget bool isLoaded() const; diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp index a950a2b966..4237b9ae18 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.cpp @@ -397,14 +397,14 @@ void ctkDICOMServerNodeWidget2Private::disconnectScheduler() return; } - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); - ctkDICOMServerNodeWidget2::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); } //---------------------------------------------------------------------------- @@ -416,14 +416,14 @@ void ctkDICOMServerNodeWidget2Private::connectScheduler() return; } - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); - ctkDICOMServerNodeWidget2::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); } //---------------------------------------------------------------------------- @@ -1204,38 +1204,50 @@ void ctkDICOMServerNodeWidget2::onVerifyCurrentServerNode() //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::onJobStarted(QVariant data) +void ctkDICOMServerNodeWidget2::onJobStarted(QList datas) { Q_D(ctkDICOMServerNodeWidget2); - ctkDICOMJobDetail td = data.value(); - d->updateServerVerification(td, QString(tr("in-progress"))); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + d->updateServerVerification(td, QString(tr("in-progress"))); + } this->updateGUIState(); } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::onJobUserStopped(QVariant data) +void ctkDICOMServerNodeWidget2::onJobUserStopped(QList datas) { Q_D(ctkDICOMServerNodeWidget2); - ctkDICOMJobDetail td = data.value(); - d->updateServerVerification(td, QString(tr("user-stopped"))); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + d->updateServerVerification(td, QString(tr("user-stopped"))); + } this->updateGUIState(); } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::onJobFailed(QVariant data) +void ctkDICOMServerNodeWidget2::onJobFailed(QList datas) { Q_D(ctkDICOMServerNodeWidget2); - ctkDICOMJobDetail td = data.value(); - d->updateServerVerification(td, QString(tr("failed"))); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + d->updateServerVerification(td, QString(tr("failed"))); + } this->updateGUIState(); } //---------------------------------------------------------------------------- -void ctkDICOMServerNodeWidget2::onJobFinished(QVariant data) +void ctkDICOMServerNodeWidget2::onJobFinished(QList datas) { Q_D(ctkDICOMServerNodeWidget2); - ctkDICOMJobDetail td = data.value(); - d->updateServerVerification(td, QString(tr("success"))); + foreach (QVariant data, datas) + { + ctkDICOMJobDetail td = data.value(); + d->updateServerVerification(td, QString(tr("success"))); + } this->updateGUIState(); } diff --git a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h index ed5c5c3406..e766b65420 100644 --- a/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h +++ b/Libs/DICOM/Widgets/ctkDICOMServerNodeWidget2.h @@ -110,10 +110,10 @@ public Q_SLOTS: /// Verify the current selected row (different from the checked rows) void onVerifyCurrentServerNode(); - void onJobStarted(QVariant); - void onJobUserStopped(QVariant); - void onJobFailed(QVariant); - void onJobFinished(QVariant); + void onJobStarted(QList); + void onJobUserStopped(QList); + void onJobFailed(QList); + void onJobFinished(QList); void readSettings(); void saveSettings(); diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index bfd984d90c..d5d9e497ab 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -37,6 +37,7 @@ #include "ctkDICOMScheduler.h" // ctkDICOMWidgets includes +#include "ctkDICOMPatientItemWidget.h" #include "ctkDICOMStudyItemWidget.h" #include "ui_ctkDICOMStudyItemWidget.h" @@ -64,9 +65,9 @@ class ctkDICOMStudyItemWidgetPrivate : public Ui_ctkDICOMStudyItemWidget ctkDICOMStudyItemWidgetPrivate(ctkDICOMStudyItemWidget& obj); ~ctkDICOMStudyItemWidgetPrivate(); - void init(QWidget* parentWidget); - void connectScheduler(); - void disconnectScheduler(); + void init(ctkDICOMPatientItemWidget* parent, QWidget* root); + void connectToTop(); + void disconnectFromTop(); void updateColumnsWidths(); void createSeries(); int getScreenWidth(); @@ -83,7 +84,9 @@ class ctkDICOMStudyItemWidgetPrivate : public Ui_ctkDICOMStudyItemWidget QSharedPointer DicomDatabase; QSharedPointer Scheduler; + QSharedPointer PatientWidget; QSharedPointer VisualDICOMBrowser; + QMap Connections; ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; int ThumbnailSizePixel; @@ -127,8 +130,7 @@ ctkDICOMStudyItemWidgetPrivate::ctkDICOMStudyItemWidgetPrivate(ctkDICOMStudyItem //---------------------------------------------------------------------------- ctkDICOMStudyItemWidgetPrivate::~ctkDICOMStudyItemWidgetPrivate() { - Q_Q(ctkDICOMStudyItemWidget); - + this->disconnectFromTop(); for (int row = 0; row < this->SeriesListTableWidget->rowCount(); row++) { for (int column = 0; column < this->SeriesListTableWidget->columnCount(); column++) @@ -140,19 +142,21 @@ ctkDICOMStudyItemWidgetPrivate::~ctkDICOMStudyItemWidgetPrivate() continue; } - q->disconnect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), - this->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); + QObject::disconnect(seriesItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this->VisualDICOMBrowser.data(), SLOT(showSeriesContextMenu(const QPoint&))); } } } //---------------------------------------------------------------------------- -void ctkDICOMStudyItemWidgetPrivate::init(QWidget* parentWidget) +void ctkDICOMStudyItemWidgetPrivate::init(ctkDICOMPatientItemWidget* top, QWidget* parent) { Q_Q(ctkDICOMStudyItemWidget); this->setupUi(q); - this->VisualDICOMBrowser = QSharedPointer(parentWidget, skipDelete); + this->PatientWidget = QSharedPointer(top, skipDelete); + this->connectToTop(); + this->VisualDICOMBrowser = QSharedPointer(parent, skipDelete); this->StudyDescriptionTextBrowser->hide(); this->StudyDescriptionTextBrowser->setReadOnly(true); @@ -160,52 +164,54 @@ void ctkDICOMStudyItemWidgetPrivate::init(QWidget* parentWidget) this->OperationStatusPushButton->hide(); - q->connect(this->StudySelectionCheckBox, SIGNAL(clicked(bool)), - q, SLOT(onStudySelectionClicked(bool))); - q->connect(this->OperationStatusPushButton, SIGNAL(clicked(bool)), - q, SLOT(onOperationStatusClicked(bool))); + QObject::connect(this->StudySelectionCheckBox, SIGNAL(clicked(bool)), + q, SLOT(onStudySelectionClicked(bool))); + QObject::connect(this->OperationStatusPushButton, SIGNAL(clicked(bool)), + q, SLOT(onOperationStatusClicked(bool))); } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidgetPrivate::connectScheduler() +void ctkDICOMStudyItemWidgetPrivate::connectToTop() { Q_Q(ctkDICOMStudyItemWidget); - if (!this->Scheduler) + if (!this->PatientWidget) { return; } - QObject::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), + QMetaObject::Connection progressConnection = + QObject::connect(this->PatientWidget.data(), SIGNAL(progressJobDetail(QVariant)), q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), + QMetaObject::Connection startedConnection = + QObject::connect(this->PatientWidget.data(), SIGNAL(jobStarted(QVariant)), + q, SLOT(onJobStarted(QVariant))); + QMetaObject::Connection userStoppedConnection = + QObject::connect(this->PatientWidget.data(), SIGNAL(jobUserStopped(QVariant)), q, SLOT(onJobUserStopped(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); + QMetaObject::Connection failedConnection = + QObject::connect(this->PatientWidget.data(), SIGNAL(jobFailed(QVariant)), + q, SLOT(onJobFailed(QVariant))); + QMetaObject::Connection finishedConnection = + QObject::connect(this->PatientWidget.data(), SIGNAL(jobFinished(QVariant)), + q, SLOT(onJobFinished(QVariant))); + this->Connections = + { + {"progress", progressConnection}, + {"started", startedConnection}, + {"userStopped", userStoppedConnection}, + {"finished", finishedConnection}, + {"failed", failedConnection} + }; } //------------------------------------------------------------------------------ -void ctkDICOMStudyItemWidgetPrivate::disconnectScheduler() +void ctkDICOMStudyItemWidgetPrivate::disconnectFromTop() { - Q_Q(ctkDICOMStudyItemWidget); - if (!this->Scheduler) - { - return; - } - - QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); + QObject::disconnect(this->Connections.value("progress")); + QObject::disconnect(this->Connections.value("started")); + QObject::disconnect(this->Connections.value("userStopped")); + QObject::disconnect(this->Connections.value("finished")); + QObject::disconnect(this->Connections.value("failed")); } //------------------------------------------------------------------------------ @@ -446,12 +452,12 @@ ctkDICOMSeriesItemWidget* ctkDICOMStudyItemWidgetPrivate::isSeriesItemAlreadyAdd // ctkDICOMStudyItemWidget methods //---------------------------------------------------------------------------- -ctkDICOMStudyItemWidget::ctkDICOMStudyItemWidget(QWidget* parentWidget) - : Superclass(parentWidget) +ctkDICOMStudyItemWidget::ctkDICOMStudyItemWidget(ctkDICOMPatientItemWidget* top, QWidget* parent) + : Superclass(parent) , d_ptr(new ctkDICOMStudyItemWidgetPrivate(*this)) { Q_D(ctkDICOMStudyItemWidget); - d->init(parentWidget); + d->init(top, parent); } //---------------------------------------------------------------------------- @@ -587,18 +593,14 @@ QSharedPointer ctkDICOMStudyItemWidget::schedulerShared() con void ctkDICOMStudyItemWidget::setScheduler(ctkDICOMScheduler& scheduler) { Q_D(ctkDICOMStudyItemWidget); - d->disconnectScheduler(); d->Scheduler = QSharedPointer(&scheduler, skipDelete); - d->connectScheduler(); } //---------------------------------------------------------------------------- void ctkDICOMStudyItemWidget::setScheduler(QSharedPointer scheduler) { Q_D(ctkDICOMStudyItemWidget); - d->disconnectScheduler(); d->Scheduler = scheduler; - d->connectScheduler(); } //---------------------------------------------------------------------------- @@ -675,7 +677,7 @@ ctkDICOMSeriesItemWidget* ctkDICOMStudyItemWidget::addSeriesItemWidget(int table } QString seriesNumber = d->DicomDatabase->fieldForSeries("SeriesNumber", seriesItem); - ctkDICOMSeriesItemWidget* seriesItemWidget = new ctkDICOMSeriesItemWidget; + ctkDICOMSeriesItemWidget* seriesItemWidget = new ctkDICOMSeriesItemWidget(this); seriesItemWidget->setSeriesItem(seriesItem); seriesItemWidget->setPatientID(d->PatientID); seriesItemWidget->setStudyInstanceUID(d->StudyInstanceUID); @@ -771,12 +773,17 @@ void ctkDICOMStudyItemWidget::updateGUIFromScheduler(const QVariant& data) if (td.JobUID.isEmpty()) { d->createSeries(); + return; } - if (td.JobUID.isEmpty() || - td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries || - td.PatientID != d->PatientID || - td.StudyInstanceUID != d->StudyInstanceUID) + if (td.StudyInstanceUID != d->StudyInstanceUID) + { + return; + } + + emit this->progressJobDetail(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries) { return; } @@ -790,15 +797,22 @@ void ctkDICOMStudyItemWidget::onJobStarted(const QVariant &data) Q_D(ctkDICOMStudyItemWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID) + if (td.JobUID.isEmpty() || + td.StudyInstanceUID != d->StudyInstanceUID) { - d->Status = ctkDICOMStudyItemWidget::InProgress; - d->OperationStatusPushButton->show(); - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/pending.svg")); + return; } + + emit this->jobStarted(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries) + { + return; + } + + d->Status = ctkDICOMStudyItemWidget::InProgress; + d->OperationStatusPushButton->show(); + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/pending.svg")); } //------------------------------------------------------------------------------ @@ -807,14 +821,21 @@ void ctkDICOMStudyItemWidget::onJobUserStopped(const QVariant &data) Q_D(ctkDICOMStudyItemWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID) + if (td.JobUID.isEmpty() || + td.StudyInstanceUID != d->StudyInstanceUID) { - d->Status = ctkDICOMStudyItemWidget::Failed; - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); + return; + } + + emit this->jobUserStopped(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries) + { + return; } + + d->Status = ctkDICOMStudyItemWidget::Failed; + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); } //------------------------------------------------------------------------------ @@ -823,14 +844,21 @@ void ctkDICOMStudyItemWidget::onJobFailed(const QVariant &data) Q_D(ctkDICOMStudyItemWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID) + if (td.JobUID.isEmpty() || + td.StudyInstanceUID != d->StudyInstanceUID) + { + return; + } + + emit this->jobFailed(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries) { - d->Status = ctkDICOMStudyItemWidget::Failed; - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); + return; } + + d->Status = ctkDICOMStudyItemWidget::Failed; + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); } //------------------------------------------------------------------------------ @@ -839,14 +867,21 @@ void ctkDICOMStudyItemWidget::onJobFinished(const QVariant &data) Q_D(ctkDICOMStudyItemWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries && - td.PatientID == d->PatientID && - td.StudyInstanceUID == d->StudyInstanceUID) + if (td.JobUID.isEmpty() || + td.StudyInstanceUID != d->StudyInstanceUID) { - d->Status = ctkDICOMStudyItemWidget::Completed; - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/accept.svg")); + return; } + + emit this->jobFinished(data); + + if (td.JobType != ctkDICOMJobResponseSet::JobType::QuerySeries) + { + return; + } + + d->Status = ctkDICOMStudyItemWidget::Completed; + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/accept.svg")); } //------------------------------------------------------------------------------ diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h index 4b22c4e96c..86755c8196 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.h @@ -40,6 +40,8 @@ class ctkDICOMScheduler; // ctkDICOMWidgets includes #include "ctkDICOMSeriesItemWidget.h" + +class ctkDICOMPatientItemWidget; class ctkDICOMSeriesItemWidget; class ctkDICOMStudyItemWidgetPrivate; @@ -66,7 +68,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMStudyItemWidget : public QWidget public: typedef QWidget Superclass; - explicit ctkDICOMStudyItemWidget(QWidget* parent = nullptr); + explicit ctkDICOMStudyItemWidget(ctkDICOMPatientItemWidget* top = nullptr, + QWidget* parent = nullptr); virtual ~ctkDICOMStudyItemWidget(); ///@{ @@ -222,6 +225,12 @@ public Q_SLOTS: Q_SIGNALS: /// Emitted when the GUI finished to update after a series query. void updateGUIFinished(); + /// Propagate jobs signals to the tree + void jobStarted(QVariant); + void jobUserStopped(QVariant); + void jobFinished(QVariant); + void jobFailed(QVariant); + void progressJobDetail(QVariant); protected: QScopedPointer d_ptr; diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 98c54274c8..a10a870770 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -144,7 +144,7 @@ class ctkDICOMVisualBrowserWidgetPrivate : public Ui_ctkDICOMVisualBrowserWidget void importDirectory(QString directory, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); void importFiles(const QStringList& files, ctkDICOMVisualBrowserWidget::ImportDirectoryMode mode); void importOldSettings(); - void restoreSearchIcon(); + void configureSearchIcon(); void showUpdateSchemaDialog(); void updateModalityCheckableComboBox(); void createPatients(bool queryRetrieve = true, @@ -240,7 +240,7 @@ class ctkDICOMVisualBrowserWidgetPrivate : public Ui_ctkDICOMVisualBrowserWidget QStringList PreviousFilteringModalities; QStringList FilteringModalities; - int NumberOfStudiesPerPatient; + int NumberOfOpenedStudiesPerPatient; ctkDICOMStudyItemWidget::ThumbnailSizeOption ThumbnailSize; bool SendActionVisible; bool DeleteActionVisible; @@ -280,7 +280,7 @@ ctkDICOMVisualBrowserWidgetPrivate::ctkDICOMVisualBrowserWidgetPrivate(ctkDICOMV this->DefaultDatabaseDirectory = ""; this->DatabaseDirectory = ""; - this->NumberOfStudiesPerPatient = 2; + this->NumberOfOpenedStudiesPerPatient = 2; this->ThumbnailSize = ctkDICOMStudyItemWidget::ThumbnailSizeOption::Medium; this->SendActionVisible = false; this->DeleteActionVisible = true; @@ -394,6 +394,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() } toggleQueryRetrieveAction->setChecked(settings.value("DICOM/QueryRetrieveEnabled", "").toBool()); queryRetrieveButtonMenu->addAction(toggleQueryRetrieveAction); + this->configureSearchIcon(); QObject::connect(toggleQueryRetrieveAction, SIGNAL(toggled(bool)), q, SLOT(onQueryRetrieveOptionToggled(bool))); @@ -490,16 +491,16 @@ void ctkDICOMVisualBrowserWidgetPrivate::disconnectScheduler() return; } - QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(updateGUIFromScheduler(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + QObject::disconnect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); } //---------------------------------------------------------------------------- @@ -511,16 +512,16 @@ void ctkDICOMVisualBrowserWidgetPrivate::connectScheduler() return; } - q->connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QVariant)), - q, SLOT(updateGUIFromScheduler(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QVariant)), - q, SLOT(onJobStarted(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QVariant)), - q, SLOT(onJobUserStopped(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QVariant)), - q, SLOT(onJobFailed(QVariant))); - QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QVariant)), - q, SLOT(onJobFinished(QVariant))); + QObject::connect(this->Scheduler.data(), SIGNAL(progressJobDetail(QList)), + q, SLOT(updateGUIFromScheduler(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobStarted(QList)), + q, SLOT(onJobStarted(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobUserStopped(QList)), + q, SLOT(onJobUserStopped(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFailed(QList)), + q, SLOT(onJobFailed(QList))); + QObject::connect(this->Scheduler.data(), SIGNAL(jobFinished(QList)), + q, SLOT(onJobFinished(QList))); } //---------------------------------------------------------------------------- @@ -580,7 +581,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::importOldSettings() } //---------------------------------------------------------------------------- -void ctkDICOMVisualBrowserWidgetPrivate::restoreSearchIcon() +void ctkDICOMVisualBrowserWidgetPrivate::configureSearchIcon() { QSettings settings; bool queryRetrieveEnabled = settings.value("DICOM/QueryRetrieveEnabled", "").toBool(); @@ -1079,7 +1080,7 @@ void ctkDICOMVisualBrowserWidgetPrivate::retrieveSeries() } this->updateFiltersWarnings(); - this->restoreSearchIcon(); + this->configureSearchIcon(); foreach (ctkDICOMSeriesItemWidget* seriesItemWidget, seriesWidgetsList) { @@ -1713,8 +1714,8 @@ CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QString, filteringStudyDescription, Fil CTK_GET_CPP(ctkDICOMVisualBrowserWidget, ctkDICOMPatientItemWidget::DateType, filteringDate, FilteringDate); CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QString, filteringSeriesDescription, FilteringSeriesDescription); CTK_GET_CPP(ctkDICOMVisualBrowserWidget, QStringList, filteringModalities, FilteringModalities); -CTK_SET_CPP(ctkDICOMVisualBrowserWidget, int, setNumberOfStudiesPerPatient, NumberOfStudiesPerPatient); -CTK_GET_CPP(ctkDICOMVisualBrowserWidget, int, numberOfStudiesPerPatient, NumberOfStudiesPerPatient); +CTK_SET_CPP(ctkDICOMVisualBrowserWidget, int, setNumberOfOpenedStudiesPerPatient, NumberOfOpenedStudiesPerPatient); +CTK_GET_CPP(ctkDICOMVisualBrowserWidget, int, numberOfOpenedStudiesPerPatient, NumberOfOpenedStudiesPerPatient); CTK_SET_CPP(ctkDICOMVisualBrowserWidget, const ctkDICOMStudyItemWidget::ThumbnailSizeOption&, setThumbnailSize, ThumbnailSize); CTK_GET_CPP(ctkDICOMVisualBrowserWidget, ctkDICOMStudyItemWidget::ThumbnailSizeOption, thumbnailSize, ThumbnailSize); CTK_SET_CPP(ctkDICOMVisualBrowserWidget, bool, setSendActionVisible, SendActionVisible); @@ -2022,7 +2023,7 @@ int ctkDICOMVisualBrowserWidget::addPatientItemWidget(const QString& patientItem patientItemWidget->setFilteringSeriesDescription(d->FilteringSeriesDescription); patientItemWidget->setFilteringModalities(d->FilteringModalities); patientItemWidget->setThumbnailSize(d->ThumbnailSize); - patientItemWidget->setNumberOfStudiesPerPatient(d->NumberOfStudiesPerPatient); + patientItemWidget->setNumberOfOpenedStudiesPerPatient(d->NumberOfOpenedStudiesPerPatient); patientItemWidget->setContextMenuPolicy(Qt::CustomContextMenu); this->connect(patientItemWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPatientContextMenu(const QPoint&))); @@ -2360,7 +2361,7 @@ void ctkDICOMVisualBrowserWidget::onIndexingComplete(int patientsAdded, int stud d->ProgressFrame->hide(); d->ProgressDetailLineEdit->hide(); - d->restoreSearchIcon(); + d->configureSearchIcon(); // allow users of this widget to know that the process has finished emit directoryImported(); @@ -2915,165 +2916,185 @@ void ctkDICOMVisualBrowserWidget::onQueryRetrieveOptionToggled(bool toggled) Q_D(ctkDICOMVisualBrowserWidget); QSettings settings; settings.setValue("DICOM/QueryRetrieveEnabled", toggled); - d->restoreSearchIcon(); + d->configureSearchIcon(); } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(const QVariant& data) +void ctkDICOMVisualBrowserWidget::updateGUIFromScheduler(QList datas) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) - { - d->updateFiltersWarnings(); - return; - } - else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies || - td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries) + bool updatePatients = false; + foreach (QVariant data, datas) { + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + d->updateFiltersWarnings(); + continue; + } + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies || + td.JobType == ctkDICOMJobResponseSet::JobType::QuerySeries) + { + d->updateFiltersWarnings(); + continue; + } + else if (td.JobType != ctkDICOMJobResponseSet::JobType::QueryPatients) + { + continue; + } + d->updateFiltersWarnings(); - return; - } - else if (td.JobType != ctkDICOMJobResponseSet::JobType::QueryPatients) - { - return; + if (td.NumberOfDataSets == 0) + { + d->WarningPushButton->setText(tr("The patients query provided no results. Please refine your filters.")); + d->WarningPushButton->show(); + d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); + } + else + { + d->WarningPushButton->hide(); + updatePatients = true; + } } - d->updateFiltersWarnings(); - if (td.NumberOfDataSets == 0) - { - d->WarningPushButton->setText(tr("The patients query provided no results. Please refine your filters.")); - d->WarningPushButton->show(); - d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); - } - else + if (updatePatients) { - d->WarningPushButton->hide(); + d->createPatients(); } - - d->createPatients(); } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onJobStarted(const QVariant& data) +void ctkDICOMVisualBrowserWidget::onJobStarted(QList datas) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + continue; + } - if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) - { - d->updateFiltersWarnings(); - d->SearchMenuButton->setIcon(QIcon(":/Icons/wait.svg")); - } - else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) - { - QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); - int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); - ctkDICOMPatientItemWidget* patientItemWidget = - qobject_cast(d->PatientsTabWidget->widget(patientIndex)); - if (patientItemWidget) + if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) { - patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::InProgress); + d->updateFiltersWarnings(); + d->SearchMenuButton->setIcon(QIcon(":/Icons/wait.svg")); + } + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) + { + QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); + int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); + ctkDICOMPatientItemWidget* patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (patientItemWidget) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::InProgress); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_pending.svg")); } - d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_pending.svg")); } } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onJobUserStopped(const QVariant& data) +void ctkDICOMVisualBrowserWidget::onJobUserStopped(QList datas) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + continue; + } - if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) - { - d->updateFiltersWarnings(); - d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); - } - else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) - { - QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); - int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); - ctkDICOMPatientItemWidget* patientItemWidget = - qobject_cast(d->PatientsTabWidget->widget(patientIndex)); - if (patientItemWidget) + if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) { - patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Failed); + d->updateFiltersWarnings(); + d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); + } + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) + { + QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); + int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); + ctkDICOMPatientItemWidget* patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (patientItemWidget) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Failed); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_failed.svg")); } - d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_failed.svg")); } } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onJobFailed(const QVariant& data) +void ctkDICOMVisualBrowserWidget::onJobFailed(QList datas) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + continue; + } - if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) - { - d->updateFiltersWarnings(); - d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); - d->WarningPushButton->setText(tr("The patients query failed. Please check the servers settings.")); - d->WarningPushButton->show(); - } - else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) - { - QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); - int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); - ctkDICOMPatientItemWidget* patientItemWidget = - qobject_cast(d->PatientsTabWidget->widget(patientIndex)); - if (patientItemWidget) + if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) + { + d->updateFiltersWarnings(); + d->SearchMenuButton->setIcon(QIcon(":/Icons/query_failed.svg")); + d->WarningPushButton->setText(tr("The patients query failed. Please check the servers settings.")); + d->WarningPushButton->show(); + } + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) { - patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Failed); + QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); + int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); + ctkDICOMPatientItemWidget* patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (patientItemWidget) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Failed); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_failed.svg")); } - d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_failed.svg")); } } //------------------------------------------------------------------------------ -void ctkDICOMVisualBrowserWidget::onJobFinished(const QVariant& data) +void ctkDICOMVisualBrowserWidget::onJobFinished(QList datas) { Q_D(ctkDICOMVisualBrowserWidget); - ctkDICOMJobDetail td = data.value(); - if (td.JobUID.isEmpty()) + foreach (QVariant data, datas) { - return; - } + ctkDICOMJobDetail td = data.value(); + if (td.JobUID.isEmpty()) + { + continue; + } - if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) - { - d->updateFiltersWarnings(); - d->SearchMenuButton->setIcon(QIcon(":/Icons/query_success.svg")); - } - else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) - { - QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); - int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); - ctkDICOMPatientItemWidget* patientItemWidget = - qobject_cast(d->PatientsTabWidget->widget(patientIndex)); - if (patientItemWidget) + if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) { - patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Completed); + d->updateFiltersWarnings(); + d->SearchMenuButton->setIcon(QIcon(":/Icons/query_success.svg")); + } + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) + { + QString patientItem = d->findPatientItemFromPatientInfo(td.PatientID); + int patientIndex = d->findPatientTabIndexFromPatientItem(patientItem); + ctkDICOMPatientItemWidget* patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(patientIndex)); + if (patientItemWidget) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Completed); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_success.svg")); } - d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_success.svg")); } } diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index a3b0fc8a0c..4e6f32f4f2 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -79,7 +79,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget 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 numberOfOpenedStudiesPerPatient READ numberOfOpenedStudiesPerPatient WRITE setNumberOfOpenedStudiesPerPatient); Q_PROPERTY(ctkDICOMStudyItemWidget::ThumbnailSizeOption thumbnailSize READ thumbnailSize WRITE setThumbnailSize); Q_PROPERTY(ctkDICOMVisualBrowserWidget::ImportDirectoryMode ImportDirectoryMode READ importDirectoryMode WRITE setImportDirectoryMode) Q_PROPERTY(bool sendActionVisible READ isSendActionVisible WRITE setSendActionVisible) @@ -220,8 +220,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMVisualBrowserWidget : public QWidget ///@{ /// Number of non collapsed studies per patient /// 2 by default - void setNumberOfStudiesPerPatient(int numberOfStudiesPerPatient); - int numberOfStudiesPerPatient() const; + void setNumberOfOpenedStudiesPerPatient(int numberOfOpenedStudiesPerPatient); + int numberOfOpenedStudiesPerPatient() const; ///@} ///@{ @@ -368,11 +368,11 @@ public Q_SLOTS: ///@{ /// update GUI after query/retrieve operations - void updateGUIFromScheduler(const QVariant&); - void onJobStarted(const QVariant&); - void onJobUserStopped(const QVariant&); - void onJobFailed(const QVariant&); - void onJobFinished(const QVariant&); + void updateGUIFromScheduler(QList); + void onJobStarted(QList); + void onJobUserStopped(QList); + void onJobFailed(QList); + void onJobFinished(QList); ///@} /// stops all the operations