diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/patient_failed.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_failed.svg new file mode 100644 index 0000000000..75d95bd770 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_failed.svg @@ -0,0 +1,42 @@ + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/patient_pending.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_pending.svg new file mode 100644 index 0000000000..e24e5cdff2 --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_pending.svg @@ -0,0 +1,42 @@ + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/Icons/patient_success.svg b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_success.svg new file mode 100644 index 0000000000..8f97c7aa1c --- /dev/null +++ b/Libs/DICOM/Widgets/Resources/UI/Icons/patient_success.svg @@ -0,0 +1,42 @@ + + + + + + + diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc index 0651ac4304..c9b8a3bbb7 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMWidget.qrc @@ -32,5 +32,8 @@ Icons/search_local.svg Icons/query.svg Icons/wait.svg + Icons/patient_failed.svg + Icons/patient_pending.svg + Icons/patient_success.svg diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp index b62ec9dff7..3879a024a0 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.cpp @@ -99,7 +99,8 @@ class ctkDICOMPatientItemWidgetPrivate : public Ui_ctkDICOMPatientItemWidget QMap StudyItemWidgetsConnectionMap; QSpacerItem* StudiesListVerticalSpacer; - QStringList AllowedServers = QStringList(); + QStringList AllowedServers; + ctkDICOMPatientItemWidget::OperationStatus Status; bool IsGUIUpdating; bool QueryOn; @@ -131,6 +132,9 @@ ctkDICOMPatientItemWidgetPrivate::ctkDICOMPatientItemWidgetPrivate(ctkDICOMPatie this->StudiesListVerticalSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Expanding); + this->AllowedServers = QStringList(); + this->Status = ctkDICOMPatientItemWidget::NoOperation; + this->IsGUIUpdating = false; this->QueryOn = true; this->RetrieveOn = true; @@ -580,6 +584,8 @@ ctkDICOMPatientItemWidget::~ctkDICOMPatientItemWidget() //------------------------------------------------------------------------------ CTK_SET_CPP(ctkDICOMPatientItemWidget, const QStringList&, setAllowedServers, AllowedServers); CTK_GET_CPP(ctkDICOMPatientItemWidget, QStringList, allowedServers, AllowedServers); +CTK_SET_CPP(ctkDICOMPatientItemWidget, const OperationStatus&, setOperationStatus, Status); +CTK_GET_CPP(ctkDICOMPatientItemWidget, ctkDICOMPatientItemWidget::OperationStatus, operationStatus, Status); CTK_GET_CPP(ctkDICOMPatientItemWidget, QString, patientItem, PatientItem); CTK_SET_CPP(ctkDICOMPatientItemWidget, const QString&, setPatientID, PatientID); CTK_GET_CPP(ctkDICOMPatientItemWidget, QString, patientID, PatientID); diff --git a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h index 9856795832..cdfad76798 100644 --- a/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMPatientItemWidget.h @@ -50,6 +50,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget Q_PROPERTY(int numberOfStudiesPerPatient READ numberOfStudiesPerPatient WRITE setNumberOfStudiesPerPatient); 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); public: typedef QWidget Superclass; @@ -189,6 +190,21 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMPatientItemWidget : public QWidget Q_INVOKABLE void updateAllowedServersUIFromDB(); ///@} + enum OperationStatus + { + NoOperation = 0, + InProgress, + Completed, + Failed, + }; + + ///@{ + /// Set the operation status + /// NoOperation by default + void setOperationStatus(const OperationStatus& status); + OperationStatus operationStatus() const; + ///@} + public Q_SLOTS: void generateStudies(bool query = true, bool retrieve = true); void generateSeriesAtToggle(bool toggled = true, const QString& studyItem = ""); diff --git a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp index 323e339ca1..77ae9ca404 100644 --- a/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMSeriesItemWidget.cpp @@ -986,7 +986,7 @@ void ctkDICOMSeriesItemWidget::onJobUserStopped(const QVariant &data) td.SeriesInstanceUID == d->SeriesInstanceUID) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::Failed); - d->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/error.svg")); + d->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/error_red.svg")); if (td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { @@ -1015,7 +1015,7 @@ void ctkDICOMSeriesItemWidget::onJobFailed(const QVariant &data) td.SeriesInstanceUID == d->SeriesInstanceUID) { d->SeriesThumbnail->setOperationStatus(ctkThumbnailLabel::Failed); - d->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/error.svg")); + d->SeriesThumbnail->setStatusIcon(QIcon(":/Icons/error_red.svg")); if (td.JobType == ctkDICOMJobResponseSet::JobType::RetrieveSOPInstance) { diff --git a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp index 7484ec147f..bfd984d90c 100644 --- a/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMStudyItemWidget.cpp @@ -813,7 +813,7 @@ void ctkDICOMStudyItemWidget::onJobUserStopped(const QVariant &data) td.StudyInstanceUID == d->StudyInstanceUID) { d->Status = ctkDICOMStudyItemWidget::Failed; - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error.svg")); + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); } } @@ -829,7 +829,7 @@ void ctkDICOMStudyItemWidget::onJobFailed(const QVariant &data) td.StudyInstanceUID == d->StudyInstanceUID) { d->Status = ctkDICOMStudyItemWidget::Failed; - d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error.svg")); + d->OperationStatusPushButton->setIcon(QIcon(":/Icons/error_red.svg")); } } diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 714916a0ab..98c54274c8 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -157,9 +157,9 @@ class ctkDICOMVisualBrowserWidgetPrivate : public Ui_ctkDICOMVisualBrowserWidget void retrieveSeries(); bool updateServer(ctkDICOMServer* server); void removeAllPatientItemWidgets(); - QString findPatientItemFromPatientInfo(const QString& patientID, - const QString& patientName, - const QString& patientBirthDate); + QString findPatientItemFromPatientInfo(const QString& patientID = "", + const QString& patientName = "", + const QString& patientBirthDate = ""); int findPatientTabIndexFromPatientItem(const QString& patientItem); void updateSeriesTablesSelection(ctkDICOMSeriesItemWidget* selectedSeriesItemWidget); QStringList getPatientItemsFromWidgets(ctkDICOMModel::IndexType level, @@ -416,6 +416,9 @@ void ctkDICOMVisualBrowserWidgetPrivate::init() QObject::connect(this->PatientsTabWidget, SIGNAL(currentChanged(int)), q, SLOT(onPatientItemChanged(int))); + QObject::connect(this->PatientsTabWidget, SIGNAL(tabBarClicked(int)), + q, SLOT(onOperationStatusTabBarItemClicked(int))); + QObject::connect(this->ServerNodeWidget, SIGNAL(serversSettingsChanged()), q, SLOT(onServersSettingsChanged())); @@ -1134,6 +1137,11 @@ QString ctkDICOMVisualBrowserWidgetPrivate::findPatientItemFromPatientInfo(const const QString &patientBirthDate) { QString patientItem; + if (patientID.isEmpty() && patientName.isEmpty() && patientBirthDate.isEmpty()) + { + return patientItem; + } + for (int index = 0; index < this->PatientsTabWidget->count(); ++index) { ctkDICOMPatientItemWidget* patientItemWidget = @@ -1142,12 +1150,12 @@ QString ctkDICOMVisualBrowserWidgetPrivate::findPatientItemFromPatientInfo(const { continue; } - if (patientItemWidget->patientID() == patientID && - patientItemWidget->patientName() == patientName && - patientItemWidget->patientBirthDate() == patientBirthDate) + + if ((patientID.isEmpty() || patientItemWidget->patientID() == patientID) && + (patientName.isEmpty() || patientItemWidget->patientName() == patientName) && + (patientBirthDate.isEmpty() || patientItemWidget->patientBirthDate() == patientBirthDate)) { - patientItem = this->PatientsTabWidget->tabWhatsThis(index); - break; + return this->PatientsTabWidget->tabWhatsThis(index); } } @@ -2953,12 +2961,28 @@ void ctkDICOMVisualBrowserWidget::onJobStarted(const QVariant& data) Q_D(ctkDICOMVisualBrowserWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) + if (td.JobUID.isEmpty()) + { + return; + } + + 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) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::InProgress); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_pending.svg")); + } } //------------------------------------------------------------------------------ @@ -2967,12 +2991,28 @@ void ctkDICOMVisualBrowserWidget::onJobUserStopped(const QVariant& data) Q_D(ctkDICOMVisualBrowserWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) + if (td.JobUID.isEmpty()) + { + return; + } + + 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) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Failed); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_failed.svg")); + } } //------------------------------------------------------------------------------ @@ -2981,20 +3021,30 @@ void ctkDICOMVisualBrowserWidget::onJobFailed(const QVariant& data) Q_D(ctkDICOMVisualBrowserWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) + if (td.JobUID.isEmpty()) + { + return; + } + + 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(); } - - if (td.JobType != ctkDICOMJobResponseSet::JobType::QueryPatients) + else if (td.JobType == ctkDICOMJobResponseSet::JobType::QueryStudies) { - return; + 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->WarningPushButton->setText(tr("The patients query failed. Please check the servers settings.")); - d->WarningPushButton->show(); } //------------------------------------------------------------------------------ @@ -3003,12 +3053,28 @@ void ctkDICOMVisualBrowserWidget::onJobFinished(const QVariant& data) Q_D(ctkDICOMVisualBrowserWidget); ctkDICOMJobDetail td = data.value(); - if (!td.JobUID.isEmpty() && - td.JobType == ctkDICOMJobResponseSet::JobType::QueryPatients) + if (td.JobUID.isEmpty()) + { + return; + } + + 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) + { + patientItemWidget->setOperationStatus(ctkDICOMPatientItemWidget::Completed); + } + d->PatientsTabWidget->setTabIcon(patientIndex, QIcon(":/Icons/patient_success.svg")); + } } //------------------------------------------------------------------------------ @@ -3025,6 +3091,34 @@ void ctkDICOMVisualBrowserWidget::onPatientItemChanged(int index) patientItem->generateStudies(); } +//------------------------------------------------------------------------------ +void ctkDICOMVisualBrowserWidget::onOperationStatusTabBarItemClicked(int index) +{ + Q_D(ctkDICOMVisualBrowserWidget); + ctkDICOMPatientItemWidget* patientItemWidget = + qobject_cast(d->PatientsTabWidget->widget(index)); + if (!patientItemWidget) + { + return; + } + + ctkDICOMPatientItemWidget::OperationStatus status = patientItemWidget->operationStatus(); + if (status == ctkDICOMPatientItemWidget::InProgress) + { + d->Scheduler->stopJobsByDICOMUIDs(QStringList(patientItemWidget->patientID())); + } + else if (status == ctkDICOMPatientItemWidget::Failed) + { + ctkDICOMJobDetail queryJobDetail; + queryJobDetail.JobClass = "ctkDICOMQueryJob"; + queryJobDetail.DICOMLevel = ctkDICOMJob::DICOMLevels::Studies; + queryJobDetail.PatientID = patientItemWidget->patientID(); + + d->Scheduler->runJob(queryJobDetail, patientItemWidget->allowedServers()); + } + +} + //------------------------------------------------------------------------------ void ctkDICOMVisualBrowserWidget::onServersSettingsChanged() { diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h index 0623c60f00..a3b0fc8a0c 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.h @@ -388,6 +388,9 @@ public Q_SLOTS: /// user change patient selection void onPatientItemChanged(int); + /// user clicked on patient tab item + void onOperationStatusTabBarItemClicked(int); + /// server settings have been changed void onServersSettingsChanged();