Browse Source

Add summary dialog summarizing the imported directory information

Now the database emits signals as various kinds of data are added.
These are matched to slots in the App Widget that track the information
and provide a summary dialog (optionally).

The test confirms that the signals and slots are properly tracking
the inserts.
Steve Pieper 12 years ago
parent
commit
18adfa2f76

+ 12 - 0
Libs/DICOM/Core/ctkDICOMDatabase.cpp

@@ -1228,6 +1228,9 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q
 
           dbPatientID = insertPatient( ctkDataset );
 
+          // let users of this class track when things happen
+          emit q->patientAdded(dbPatientID, patientID, patientsName, patientsBirthDate);
+
           /// keep this for the next image
           LastPatientUID = dbPatientID;
           LastPatientID = patientID;
@@ -1242,11 +1245,17 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q
       if ( studyInstanceUID != "" && LastStudyInstanceUID != studyInstanceUID )
         {
           insertStudy(ctkDataset,dbPatientID);
+
+          // let users of this class track when things happen
+          emit q->studyAdded(studyInstanceUID);
         }
 
       if ( seriesInstanceUID != "" && seriesInstanceUID != LastSeriesInstanceUID )
         {
           insertSeries(ctkDataset, studyInstanceUID);
+
+          // let users of this class track when things happen
+          emit q->seriesAdded(seriesInstanceUID);
         }
       // TODO: what to do with imported files
       //
@@ -1268,6 +1277,9 @@ void ctkDICOMDatabasePrivate::insert( const ctkDICOMDataset& ctkDataset, const Q
 
               // insert was needed, so cache any application-requested tags
               this->precacheTags(sopInstanceUID);
+
+              // let users of this class track when things happen
+              emit q->instanceAdded(sopInstanceUID);
             }
         }
 

+ 18 - 0
Libs/DICOM/Core/ctkDICOMDatabase.h

@@ -233,6 +233,24 @@ public:
 
 
 Q_SIGNALS:
+  /// Things inserted to database.
+  /// patientAdded arguments:
+  ///  - int: database index of patient (unique) within CTK database
+  ///  - QString: patient ID (not unique across institutions)
+  ///  - QString: patient Name (not unique)
+  ///  - QString: patient Birth Date (not unique)
+  void patientAdded(int, QString, QString, QString);
+  /// studyAdded arguments:
+  ///  - studyUID (unique)
+  void studyAdded(QString);
+  /// seriesAdded arguments:
+  ///  - seriesUID (unique)
+  void seriesAdded(QString);
+  /// instance UID is provided
+  /// instanceAdded arguments:
+  ///  - instanceUID (unique)
+  void instanceAdded(QString);
+  /// Indicates that an in-memory database has been updated
   void databaseChanged();
   /// Indicates that the schema is about to be updated and how many files will be processed
   void schemaUpdateStarted(int);

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

@@ -26,7 +26,7 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME})
 # Add Tests
 #
 
-SIMPLE_TEST(ctkDICOMAppWidgetTest1)
+SIMPLE_TEST(ctkDICOMAppWidgetTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD)
 SIMPLE_TEST(ctkDICOMDatasetViewTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA)
 SIMPLE_TEST(ctkDICOMDirectoryListWidgetTest1)
 SIMPLE_TEST(ctkDICOMImageTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA)

+ 36 - 4
Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMAppWidgetTest1.cpp

@@ -22,9 +22,13 @@
 #include <QApplication>
 #include <QDebug>
 #include <QDir>
+#include <QKeyEvent>
 #include <QTimer>
 #include <QString>
 
+// ctk includes
+#include "ctkUtils.h"
+
 // ctkDICOMCore includes
 #include "ctkDICOMAppWidget.h"
 
@@ -32,7 +36,9 @@
 #include <iostream>
 
 /* Test from build directory:
- ./CTK-build/bin/CTKDICOMWidgetsCxxTests ctkDICOMAppWidgetTest1 test.db ../CTK/Libs/DICOM/Core/Resources/dicom-sample.sql
+ ./CTK-build/bin/CTKDICOMWidgetsCppTests ctkDICOMAppWidgetTest1 <test directory>
+
+ if the test directory does not have 100 instances in one patient/study/series, test will fail
 */
 
 int ctkDICOMAppWidgetTest1( int argc, char * argv [] )
@@ -40,18 +46,44 @@ int ctkDICOMAppWidgetTest1( int argc, char * argv [] )
   QApplication app(argc, argv);
   
   ctkDICOMAppWidget appWidget;
-  appWidget.setDatabaseDirectory(QDir::currentPath());
+
+  QFileInfo tempFileInfo(QDir::tempPath() + QString("/ctkDICOMAppWidgetTest1-db"));
+  QString dbDir = tempFileInfo.absoluteFilePath();
+  std::cerr << "\n\nUsing directory: " << dbDir.toLatin1().data() << std::endl;
+  if (tempFileInfo.exists())
+    {
+    std::cerr << "\n\nRemoving directory: " << dbDir.toLatin1().data() << std::endl;
+    ctk::removeDirRecursively(dbDir);
+    }
+  std::cerr << "\n\nMaking directory: " << dbDir.toLatin1().data() << std::endl;
+  QDir dir(dbDir);
+  dir.mkdir(dbDir);
+
+  appWidget.setDatabaseDirectory(dbDir);
   QString testString = QString("Test String");
   appWidget.onFileIndexed(testString);
   appWidget.openImportDialog();
   appWidget.openExportDialog();
   appWidget.openQueryDialog();
+
+  appWidget.openQueryDialog();
   
-  appWidget.show();
+  appWidget.setDisplayImportSummary(false);
+  appWidget.onImportDirectory(argv[argc -1]);
+  if ( appWidget.patientsAddedDuringImport() != 1
+    || appWidget.studiesAddedDuringImport() != 1
+    || appWidget.seriesAddedDuringImport() != 1
+    || appWidget.instancesAddedDuringImport() != 100)
+    {
+    std::cerr << "\n\nDirectory did not import as expected!\n\n";
+    exit(-1);
+    }
+
 
-  if (argc <= 1 || QString(argv[1]) != "-I")
+  if (argc <= 2 || QString(argv[1]) != "-I")
     {
     QTimer::singleShot(200, &app, SLOT(quit()));
     }
+  std::cerr << "\n\nAdded to database directory: " << dbDir.toLatin1().data() << std::endl;
   return app.exec();
 }

+ 124 - 0
Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp

@@ -28,6 +28,7 @@
 #include <QCoreApplication>
 #include <QCheckBox>
 #include <QDebug>
+#include <QMessageBox>
 #include <QMetaType>
 #include <QModelIndex>
 #include <QPersistentModelIndex>
@@ -94,6 +95,14 @@ public:
   QTimer* AutoPlayTimer;
 
   bool IsSearchWidgetPopUpMode;
+
+  // local count variables to keep track of the number of items
+  // added to the database during an import operation
+  bool DisplayImportSummary;
+  int PatientsAddedDuringImport;
+  int StudiesAddedDuringImport;
+  int SeriesAddedDuringImport;
+  int InstancesAddedDuringImport;
 };
 
 //----------------------------------------------------------------------------
@@ -106,6 +115,11 @@ ctkDICOMAppWidgetPrivate::ctkDICOMAppWidgetPrivate(ctkDICOMAppWidget* parent): q
   DICOMIndexer = QSharedPointer<ctkDICOMIndexer> (new ctkDICOMIndexer);
   IndexerProgress = 0;
   UpdateSchemaProgress = 0;
+  DisplayImportSummary = true;
+  PatientsAddedDuringImport = 0;
+  StudiesAddedDuringImport = 0;
+  SeriesAddedDuringImport = 0;
+  InstancesAddedDuringImport = 0;
 }
 
 ctkDICOMAppWidgetPrivate::~ctkDICOMAppWidgetPrivate()
@@ -243,6 +257,14 @@ ctkDICOMAppWidget::ctkDICOMAppWidget(QWidget* _parent):Superclass(_parent),
   d->ThumbnailsWidget->setThumbnailSize(
     QSize(d->ThumbnailWidthSlider->value(), d->ThumbnailWidthSlider->value()));
 
+  // signals related to tracking inserts
+  connect(d->DICOMDatabase.data(), SIGNAL(patientAdded(int,QString,QString,QString)), this,
+                              SLOT(onPatientAdded(int,QString,QString,QString)));
+  connect(d->DICOMDatabase.data(), SIGNAL(studyAdded(QString)), this, SLOT(onStudyAdded(QString)));
+  connect(d->DICOMDatabase.data(), SIGNAL(seriesAdded(QString)), this, SLOT(onSeriesAdded(QString)));
+  connect(d->DICOMDatabase.data(), SIGNAL(instanceAdded(QString)), this, SLOT(onInstanceAdded(QString)));
+
+  // Treeview signals
   connect(d->TreeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(onTreeCollapsed(QModelIndex)));
   connect(d->TreeView, SIGNAL(expanded(QModelIndex)), this, SLOT(onTreeExpanded(QModelIndex)));
 
@@ -307,6 +329,54 @@ ctkDICOMAppWidget::~ctkDICOMAppWidget()
 }
 
 //----------------------------------------------------------------------------
+bool ctkDICOMAppWidget::displayImportSummary()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  return d->DisplayImportSummary;
+}
+
+//----------------------------------------------------------------------------
+void ctkDICOMAppWidget::setDisplayImportSummary(bool onOff)
+{
+  Q_D(ctkDICOMAppWidget);
+
+  d->DisplayImportSummary = onOff;
+}
+
+//----------------------------------------------------------------------------
+int ctkDICOMAppWidget::patientsAddedDuringImport()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  return d->PatientsAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+int ctkDICOMAppWidget::studiesAddedDuringImport()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  return d->StudiesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+int ctkDICOMAppWidget::seriesAddedDuringImport()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  return d->SeriesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+int ctkDICOMAppWidget::instancesAddedDuringImport()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  return d->InstancesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
 void ctkDICOMAppWidget::updateDatabaseSchemaIfNeeded()
 {
 
@@ -557,6 +627,41 @@ void ctkDICOMAppWidget::onThumbnailDoubleClicked(const ctkThumbnailLabel& widget
 }
 
 //----------------------------------------------------------------------------
+void ctkDICOMAppWidget::onPatientAdded(int databaseID, QString patientID, QString patientName, QString patientBirthDate )
+{
+  Q_D(ctkDICOMAppWidget);
+  Q_UNUSED(databaseID);
+  Q_UNUSED(patientID);
+  Q_UNUSED(patientName);
+  Q_UNUSED(patientBirthDate);
+  ++d->PatientsAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+void ctkDICOMAppWidget::onStudyAdded(QString studyUID)
+{
+  Q_D(ctkDICOMAppWidget);
+  Q_UNUSED(studyUID);
+  ++d->StudiesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+void ctkDICOMAppWidget::onSeriesAdded(QString seriesUID)
+{
+  Q_D(ctkDICOMAppWidget);
+  Q_UNUSED(seriesUID);
+  ++d->SeriesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
+void ctkDICOMAppWidget::onInstanceAdded(QString instanceUID)
+{
+  Q_D(ctkDICOMAppWidget);
+  Q_UNUSED(instanceUID);
+  ++d->InstancesAddedDuringImport;
+}
+
+//----------------------------------------------------------------------------
 void ctkDICOMAppWidget::onImportDirectory(QString directory)
 {
   Q_D(ctkDICOMAppWidget);
@@ -568,8 +673,27 @@ void ctkDICOMAppWidget::onImportDirectory(QString directory)
       {
       targetDirectory = d->DICOMDatabase->databaseDirectory();
       }
+
+    // reset counts
+    d->PatientsAddedDuringImport = 0;
+    d->StudiesAddedDuringImport = 0;
+    d->SeriesAddedDuringImport = 0;
+    d->InstancesAddedDuringImport = 0;
+
+    // show progress dialog and perform indexing
     d->showIndexerDialog();
     d->DICOMIndexer->addDirectory(*d->DICOMDatabase,directory,targetDirectory);
+
+    // display summary result
+    if (d->DisplayImportSummary)
+      {
+      QString message = "Directory import completed.\n\n";
+      message += QString("%1 New Patients\n").arg(QString::number(d->PatientsAddedDuringImport));
+      message += QString("%1 New Studies\n").arg(QString::number(d->StudiesAddedDuringImport));
+      message += QString("%1 New Series\n").arg(QString::number(d->SeriesAddedDuringImport));
+      message += QString("%1 New Instances\n").arg(QString::number(d->InstancesAddedDuringImport));
+      QMessageBox::information(this,"DICOM Directory Import", message);
+      }
   }
 }
 

+ 24 - 1
Libs/DICOM/Widgets/ctkDICOMAppWidget.h

@@ -39,6 +39,7 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMAppWidget : public QWidget
   Q_PROPERTY(QString databaseDirectory READ databaseDirectory WRITE setDatabaseDirectory)
   Q_PROPERTY(bool searchWidgetPopUpMode READ searchWidgetPopUpMode WRITE setSearchWidgetPopUpMode)
   Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache)
+  Q_PROPERTY(bool displayImportSummary READ displayImportSummary WRITE setDisplayImportSummary)
 
 public:
   typedef QWidget Superclass;
@@ -67,6 +68,17 @@ public:
   bool searchWidgetPopUpMode();
   ctkDICOMDatabase* database();
 
+  /// Option to show or not import summary dialog.
+  /// Since the summary dialog is modal, we give the option
+  /// of disabling it for batch modes or testing.
+  void setDisplayImportSummary(bool);
+  bool displayImportSummary();
+  /// Accessors to status of last directory import operation
+  int patientsAddedDuringImport();
+  int studiesAddedDuringImport();
+  int seriesAddedDuringImport();
+  int instancesAddedDuringImport();
+
 public Q_SLOTS:
   void setDatabaseDirectory(const QString& directory);
   void onFileIndexed(const QString& filePath);
@@ -80,6 +92,18 @@ public Q_SLOTS:
   void resumeModel();
   void resetModel();
 
+  /// Import a directory - this is used when the user selects a directory
+  /// from the Import Dialog, but can also be used externally to trigger
+  /// an import (i.e. for testing or to support drag-and-drop)
+  void onImportDirectory(QString directory);
+
+  /// slots to capture status updates from the database during an 
+  /// import operation
+  void onPatientAdded(int, QString, QString, QString);
+  void onStudyAdded(QString);
+  void onSeriesAdded(QString);
+  void onInstanceAdded(QString);
+
 Q_SIGNALS:
   /// Emited when directory is changed
   void databaseDirectoryChanged(const QString&);
@@ -91,7 +115,6 @@ Q_SIGNALS:
 protected:
     QScopedPointer<ctkDICOMAppWidgetPrivate> d_ptr;
 protected Q_SLOTS:
-    void onImportDirectory(QString directory);
     void onModelSelected(const QModelIndex& index);
 
     /// To be called when a thumbnail in thumbnail list widget is selected