瀏覽代碼

Merge branch 'enable-remove-from-dicom-database'

This is first part of the solution for #97
Marco Nolden 13 年之前
父節點
當前提交
68f14a5009

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

@@ -798,3 +798,92 @@ bool ctkDICOMDatabase::isInMemory() const
   Q_D(const ctkDICOMDatabase);
   return d->DatabaseFileName == ":memory:";
 }
+/*
+bool ctkDICOMDatabase::removeImage(const QString& sopInstanceUID)
+{
+  return false;
+}
+*/
+bool ctkDICOMDatabase::removeSeries(const QString& seriesInstanceUID)
+{
+  Q_D(ctkDICOMDatabase);
+
+  // get all images from series
+  QSqlQuery fileExists ( d->Database );
+  fileExists.prepare("SELECT Filename, SOPInstanceUID, StudyInstanceUID FROM Images,Series WHERE Series.SeriesInstanceUID = Images.SeriesInstanceUID AND Images.SeriesInstanceUID = :seriesID");
+  fileExists.bindValue(":seriesID",seriesInstanceUID);
+  bool success = fileExists.exec();
+  if (!success)
+  {
+    logger.error("SQLITE ERROR: " + fileExists.lastError().driverText());
+    return false;
+  }
+
+  QList< QPair<QString,QString> > removeList;
+  while ( fileExists.next() )
+  {
+    QString dbFilePath = fileExists.value(fileExists.record().indexOf("Filename")).toString();
+    QString sopInstanceUID = fileExists.value(fileExists.record().indexOf("SOPInstanceUID")).toString();
+    QString studyInstanceUID = fileExists.value(fileExists.record().indexOf("StudyInstanceUID")).toString();
+    QString internalFilePath = studyInstanceUID + "/" + seriesInstanceUID + "/" + sopInstanceUID;
+    removeList << qMakePair(dbFilePath,internalFilePath);
+  }
+
+  QSqlQuery fileRemove ( d->Database );
+  fileRemove.prepare("DELETE FROM Images WHERE SeriesInstanceUID == ?");
+  fileRemove.bindValue(0,seriesInstanceUID);
+  fileRemove.exec();
+  
+  QPair<QString,QString> fileToRemove;
+  foreach (fileToRemove, removeList)
+  {
+    QString dbFilePath = fileToRemove.first;
+    QString thumbnailToRemove = databaseDirectory() + "/thumbs/" + fileToRemove.second + ".png";
+
+    // check that the file is below our internal storage
+    if (dbFilePath.startsWith( databaseDirectory() + "/dicom/"))
+    {
+      if (!dbFilePath.endsWith(fileToRemove.second))
+      {
+        logger.error("Database inconsistency detected during delete!");
+        continue;
+      }
+      if (QFile( dbFilePath ).remove())
+      {
+        logger.debug("Removed file " + dbFilePath );
+      }
+      else
+      {
+        logger.warn("Failed to remove file " + dbFilePath );
+      }
+    }
+    if (QFile( thumbnailToRemove ).remove())
+      {
+        logger.debug("Removed thumbnail " + thumbnailToRemove);
+      }
+      else
+      {
+        logger.warn("Failed to remove thumbnail " + thumbnailToRemove);
+      }
+    }    
+
+  this->cleanup();
+
+  return true;
+}
+
+bool ctkDICOMDatabase::cleanup()
+{
+  Q_D(ctkDICOMDatabase);
+  QSqlQuery seriesCleanup ( d->Database );
+  seriesCleanup.exec("DELETE FROM Series WHERE ( SELECT COUNT(*) FROM Images WHERE Images.SeriesInstanceUID = Series.SeriesInstanceUID ) = 0;");
+  seriesCleanup.exec("DELETE FROM Studies WHERE ( SELECT COUNT(*) FROM Series WHERE Series.StudyInstanceUID = Studies.StudyInstanceUID ) = 0;");
+  seriesCleanup.exec("DELETE FROM Patients WHERE ( SELECT COUNT(*) FROM Studies WHERE Studies.PatientsUID = Patients.UID ) = 0;");
+  return true;
+}
+/*
+bool ctkDICOMDatabase::removeStudy(const QString& studyInstanceUID)
+{
+  return false;
+}
+*/

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

@@ -145,6 +145,12 @@ public:
   /// Check if file is already in database and up-to-date
   bool fileExistsAndUpToDate(const QString& filePath);
 
+  // bool removeImage(const QString& sopInstanceUID);
+  /// remove the series from the database, including images and
+  /// thumbnails  
+  bool removeSeries(const QString& seriesInstanceUID);
+  // bool removeStudy(const QString& studyInstanceUID);
+  bool cleanup();
 
 Q_SIGNALS:
   void databaseChanged();

+ 31 - 3
Libs/DICOM/Widgets/Resources/UI/ctkDICOMAppWidget.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>739</width>
-    <height>638</height>
+    <width>788</width>
+    <height>607</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -44,6 +44,7 @@
      <addaction name="ActionExport"/>
      <addaction name="ActionQuery"/>
      <addaction name="ActionSend"/>
+     <addaction name="ActionRemove"/>
     </widget>
    </item>
    <item>
@@ -507,6 +508,17 @@
     <string>Send DICOM Studies to a DICOM node (not yet available)</string>
    </property>
   </action>
+  <action name="ActionRemove">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Remove</string>
+   </property>
+   <property name="toolTip">
+    <string>Remove from database</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>
@@ -715,9 +727,24 @@
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>ActionRemove</sender>
+   <signal>triggered()</signal>
+   <receiver>ctkDICOMAppWidget</receiver>
+   <slot>onRemoveAction()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>391</x>
+     <y>303</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
  <slots>
-  <signal>signal1()</signal>
   <slot>openImportDialog()</slot>
   <slot>openExportDialog()</slot>
   <slot>openQueryDialog()</slot>
@@ -730,5 +757,6 @@
   <slot>onPreviousStudy()</slot>
   <slot>onAutoPlayCheckboxStateChanged(int)</slot>
   <slot>onThumbnailWidthSliderValueChanged(int)</slot>
+  <slot>onRemoveAction()</slot>
  </slots>
 </ui>

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

@@ -304,6 +304,29 @@ void ctkDICOMAppWidget::openQueryDialog()
 }
 
 //----------------------------------------------------------------------------
+void ctkDICOMAppWidget::onRemoveAction()
+{
+  Q_D(ctkDICOMAppWidget);
+
+  //d->QueryRetrieveWidget->show();
+  // d->QueryRetrieveWidget->raise();
+  std::cout << "on remove" << std::endl;
+  QModelIndexList selection = d->TreeView->selectionModel()->selectedIndexes();
+  std::cout << selection.size() << std::endl;
+  QModelIndex index;
+  foreach(index,selection)
+  {
+    QModelIndex index0 = index.sibling(index.row(), 0);
+    if ( d->DICOMModel.data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType))
+    {
+      QString seriesUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString();
+      d->DICOMDatabase->removeSeries(seriesUID);
+    }
+  }
+  d->DICOMModel.reset();
+}
+
+//----------------------------------------------------------------------------
 void ctkDICOMAppWidget::suspendModel()
 {
   Q_D(ctkDICOMAppWidget);
@@ -426,7 +449,9 @@ Q_D(ctkDICOMAppWidget);
           d->NextStudyButton->hide();
           d->PrevStudyButton->hide();
           }
+        d->ActionRemove->setEnabled(model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) );
         }
+
       else
         {
         d->NextImageButton->hide();
@@ -435,6 +460,7 @@ Q_D(ctkDICOMAppWidget);
         d->PrevSeriesButton->hide();
         d->NextStudyButton->hide();
         d->PrevStudyButton->hide();
+        d->ActionRemove->setEnabled(false);
         }
 }
 

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

@@ -56,6 +56,7 @@ public Q_SLOTS:
   void openImportDialog();
   void openExportDialog();
   void openQueryDialog();
+  void onRemoveAction();
 
   void suspendModel();
   void resumeModel();