Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser thumbnails #1063

Merged
merged 3 commits into from
Jan 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions Libs/DICOM/Widgets/Resources/UI/ctkDICOMObjectListWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>442</width>
<height>311</height>
<height>514</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -51,8 +51,8 @@
</layout>
</item>
<item>
<widget class="ctkSliderWidget" name="fileSliderWidget" native="true">
<property name="decimals" stdset="0">
<widget class="ctkSliderWidget" name="fileSliderWidget">
<property name="decimals">
<number>0</number>
</property>
</widget>
Expand Down Expand Up @@ -106,20 +106,46 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item>
<widget class="QTreeView" name="dcmObjectTreeView">
<property name="toolTip">
<string>Double-click to show DICOM tag definition.</string>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
</property>
</widget>
<item>
<widget class="QTreeView" name="dcmObjectTreeView">
<property name="toolTip">
<string>Double-click to show DICOM tag definition.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="thumbnailLabel">
<property name="text">
<string>Thumbnail</string>
</property>
</widget>
</item>
<item>
<widget class="ctkExpandButton" name="showThumbnailButton">
<property name="toolTip">
<string>Show image thumbnail</string>
</property>
<property name="mirrorOnExpand">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ctkExpandButton</class>
<extends>QToolButton</extends>
<header>ctkExpandButton.h</header>
</customwidget>
<customwidget>
<class>ctkSearchBox</class>
<extends>QLineEdit</extends>
Expand Down
5 changes: 5 additions & 0 deletions Libs/DICOM/Widgets/ctkDICOMBrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <QCloseEvent>
#include <QComboBox>
#include <QDebug>
#include <QDesktopWidget>
#include <QDialogButtonBox>
#include <QFile>
#include <QFileInfo>
Expand Down Expand Up @@ -105,6 +106,10 @@ class ctkDICOMMetadataDialog : public QDialog
if (!savedGeometry.isEmpty())
{
this->restoreGeometry(savedGeometry);
if (this->isMaximized())
{
this->setGeometry(QApplication::desktop()->availableGeometry(this));
}
}
}

Expand Down
54 changes: 51 additions & 3 deletions Libs/DICOM/Widgets/ctkDICOMObjectListWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@

// ctkDICOMWidgets includes
#include "ctkDICOMObjectListWidget.h"
#include "ctkDICOMThumbnailGenerator.h"
#include "ui_ctkDICOMObjectListWidget.h"

// Qt includes
#include <QApplication>
#include <QClipboard>
#include <QDesktopServices>
#include <QImage>
#include <QSortFilterProxyModel>
#include <QString>
#include <QStringList>
Expand Down Expand Up @@ -101,6 +103,7 @@ class ctkDICOMObjectListWidgetPrivate: public Ui_ctkDICOMObjectListWidget
ctkDICOMObjectModel* dicomObjectModel;
qRecursiveTreeProxyFilter* filterModel;
QString filterExpression;
bool thumbnailVisible{true};
};

//----------------------------------------------------------------------------
Expand Down Expand Up @@ -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&)),
Expand All @@ -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)));
}

//----------------------------------------------------------------------------
Expand All @@ -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);
Expand Down Expand Up @@ -307,7 +314,19 @@ void ctkDICOMObjectListWidget::updateWidget()
d->currentFile = d->fileList[static_cast<int>(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()
Expand Down Expand Up @@ -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;
}
4 changes: 4 additions & 0 deletions Libs/DICOM/Widgets/ctkDICOMObjectListWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<ctkDICOMObjectListWidgetPrivate> d_ptr;

Expand All @@ -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&);
Expand Down
36 changes: 32 additions & 4 deletions Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
6 changes: 6 additions & 0 deletions Libs/DICOM/Widgets/ctkDICOMThumbnailGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 6 additions & 22 deletions Libs/Widgets/ctkExpandButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class ctkExpandButtonPrivate
bool mirrorOnExpand;
QPixmap defaultPixmap;
Qt::Orientation orientation;
Qt::LayoutDirection direction;
};

//-----------------------------------------------------------------------------
Expand All @@ -45,7 +44,6 @@ ctkExpandButtonPrivate::ctkExpandButtonPrivate(ctkExpandButton &object)
{
this->mirrorOnExpand = false;
this->orientation = Qt::Horizontal;
this->direction = Qt::LeftToRight;
}

//-----------------------------------------------------------------------------
Expand All @@ -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()));
}

//-----------------------------------------------------------------------------
Expand All @@ -80,7 +80,7 @@ void ctkExpandButton::setMirrorOnExpand(bool newBehavior)
{
Q_D(ctkExpandButton);
d->mirrorOnExpand = newBehavior;
this->updateIcon(d->direction);
this->updateIcon();
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -115,7 +115,7 @@ void ctkExpandButton::setOrientation(Qt::Orientation newOrientation)
QStyle::SP_ToolBarVerticalExtensionButton, &opt);
d->orientation = Qt::Vertical;
}
this->updateIcon(d->direction);
this->updateIcon();
}

//-----------------------------------------------------------------------------
Expand All @@ -126,36 +126,20 @@ 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
{
QImage mirrorImage =
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();
}
5 changes: 1 addition & 4 deletions Libs/Widgets/ctkExpandButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ctkExpandButtonPrivate> d_ptr;
Expand Down