Переглянути джерело

Merge branch '272-thumbnails-hidden-in-dicom-browser'

* 272-thumbnails-hidden-in-dicom-browser:
  Relayout ctkThumbnailListWidget when thumbnails become visible
  Factorize ctkThumbnailListWidget/ctkDICOMThumbnailListWidget
  Improve ctkDICOMThumbnailListWidgetTest1
Julien Finet 12 роки тому
батько
коміт
c30f99804b

+ 1 - 1
Libs/DICOM/Widgets/Testing/Cpp/CMakeLists.txt

@@ -38,4 +38,4 @@ SIMPLE_TEST(ctkDICOMModelTest2
   )
 SIMPLE_TEST(ctkDICOMQueryRetrieveWidgetTest1)
 SIMPLE_TEST(ctkDICOMQueryResultsTabWidgetTest1)
-SIMPLE_TEST(ctkDICOMThumbnailListWidgetTest1)
+SIMPLE_TEST(ctkDICOMThumbnailListWidgetTest1 ${CMAKE_CURRENT_BINARY_DIR}/dicom.db ${CMAKE_CURRENT_SOURCE_DIR}/../../../Core/Resources/dicom-sample.sql)

+ 37 - 7
Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMThumbnailListWidgetTest1.cpp

@@ -24,6 +24,8 @@
 #include <QTimer>
 
 // ctkDICOMCore includes
+#include "ctkDICOMDatabase.h"
+#include "ctkDICOMModel.h"
 #include "ctkDICOMThumbnailListWidget.h"
 
 // STD includes
@@ -33,14 +35,42 @@ int ctkDICOMThumbnailListWidgetTest1( int argc, char * argv [] )
 {
   QApplication app(argc, argv);
 
-  ctkDICOMThumbnailListWidget widget;
-  widget.setDatabaseDirectory(QDir::currentPath());
-  widget.show();
-
-  if (argc <= 1 || QString(argv[1]) != "-I")
+  if (argc <= 2)
     {
-    QTimer::singleShot(200, &app, SLOT(quit()));
+    std::cerr << "Warning, no sql file given. Test stops" << std::endl;
+    std::cerr << "Usage: qctkDICOMModelTest1 <scratch.db> <dumpfile.sql>" << std::endl;
+    return EXIT_FAILURE;
     }
 
-  return app.exec();
+  try
+    {
+    ctkDICOMDatabase myCTK( argv[1] );
+
+    if (!myCTK.initializeDatabase(argv[2]))
+      {
+      std::cerr << "Error when initializing the data base: " << argv[2]
+                << " error: " << myCTK.lastError().toStdString();
+      }
+
+    ctkDICOMModel model;
+    model.setDatabase(myCTK.database());
+
+    ctkDICOMThumbnailListWidget widget;
+    //widget.setDatabaseDirectory(QDir::currentPath());
+    widget.setDatabaseDirectory("E:\\work\\CTK\\CTK-VTK-64\\CTK-build\\Libs\\DICOM\\Widgets\\Testing\\Cpp");
+    widget.addThumbnails(model.index(0,0));
+    widget.show();
+
+    if (argc <= 3 || QString(argv[3]) != "-I")
+      {
+      QTimer::singleShot(200, &app, SLOT(quit()));
+      }
+    return app.exec();
+    }
+  catch (...)
+    {
+    std::cerr << "Error" << std::endl;
+    return EXIT_FAILURE;
+    }
+  return EXIT_FAILURE;
 }

+ 3 - 3
Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp

@@ -284,7 +284,7 @@ ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent),
   d->ImportDialog->setWindowModality(Qt::ApplicationModal);
 
   //connect signal and slots
-  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), d->ThumbnailsWidget, SLOT(onModelSelected(QModelIndex)));
+  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), d->ThumbnailsWidget, SLOT(addThumbnails(QModelIndex)));
   connect(d->TreeView, SIGNAL(clicked(QModelIndex)), d->ImagePreview, SLOT(onModelSelected(QModelIndex)));
   connect(d->TreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onModelSelected(QModelIndex)));
 
@@ -550,7 +550,7 @@ void ctkDICOMAppWidget::onThumbnailDoubleClicked(const ctkThumbnailLabel& widget
       {
         this->onModelSelected(index0);
         d->TreeView->setCurrentIndex(index0);
-        d->ThumbnailsWidget->onModelSelected(index0);
+        d->ThumbnailsWidget->addThumbnails(index0);
         d->ImagePreview->onModelSelected(index0);
       }
 }
@@ -868,7 +868,7 @@ void ctkDICOMAppWidget::onSearchParameterChanged(){
 
   this->onModelSelected(d->DICOMModel.index(0,0));
   d->ThumbnailsWidget->clearThumbnails();
-  d->ThumbnailsWidget->onModelSelected(d->DICOMModel.index(0,0));
+  d->ThumbnailsWidget->addThumbnails(d->DICOMModel.index(0,0));
   d->ImagePreview->clearImages();
   d->ImagePreview->onModelSelected(d->DICOMModel.index(0,0));
 }

+ 146 - 149
Libs/DICOM/Widgets/ctkDICOMThumbnailListWidget.cpp

@@ -71,9 +71,9 @@ public:
 
   void addThumbnailWidget(const QModelIndex &imageIndex, const QModelIndex& sourceIndex, const QString& text);
 
-  void onPatientModelSelected(const QModelIndex &index);
-  void onStudyModelSelected(const QModelIndex &index);
-  void onSeriesModelSelected(const QModelIndex &index);
+  void addPatientThumbnails(const QModelIndex& patientIndex);
+  void addStudyThumbnails(const QModelIndex& studyIndex);
+  void addSeriesThumbnails(const QModelIndex& seriesIndex);
 
 private:
   Q_DISABLE_COPY( ctkDICOMThumbnailListWidgetPrivate );
@@ -83,147 +83,142 @@ private:
 // ctkDICOMThumbnailListWidgetPrivate methods
 
 //----------------------------------------------------------------------------
-ctkDICOMThumbnailListWidgetPrivate::ctkDICOMThumbnailListWidgetPrivate(ctkDICOMThumbnailListWidget* parent):
-  Superclass(parent)
+ctkDICOMThumbnailListWidgetPrivate
+::ctkDICOMThumbnailListWidgetPrivate(ctkDICOMThumbnailListWidget* parent)
+  : Superclass(parent)
 {
 
 }
 
 //----------------------------------------------------------------------------
-void ctkDICOMThumbnailListWidgetPrivate::onPatientModelSelected(const QModelIndex &index){
-    QModelIndex patientIndex = index;
+void ctkDICOMThumbnailListWidgetPrivate
+::addPatientThumbnails(const QModelIndex &index)
+{
+  Q_Q(ctkDICOMThumbnailListWidget);
+  QModelIndex patientIndex = index;
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(
+    qobject_cast<const ctkDICOMModel*>(index.model()));
 
-    if(model)
+  if(model)
     {
-        model->fetchMore(patientIndex);
-        int studyCount = model->rowCount(patientIndex);
+    model->fetchMore(patientIndex);
+    const int studyCount = model->rowCount(patientIndex);
 
-        for(int i=0; i<studyCount; i++)
-        {
-            QModelIndex studyIndex = patientIndex.child(i, 0);
-            QModelIndex seriesIndex = studyIndex.child(0, 0);
-            model->fetchMore(seriesIndex);
-            int imageCount = model->rowCount(seriesIndex);
-            QModelIndex imageIndex = seriesIndex.child(imageCount/2, 0);
-
-            QString thumbnailPath = this->DatabaseDirectory +
-                                    "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-
-            if(QFile(thumbnailPath).exists())
-            {
-                this->addThumbnailWidget(imageIndex, studyIndex, model->data(studyIndex, Qt::DisplayRole).toString());
-            }
-        }
+    for(int i=0; i<studyCount; i++)
+      {
+      QModelIndex studyIndex = patientIndex.child(i, 0);
+      QModelIndex seriesIndex = studyIndex.child(0, 0);
+      model->fetchMore(seriesIndex);
+      const int imageCount = model->rowCount(seriesIndex);
+      QModelIndex imageIndex = seriesIndex.child(imageCount/2, 0);
+      QString study = model->data(studyIndex, Qt::DisplayRole).toString();
+      this->addThumbnailWidget(imageIndex, studyIndex, study);
+      }
     }
 }
 
-void ctkDICOMThumbnailListWidgetPrivate::onStudyModelSelected(const QModelIndex &index){
-    QModelIndex studyIndex = index;
+//----------------------------------------------------------------------------
+void ctkDICOMThumbnailListWidgetPrivate
+::addStudyThumbnails(const QModelIndex &index)
+{
+  Q_Q(ctkDICOMThumbnailListWidget);
+  QModelIndex studyIndex = index;
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
 
-    if(model)
+  if (!model)
     {
-        model->fetchMore(studyIndex);
-        int seriesCount = model->rowCount(studyIndex);
+    return;
+    }
+  model->fetchMore(studyIndex);
+  const int seriesCount = model->rowCount(studyIndex);
 
-        for(int i=0; i<seriesCount; i++)
-        {
-            QModelIndex seriesIndex = studyIndex.child(i, 0);
-            model->fetchMore(seriesIndex);
-            int imageCount = model->rowCount(seriesIndex);
-            QModelIndex imageIndex = seriesIndex.child(imageCount/2, 0);
-
-            QString thumbnailPath = this->DatabaseDirectory +
-                                    "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-
-            if (QFile(thumbnailPath).exists())
-            {
-                this->addThumbnailWidget(imageIndex, seriesIndex, model->data(seriesIndex, Qt::DisplayRole).toString());
-            }
-        }
+  for(int i=0; i<seriesCount; i++)
+    {
+    QModelIndex seriesIndex = studyIndex.child(i, 0);
+    model->fetchMore(seriesIndex);
+    int imageCount = model->rowCount(seriesIndex);
+    QModelIndex imageIndex = seriesIndex.child(imageCount/2, 0);
+    this->addThumbnailWidget(imageIndex, seriesIndex, model->data(seriesIndex, Qt::DisplayRole).toString());
     }
 }
 
-void ctkDICOMThumbnailListWidgetPrivate::onSeriesModelSelected(const QModelIndex &index){
-    QModelIndex studyIndex = index.parent();
-    QModelIndex seriesIndex = index;
+//----------------------------------------------------------------------------
+void ctkDICOMThumbnailListWidgetPrivate
+::addSeriesThumbnails(const QModelIndex &index)
+{
+  Q_Q(ctkDICOMThumbnailListWidget);
+  QModelIndex studyIndex = index.parent();
+  QModelIndex seriesIndex = index;
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
 
-    if(model)
+  if (!model)
     {
-        model->fetchMore(seriesIndex);
-
-        int imageCount = model->rowCount(seriesIndex);
-        logger.debug(QString("Thumbs: %1").arg(imageCount));
-        for (int i = 0 ; i < imageCount ; i++ )
-        {
-            QModelIndex imageIndex = seriesIndex.child(i,0);
+    return;
+    }
+  model->fetchMore(seriesIndex);
 
-            QString thumbnailPath = this->DatabaseDirectory +
-                                    "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
+  const int imageCount = model->rowCount(seriesIndex);
+  logger.debug(QString("Thumbs: %1").arg(imageCount));
+  for (int i = 0 ; i < imageCount ; i++ )
+    {
+    QModelIndex imageIndex = seriesIndex.child(i,0);
 
-            if(QFile(thumbnailPath).exists())
-            {
-                this->addThumbnailWidget(imageIndex, imageIndex, QString("Image %1").arg(i));
-            }
-        }
+    this->addThumbnailWidget(imageIndex, imageIndex, QString("Image %1").arg(i));
     }
 }
 
-void ctkDICOMThumbnailListWidgetPrivate::addThumbnailWidget(const QModelIndex& imageIndex, const QModelIndex& sourceIndex, const QString &text){
-    Q_Q(ctkDICOMThumbnailListWidget);
+//----------------------------------------------------------------------------
+void ctkDICOMThumbnailListWidgetPrivate
+::addThumbnailWidget(const QModelIndex& imageIndex,
+                     const QModelIndex& sourceIndex, const QString &text)
+{
+  Q_Q(ctkDICOMThumbnailListWidget);
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(imageIndex.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(imageIndex.model()));
 
-    if(model)
+  if(!model)
     {
-        QModelIndex seriesIndex = imageIndex.parent();
-        QModelIndex studyIndex = seriesIndex.parent();
-
-        QString thumbnailPath = this->DatabaseDirectory +
-                                "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-
-        ctkThumbnailLabel* widget = new ctkThumbnailLabel(this->ScrollAreaContentWidget);
-
-        QString widgetLabel = text;
-        widget->setText( widgetLabel );
-        QPixmap pix(thumbnailPath);
-        logger.debug("Setting pixmap to " + thumbnailPath);
-        if(this->ThumbnailSize.isValid()){
-          widget->setFixedSize(this->ThumbnailSize);
-        }
-        widget->setPixmap(pix);
+    return;
+    }
+  QModelIndex seriesIndex = imageIndex.parent();
+  QModelIndex studyIndex = seriesIndex.parent();
+
+  QString thumbnailPath = this->DatabaseDirectory +
+                          "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                          model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                          model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
+  if(!QFileInfo(thumbnailPath).exists())
+    {
+    return;
+    }
+  ctkThumbnailLabel* widget = new ctkThumbnailLabel(this->ScrollAreaContentWidget);
 
-        QVariant var;
-        var.setValue(QPersistentModelIndex(sourceIndex));
-        widget->setProperty("sourceIndex", var);
-        this->ScrollAreaContentWidget->layout()->addWidget(widget);
+  QString widgetLabel = text;
+  widget->setText( widgetLabel );
+  QPixmap pix(thumbnailPath);
+  logger.debug("Setting pixmap to " + thumbnailPath);
+  if(this->ThumbnailSize.isValid())
+    {
+    widget->setFixedSize(this->ThumbnailSize);
+    }
+  widget->setPixmap(pix);
 
-        q->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), q, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
-        q->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), q, SIGNAL(selected(ctkThumbnailLabel)));
-        q->connect(widget, SIGNAL(doubleClicked(ctkThumbnailLabel)), q, SIGNAL(doubleClicked(ctkThumbnailLabel)));
+  QVariant var;
+  var.setValue(QPersistentModelIndex(sourceIndex));
+  widget->setProperty("sourceIndex", var);
 
-    }
+  this->addThumbnail(widget);
 }
 
 //----------------------------------------------------------------------------
 // ctkDICOMThumbnailListWidget methods
 
 //----------------------------------------------------------------------------
-ctkDICOMThumbnailListWidget::ctkDICOMThumbnailListWidget(QWidget* _parent):
-  Superclass(new ctkDICOMThumbnailListWidgetPrivate(this), _parent)
+ctkDICOMThumbnailListWidget::ctkDICOMThumbnailListWidget(QWidget* _parent)
+  : Superclass(new ctkDICOMThumbnailListWidgetPrivate(this), _parent)
 {
 
 }
@@ -236,72 +231,74 @@ ctkDICOMThumbnailListWidget::~ctkDICOMThumbnailListWidget()
 
 //----------------------------------------------------------------------------
 void ctkDICOMThumbnailListWidget::setDatabaseDirectory(const QString &directory){
-    Q_D(ctkDICOMThumbnailListWidget);
+  Q_D(ctkDICOMThumbnailListWidget);
 
-    d->DatabaseDirectory = directory;
+  d->DatabaseDirectory = directory;
 }
 
 //----------------------------------------------------------------------------
 void ctkDICOMThumbnailListWidget::selectThumbnailFromIndex(const QModelIndex &index){
-    Q_D(ctkDICOMThumbnailListWidget);
+  Q_D(ctkDICOMThumbnailListWidget);
 
-    if(!d->CurrentSelectedModel.isValid())
-      {
-      return;
-      }
-    if(index.parent() != d->CurrentSelectedModel)
-      {
-      return;
-      }
+  if(!d->CurrentSelectedModel.isValid())
+    {
+    return;
+    }
+  if(index.parent() != d->CurrentSelectedModel)
+    {
+    return;
+    }
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
 
-    if(model)
+  if(model)
     {
-        int count = d->ScrollAreaContentWidget->layout()->count();
+    int count = d->ScrollAreaContentWidget->layout()->count();
 
-        for(int i=0; i<count; i++)
+    for(int i=0; i<count; i++)
+      {
+      ctkThumbnailLabel* thumbnailWidget = qobject_cast<ctkThumbnailLabel*>(d->ScrollAreaContentWidget->layout()->itemAt(i)->widget());
+      if(thumbnailWidget->property("sourceIndex").value<QPersistentModelIndex>() == index)
         {
-            ctkThumbnailLabel* thumbnailWidget = qobject_cast<ctkThumbnailLabel*>(d->ScrollAreaContentWidget->layout()->itemAt(i)->widget());
-            if(thumbnailWidget->property("sourceIndex").value<QPersistentModelIndex>() == index){
-                thumbnailWidget->setSelected(true);
-                d->ScrollArea->ensureWidgetVisible(thumbnailWidget);
-            }
-            else
-            {
-                thumbnailWidget->setSelected(false);
-            }
+        thumbnailWidget->setSelected(true);
+        d->ScrollArea->ensureWidgetVisible(thumbnailWidget);
         }
+      else
+        {
+        thumbnailWidget->setSelected(false);
+        }
+      }
     }
 }
 
 //----------------------------------------------------------------------------
-void ctkDICOMThumbnailListWidget::onModelSelected(const QModelIndex &index){
-    Q_D(ctkDICOMThumbnailListWidget);
+void ctkDICOMThumbnailListWidget::addThumbnails(const QModelIndex &index)
+{
+  Q_D(ctkDICOMThumbnailListWidget);
 
-    this->clearThumbnails();
+  this->clearThumbnails();
 
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
+  ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
 
-    if(model)
+  if(model)
     {
-        QModelIndex index0 = index.sibling(index.row(), 0);
+    QModelIndex index0 = index.sibling(index.row(), 0);
 
-        d->CurrentSelectedModel = index0;
+    d->CurrentSelectedModel = index0;
 
-        if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType) )
-        {
-            d->onPatientModelSelected(index0);
-        }
-        else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType) )
-        {
-            d->onStudyModelSelected(index0);
-        }
-        else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) )
-        {
-            d->onSeriesModelSelected(index0);
-        }
+    if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType) )
+      {
+      d->addPatientThumbnails(index0);
+      }
+    else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType) )
+      {
+      d->addStudyThumbnails(index0);
+      }
+    else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) )
+      {
+      d->addSeriesThumbnails(index0);
+      }
     }
 
-    this->setCurrentThumbnail(0);
+  this->setCurrentThumbnail(0);
 }

+ 1 - 1
Libs/DICOM/Widgets/ctkDICOMThumbnailListWidget.h

@@ -46,7 +46,7 @@ private:
   Q_DISABLE_COPY(ctkDICOMThumbnailListWidget);
 
 public Q_SLOTS:
-  void onModelSelected(const QModelIndex& index);
+  void addThumbnails(const QModelIndex& index);
 };
 
 #endif

+ 95 - 45
Libs/Widgets/ctkThumbnailListWidget.cpp

@@ -21,6 +21,7 @@
 // Qt include
 #include <QDateTime>
 #include <QDir>
+#include <QEvent>
 #include <QFile>
 #include <QFileInfo>
 #include <QGridLayout>
@@ -28,6 +29,7 @@
 #include <QPushButton>
 #include <QResizeEvent>
 #include <QScrollBar>
+#include <QTimer>
 
 // ctk includes
 #include "ctkLogger.h"
@@ -52,6 +54,7 @@ static ctkLogger logger("org.commontk.Widgets.ctkThumbnailListWidget");
 ctkThumbnailListWidgetPrivate
 ::ctkThumbnailListWidgetPrivate(ctkThumbnailListWidget* parent)
   : q_ptr(parent)
+  , RequestRelayout(false)
 {
 }
 
@@ -93,6 +96,59 @@ void ctkThumbnailListWidgetPrivate::clearAllThumbnails()
 }
 
 //----------------------------------------------------------------------------
+void ctkThumbnailListWidgetPrivate::addThumbnail(ctkThumbnailLabel* thumbnail)
+{
+  Q_Q(ctkThumbnailListWidget);
+
+  this->ScrollAreaContentWidget->layout()->addWidget(thumbnail);
+  thumbnail->installEventFilter(q);
+  this->RequestRelayout = true;
+
+  q->connect(thumbnail, SIGNAL(selected(ctkThumbnailLabel)),
+    q, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
+  q->connect(thumbnail, SIGNAL(selected(ctkThumbnailLabel)),
+    q, SIGNAL(selected(ctkThumbnailLabel)));
+  q->connect(thumbnail, SIGNAL(doubleClicked(ctkThumbnailLabel)),
+    q, SIGNAL(doubleClicked(ctkThumbnailLabel)));
+}
+
+//----------------------------------------------------------------------------
+void ctkThumbnailListWidgetPrivate::updateScrollAreaContentWidgetSize(QSize size)
+{
+  QSize newViewportSize = size - QSize(2 * this->ScrollArea->lineWidth(),
+                                       2 * this->ScrollArea->lineWidth());
+
+  ctkFlowLayout* flowLayout = qobject_cast<ctkFlowLayout*>(
+    this->ScrollAreaContentWidget->layout());
+  newViewportSize = newViewportSize.expandedTo(flowLayout->minimumSize());
+  if (flowLayout->hasHeightForWidth())
+    {
+    int newViewPortHeight = newViewportSize.height();
+    newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
+    if (newViewportSize.height() > newViewPortHeight)
+      {
+      // The new width is too narrow, to fit everything, a vertical scrollbar
+      // is needed. Recompute with the scrollbar width.
+      newViewportSize.rwidth() -= this->ScrollArea->verticalScrollBar()->sizeHint().width();
+      newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
+      }
+    }
+  else if (flowLayout->hasWidthForHeight())
+    {
+    int newViewPortWidth = newViewportSize.width();
+    newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
+    if (newViewportSize.width() > newViewPortWidth)
+      {
+      // The new height is too narrow, to fit everything, an horizontal scrollbar
+      // is needed. Recompute with the scrollbar height.
+      newViewportSize.rheight() -= this->ScrollArea->horizontalScrollBar()->sizeHint().height();
+      newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
+      }
+    }
+  this->ScrollAreaContentWidget->resize(newViewportSize);
+}
+
+//----------------------------------------------------------------------------
 // ctkThumbnailListWidget methods
 
 //----------------------------------------------------------------------------
@@ -106,9 +162,10 @@ ctkThumbnailListWidget::ctkThumbnailListWidget(QWidget* _parent):
 }
 
 //----------------------------------------------------------------------------
-ctkThumbnailListWidget::ctkThumbnailListWidget(ctkThumbnailListWidgetPrivate *_ptr, QWidget *_parent):
-  Superclass(_parent),
-  d_ptr(_ptr)
+ctkThumbnailListWidget
+::ctkThumbnailListWidget(ctkThumbnailListWidgetPrivate *_ptr, QWidget *_parent)
+  : Superclass(_parent)
+  , d_ptr(_ptr)
 {
   Q_D(ctkThumbnailListWidget);
 
@@ -124,24 +181,26 @@ ctkThumbnailListWidget::~ctkThumbnailListWidget()
 }
 
 //----------------------------------------------------------------------------
-void ctkThumbnailListWidget::addThumbnails(QList<QPixmap> thumbnails)
+void ctkThumbnailListWidget::addThumbnails(const QList<QPixmap>& thumbnails)
 {
-  Q_D(ctkThumbnailListWidget);
   for(int i=0; i<thumbnails.count(); i++)
     {
-    ctkThumbnailLabel* widget = new ctkThumbnailLabel(d->ScrollAreaContentWidget);
-    widget->setText("");
-    if(d->ThumbnailSize.isValid())
-      {
-      widget->setFixedSize(d->ThumbnailSize);
-      }
-    widget->setPixmap(thumbnails[i]);
-    d->ScrollAreaContentWidget->layout()->addWidget(widget);
+    this->addThumbnail("", thumbnails[i]);
+    }
+}
 
-    this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
-    this->connect(widget, SIGNAL(selected(ctkThumbnailLabel)), this, SIGNAL(selected(ctkThumbnailLabel)));
-    this->connect(widget, SIGNAL(doubleClicked(ctkThumbnailLabel)), this, SIGNAL(doubleClicked(ctkThumbnailLabel)));
+//----------------------------------------------------------------------------
+void ctkThumbnailListWidget::addThumbnail(const QString& label, const QPixmap& pixmap)
+{
+  Q_D(ctkThumbnailListWidget);
+  ctkThumbnailLabel* widget = new ctkThumbnailLabel(d->ScrollAreaContentWidget);
+  widget->setText("");
+  if(d->ThumbnailSize.isValid())
+    {
+    widget->setFixedSize(d->ThumbnailSize);
     }
+  widget->setPixmap(pixmap);
+  d->addThumbnail(widget);
 }
 
 //----------------------------------------------------------------------------
@@ -245,36 +304,27 @@ void ctkThumbnailListWidget::clearThumbnails()
 void ctkThumbnailListWidget::resizeEvent(QResizeEvent* event)
 {
   Q_D(ctkThumbnailListWidget);
+  d->updateScrollAreaContentWidgetSize(event->size());
+}
 
-  QSize newViewportSize = event->size() - QSize(2 * d->ScrollArea->lineWidth(),
-                                                2 * d->ScrollArea->lineWidth());
-
-  ctkFlowLayout* flowLayout = qobject_cast<ctkFlowLayout*>(
-    d->ScrollAreaContentWidget->layout());
-  newViewportSize = newViewportSize.expandedTo(flowLayout->minimumSize());
-  if (flowLayout->hasHeightForWidth())
-    {
-    int newViewPortHeight = newViewportSize.height();
-    newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
-    if (newViewportSize.height() > newViewPortHeight)
-      {
-      // The new width is too narrow, to fit everything, a vertical scrollbar
-      // is needed. Recompute with the scrollbar width.
-      newViewportSize.rwidth() -= d->ScrollArea->verticalScrollBar()->sizeHint().width();
-      newViewportSize.rheight() = flowLayout->heightForWidth( newViewportSize.width() );
-      }
-    }
-  else if (flowLayout->hasWidthForHeight())
+//----------------------------------------------------------------------------
+bool ctkThumbnailListWidget::eventFilter(QObject* watched, QEvent* event)
+{
+  Q_D(ctkThumbnailListWidget);
+  if (d->RequestRelayout &&
+      qobject_cast<ctkThumbnailLabel*>(watched) &&
+      event->type() == QEvent::Show)
     {
-    int newViewPortWidth = newViewportSize.width();
-    newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
-    if (newViewportSize.width() > newViewPortWidth)
-      {
-      // The new height is too narrow, to fit everything, an horizontal scrollbar
-      // is needed. Recompute with the scrollbar height.
-      newViewportSize.rheight() -= d->ScrollArea->horizontalScrollBar()->sizeHint().height();
-      newViewportSize.rwidth() = flowLayout->widthForHeight( newViewportSize.height() );
-      }
+    d->RequestRelayout = false;
+    watched->removeEventFilter(this);
+    QTimer::singleShot(0, this, SLOT(updateLayout()));
     }
-  d->ScrollAreaContentWidget->resize(newViewportSize);
+  return this->Superclass::eventFilter(watched, event);
+}
+
+//----------------------------------------------------------------------------
+void ctkThumbnailListWidget::updateLayout()
+{
+  Q_D(ctkThumbnailListWidget);
+  d->updateScrollAreaContentWidgetSize(this->size());
 }

+ 8 - 2
Libs/Widgets/ctkThumbnailListWidget.h

@@ -41,9 +41,12 @@ public:
   typedef QWidget Superclass;
   explicit ctkThumbnailListWidget(QWidget* parent=0);
   virtual ~ctkThumbnailListWidget();
-  
+
+  /// Add a thumbnail to the widget
+  void addThumbnail(const QString& label, const QPixmap& thumbnail);
+
   /// Add multiple thumbnails to the widget
-  void addThumbnails(QList<QPixmap> thumbnails);
+  void addThumbnails(const QList<QPixmap>& thumbnails);
 
   /// Set current thumbnail
   void setCurrentThumbnail(int index);
@@ -63,6 +66,8 @@ public:
   /// Get thumbnail width
   QSize thumbnailSize()const;
 
+  virtual bool eventFilter(QObject* watched, QEvent* event);
+
 public Q_SLOTS:
   /// Set thumbnail width
   void setThumbnailSize(QSize size);
@@ -73,6 +78,7 @@ Q_SIGNALS:
 
 protected Q_SLOTS:
   void onThumbnailSelected(const ctkThumbnailLabel& widget);
+  void updateLayout();
 
 protected:
   explicit ctkThumbnailListWidget(ctkThumbnailListWidgetPrivate* ptr, QWidget* parent=0);

+ 4 - 0
Libs/Widgets/ctkThumbnailListWidget_p.h

@@ -24,6 +24,7 @@
 #include "ctkWidgetsExport.h"
 #include "ui_ctkThumbnailListWidget.h"
 
+class ctkThumbnailLabel;
 class ctkThumbnailListWidget;
 
 //----------------------------------------------------------------------------
@@ -39,9 +40,12 @@ public:
   void init();
 
   void clearAllThumbnails();
+  void addThumbnail(ctkThumbnailLabel* thumbnail);
+  void updateScrollAreaContentWidgetSize(QSize size);
 
   int CurrentThumbnail;
   QSize ThumbnailSize;
+  bool RequestRelayout;
 
 protected:
   ctkThumbnailListWidget* const q_ptr;