diff --git a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMObjectListWidget.ui b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMObjectListWidget.ui index 29020c49a1..07f4235181 100644 --- a/Libs/DICOM/Widgets/Resources/UI/ctkDICOMObjectListWidget.ui +++ b/Libs/DICOM/Widgets/Resources/UI/ctkDICOMObjectListWidget.ui @@ -7,7 +7,7 @@ 0 0 442 - 311 + 514 @@ -51,8 +51,8 @@ - - + + 0 @@ -106,20 +106,46 @@ - - - - - - Double-click to show DICOM tag definition. + + + 0 - + + + + Double-click to show DICOM tag definition. + + + + + + + Thumbnail + + + + + + + Show image thumbnail + + + true + + + + + + ctkExpandButton + QToolButton +
ctkExpandButton.h
+
ctkSearchBox QLineEdit diff --git a/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp b/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp index 8d958eef09..65b7c1e951 100644 --- a/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMBrowser.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,10 @@ class ctkDICOMMetadataDialog : public QDialog if (!savedGeometry.isEmpty()) { this->restoreGeometry(savedGeometry); + if (this->isMaximized()) + { + this->setGeometry(QApplication::desktop()->availableGeometry(this)); + } } } diff --git a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp index 99304ea20c..1648031e4a 100644 --- a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp @@ -20,12 +20,14 @@ // ctkDICOMWidgets includes #include "ctkDICOMObjectListWidget.h" +#include "ctkDICOMThumbnailGenerator.h" #include "ui_ctkDICOMObjectListWidget.h" // Qt includes #include #include #include +#include #include #include #include @@ -101,6 +103,7 @@ class ctkDICOMObjectListWidgetPrivate: public Ui_ctkDICOMObjectListWidget ctkDICOMObjectModel* dicomObjectModel; qRecursiveTreeProxyFilter* filterModel; QString filterExpression; + bool thumbnailVisible{true}; }; //---------------------------------------------------------------------------- @@ -212,6 +215,9 @@ ctkDICOMObjectListWidget::ctkDICOMObjectListWidget(QWidget* _parent):Superclass( d->fileSliderWidget->setMinimum(1); d->fileSliderWidget->setPageStep(1); + d->showThumbnailButton->setChecked(d->thumbnailVisible); + d->thumbnailLabel->setVisible(d->thumbnailVisible); + d->currentPathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); connect(d->fileSliderWidget, SIGNAL(valueChanged(double)), this, SLOT(updateWidget())); connect(d->dcmObjectTreeView, SIGNAL(doubleClicked(const QModelIndex&)), @@ -225,6 +231,8 @@ ctkDICOMObjectListWidget::ctkDICOMObjectListWidget(QWidget* _parent):Superclass( QObject::connect(d->metadataSearchBox, SIGNAL(textChanged(QString)), this, SLOT(setFilterExpression(QString))); QObject::connect(d->metadataSearchBox, SIGNAL(textChanged(QString)), this, SLOT(onFilterChanged())); + + QObject::connect(d->showThumbnailButton, SIGNAL(toggled(bool)), this, SLOT(setThumbnailVisible(bool))); } //---------------------------------------------------------------------------- @@ -250,10 +258,9 @@ void ctkDICOMObjectListWidget::setFileList(const QStringList& fileList) if (d->fileList.size() > 0) { d->currentFile = d->fileList[0]; - - d->populateDICOMObjectTreeView(d->currentFile); d->fileSliderWidget->setMaximum(fileList.size()); d->fileSliderWidget->setSuffix(QString(" / %1").arg(fileList.size())); + this->updateWidget(); for (int columnIndex = 0; columnIndex < d->dicomObjectModel->columnCount(); ++columnIndex) { d->dcmObjectTreeView->resizeColumnToContents(columnIndex); @@ -307,7 +314,19 @@ void ctkDICOMObjectListWidget::updateWidget() d->currentFile = d->fileList[static_cast(d->fileSliderWidget->value())-1]; d->setPathLabel(d->currentFile); d->populateDICOMObjectTreeView(d->currentFile); - } + + if (this->isThumbnailVisible()) + { + // only update the thumbnail if visible for better update performance + ctkDICOMThumbnailGenerator thumbnailGenerator; + QImage thumbnailImage; + if (!thumbnailGenerator.generateThumbnail(d->currentFile, thumbnailImage)) + { + thumbnailGenerator.generateBlankThumbnail(thumbnailImage); + } + d->thumbnailLabel->setPixmap(QPixmap::fromImage(thumbnailImage)); + } +} // -------------------------------------------------------------------------- void ctkDICOMObjectListWidget::copyPath() @@ -406,3 +425,32 @@ QString ctkDICOMObjectListWidget::filterExpression() Q_D(ctkDICOMObjectListWidget); return d->filterExpression; } + +//------------------------------------------------------------------------------ +void ctkDICOMObjectListWidget::setThumbnailVisible(bool visible) +{ + Q_D(ctkDICOMObjectListWidget); + if (visible == d->thumbnailVisible) + { + // no change + return; + } + d->thumbnailVisible = visible; + + QSignalBlocker blocker(d->showThumbnailButton); + d->showThumbnailButton->setChecked(visible); + + d->thumbnailLabel->setVisible(visible); + if (visible) + { + // Previously the thumbnail was not visible, so it was not updated. Update it now. + this->updateWidget(); + } +} + +//------------------------------------------------------------------------------ +bool ctkDICOMObjectListWidget::isThumbnailVisible()const +{ + Q_D(const ctkDICOMObjectListWidget); + return d->thumbnailVisible; +} diff --git a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.h b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.h index 5e96592d19..b7bd501451 100644 --- a/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.h +++ b/Libs/DICOM/Widgets/ctkDICOMObjectListWidget.h @@ -36,6 +36,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMObjectListWidget : public QWidget Q_PROPERTY(QString currentFile READ currentFile WRITE setCurrentFile) Q_PROPERTY(QStringList fileList READ fileList WRITE setFileList) Q_PROPERTY(QString filterExpression READ filterExpression WRITE setFilterExpression) + Q_PROPERTY(bool thumbnailVisible READ isThumbnailVisible WRITE setThumbnailVisible) public: typedef QWidget Superclass; @@ -58,6 +59,8 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMObjectListWidget : public QWidget /// Open DICOM tag definition in a web browser void openLookupUrl(QString tag); + bool isThumbnailVisible()const; + protected: QScopedPointer d_ptr; @@ -72,6 +75,7 @@ public Q_SLOTS: void setCurrentFile(const QString& newFileName); void setFileList(const QStringList& fileList); void setFilterExpression(const QString& expr); + void setThumbnailVisible(bool visible); protected Q_SLOTS: void itemDoubleClicked(const QModelIndex&); diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp index 2c0a07cbe8..3439f3d1b0 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp @@ -123,11 +123,10 @@ void ctkDICOMThumbnailGenerator::setSmoothResize(bool on) } //------------------------------------------------------------------------------ -bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const QString &path) +bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, QImage& image) { Q_D(ctkDICOMThumbnailGenerator); - QImage image; // Check whether we have a valid image EI_Status result = dcmImage->getStatus(); if (result != EIS_Normal) @@ -184,14 +183,43 @@ bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const Q return false; } } - image.scaled( d->Width, d->Height, Qt::KeepAspectRatio, - (d->SmoothResize ? Qt::SmoothTransformation : Qt::FastTransformation) ).save(path,"PNG"); + image = image.scaled( d->Width, d->Height, Qt::KeepAspectRatio, + (d->SmoothResize ? Qt::SmoothTransformation : Qt::FastTransformation) ); return true; } +//------------------------------------------------------------------------------ +bool ctkDICOMThumbnailGenerator::generateThumbnail(DicomImage *dcmImage, const QString &path) +{ + QImage image; + if (this->generateThumbnail(dcmImage, image)) + { + return image.save(path,"PNG"); + } + return false; +} + +//------------------------------------------------------------------------------ +bool ctkDICOMThumbnailGenerator::generateThumbnail(const QString dcmImagePath, QImage& image) +{ + DicomImage dcmImage(QDir::toNativeSeparators(dcmImagePath).toUtf8()); + return this->generateThumbnail(&dcmImage, image); +} + //------------------------------------------------------------------------------ bool ctkDICOMThumbnailGenerator::generateThumbnail(const QString dcmImagePath, const QString& thumbnailPath) { DicomImage dcmImage(QDir::toNativeSeparators(dcmImagePath).toUtf8()); return this->generateThumbnail(&dcmImage, thumbnailPath); } + +//------------------------------------------------------------------------------ +void ctkDICOMThumbnailGenerator::generateBlankThumbnail(QImage& image) +{ + Q_D(ctkDICOMThumbnailGenerator); + if (image.width() != d->Width || image.height() != d->Height) + { + image = QImage(d->Width, d->Height, QImage::Format_RGB32); + } + image.fill(Qt::darkGray); +} diff --git a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h index e556e14250..61c1aa3aaf 100644 --- a/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h +++ b/Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h @@ -47,8 +47,14 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailGenerator : public ctkDICOMAbstr virtual bool generateThumbnail(DicomImage* dcmImage, const QString& path); + Q_INVOKABLE bool generateThumbnail(DicomImage *dcmImage, QImage& image); + Q_INVOKABLE bool generateThumbnail(const QString dcmImagePath, QImage& image); Q_INVOKABLE bool generateThumbnail(const QString dcmImagePath, const QString& thumbnailPath); + /// Generate a blank thumbnail image (currently a solid gray box of the requested thumbnail size). + /// It can be used as a placeholder for invalid images or duringan image is loaded. + Q_INVOKABLE void generateBlankThumbnail(QImage& image); + /// Set thumbnail width void setWidth(int width); /// Get thumbnail width diff --git a/Libs/Widgets/ctkExpandButton.cpp b/Libs/Widgets/ctkExpandButton.cpp index 7eea69c9cd..7d46c65df4 100644 --- a/Libs/Widgets/ctkExpandButton.cpp +++ b/Libs/Widgets/ctkExpandButton.cpp @@ -36,7 +36,6 @@ class ctkExpandButtonPrivate bool mirrorOnExpand; QPixmap defaultPixmap; Qt::Orientation orientation; - Qt::LayoutDirection direction; }; //----------------------------------------------------------------------------- @@ -45,7 +44,6 @@ ctkExpandButtonPrivate::ctkExpandButtonPrivate(ctkExpandButton &object) { this->mirrorOnExpand = false; this->orientation = Qt::Horizontal; - this->direction = Qt::LeftToRight; } //----------------------------------------------------------------------------- @@ -56,6 +54,8 @@ void ctkExpandButtonPrivate::init() q->setOrientation(Qt::Horizontal); q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); q->setCheckable(true); + + QObject::connect(q, SIGNAL(toggled(bool)), q, SLOT(updateIcon())); } //----------------------------------------------------------------------------- @@ -80,7 +80,7 @@ void ctkExpandButton::setMirrorOnExpand(bool newBehavior) { Q_D(ctkExpandButton); d->mirrorOnExpand = newBehavior; - this->updateIcon(d->direction); + this->updateIcon(); } //----------------------------------------------------------------------------- @@ -115,7 +115,7 @@ void ctkExpandButton::setOrientation(Qt::Orientation newOrientation) QStyle::SP_ToolBarVerticalExtensionButton, &opt); d->orientation = Qt::Vertical; } - this->updateIcon(d->direction); + this->updateIcon(); } //----------------------------------------------------------------------------- @@ -126,15 +126,14 @@ Qt::Orientation ctkExpandButton::orientation() const } //----------------------------------------------------------------------------- -void ctkExpandButton::updateIcon(Qt::LayoutDirection newDirection) +void ctkExpandButton::updateIcon() { Q_D(ctkExpandButton); // If the orientation is vertical, UpToBottom is LeftToRight and // BottomToUp is RightToLeft. Rotate 90' clockwise. - if(newDirection == Qt::LeftToRight) + if(!d->mirrorOnExpand || !this->isChecked()) { this->setIcon(QIcon(d->defaultPixmap)); - d->direction = Qt::LeftToRight; } else { @@ -142,20 +141,5 @@ void ctkExpandButton::updateIcon(Qt::LayoutDirection newDirection) d->defaultPixmap.toImage().mirrored(d->orientation == Qt::Horizontal, d->orientation == Qt::Vertical); this->setIcon(QIcon(QPixmap::fromImage(mirrorImage))); - d->direction = Qt::RightToLeft; } } - -//----------------------------------------------------------------------------- -void ctkExpandButton::nextCheckState() -{ - Q_D(ctkExpandButton); - if (d->mirrorOnExpand) - { - Qt::LayoutDirection newDirection = - this->isChecked() ? Qt::LeftToRight : Qt::RightToLeft; - this->updateIcon(newDirection); - } - - return this->Superclass::nextCheckState(); -} diff --git a/Libs/Widgets/ctkExpandButton.h b/Libs/Widgets/ctkExpandButton.h index c7c05d89ae..51e2a10982 100644 --- a/Libs/Widgets/ctkExpandButton.h +++ b/Libs/Widgets/ctkExpandButton.h @@ -62,10 +62,7 @@ class CTK_WIDGETS_EXPORT ctkExpandButton virtual QSize sizeHint() const; private Q_SLOTS: - void updateIcon(Qt::LayoutDirection newDirection); - -protected: - virtual void nextCheckState(); + void updateIcon(); protected: QScopedPointer d_ptr;