浏览代码

Merge branch 'dicom-query-progress-report'

* dicom-query-progress-report:
  Add progress dialog when querying server nodes
  Move ctkDICOMModel/checkable header initialization

Conflicts:
	Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp
	Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h
Julien Finet 14 年之前
父节点
当前提交
1c7df8977d

+ 1 - 1
Libs/DICOM/Core/ctkDICOMModel.cpp

@@ -398,7 +398,7 @@ bool ctkDICOMModel::canFetchMore ( const QModelIndex & parentValue ) const
 {
   Q_D(const ctkDICOMModel);
   Node* node = d->nodeFromIndex(parentValue);
-  return !node->AtEnd;
+  return node ? !node->AtEnd : false;
 }
 
 //------------------------------------------------------------------------------

+ 53 - 3
Libs/DICOM/Core/ctkDICOMQuery.cpp

@@ -101,6 +101,7 @@ ctkDICOMQuery::~ctkDICOMQuery()
 {
 }
 
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::addStudyInstanceUID ( QString s )
 {
   Q_D(ctkDICOMQuery);
@@ -108,63 +109,83 @@ void ctkDICOMQuery::addStudyInstanceUID ( QString s )
 }
 
 /// Set methods for connectivity
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::setCallingAETitle ( QString callingAETitle )
 {
   Q_D(ctkDICOMQuery);
   d->CallingAETitle = callingAETitle;
 }
+
+//------------------------------------------------------------------------------
 const QString& ctkDICOMQuery::callingAETitle() 
 {
   Q_D(ctkDICOMQuery);
   return d->CallingAETitle;
 }
+
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::setCalledAETitle ( QString calledAETitle )
 {
   Q_D(ctkDICOMQuery);
   d->CalledAETitle = calledAETitle;
 }
+
+//------------------------------------------------------------------------------
 const QString& ctkDICOMQuery::calledAETitle()
 {
   Q_D(ctkDICOMQuery);
   return d->CalledAETitle;
 }
+
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::setHost ( QString host )
 {
   Q_D(ctkDICOMQuery);
   d->Host = host;
 }
+
+//------------------------------------------------------------------------------
 const QString& ctkDICOMQuery::host()
 {
   Q_D(ctkDICOMQuery);
   return d->Host;
 }
+
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::setPort ( int port ) 
 {
   Q_D(ctkDICOMQuery);
   d->Port = port;
 }
+
+//------------------------------------------------------------------------------
 int ctkDICOMQuery::port()
 {
   Q_D(ctkDICOMQuery);
   return d->Port;
 }
+
+//------------------------------------------------------------------------------
 void ctkDICOMQuery::setFilters ( QMap<QString,QVariant> filters ) 
 {
   Q_D(ctkDICOMQuery);
   d->Filters = filters;
 }
+
+//------------------------------------------------------------------------------
 QMap<QString,QVariant> ctkDICOMQuery::filters()
 {
   Q_D(ctkDICOMQuery);
   return d->Filters;
 }
+
+//------------------------------------------------------------------------------
 QStringList ctkDICOMQuery::studyInstanceUIDQueried()
 {
   Q_D(ctkDICOMQuery);
   return d->StudyInstanceUIDList;
 }
 
-
 //------------------------------------------------------------------------------
 void ctkDICOMQuery::query(ctkDICOMDatabase& database )
 {
@@ -173,11 +194,15 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   if ( database.database().isOpen() )
     {
     logger.debug ( "DB open in Query" );
+    emit progress("DB open in Query");
     }
   else
     {
     logger.debug ( "DB not open in Query" );
+    emit progress("DB not open in Query");
     }
+  emit progress(0);
+
   d->StudyInstanceUIDList.clear();
   d->SCU.setAETitle ( OFString(this->callingAETitle().toStdString().c_str()) );
   d->SCU.setPeerAETitle ( OFString(this->calledAETitle().toStdString().c_str()) );
@@ -185,6 +210,9 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   d->SCU.setPeerPort ( this->port() );
 
   logger.error ( "Setting Transfer Syntaxes" );
+  emit progress("Setting Transfer Syntaxes");
+  emit progress(10);
+
   OFList<OFString> transferSyntaxes;
   transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax );
   transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax );
@@ -194,10 +222,15 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   // d->SCU.addPresentationContext ( UID_VerificationSOPClass, transferSyntaxes );
   if ( !d->SCU.initNetwork().good() ) 
     {
-    std::cerr << "Error initializing the network" << std::endl;
+    logger.error( "Error initializing the network" );
+    emit progress("Error initializing the network");
+    emit progress(100);
     return;
     }
   logger.debug ( "Negotiating Association" );
+  emit progress("Negatiating Association");
+  emit progress(20);
+
   d->SCU.negotiateAssociation();
 
   // Clear the query
@@ -269,6 +302,8 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
     }
   }
 
+  emit progress(30);
+
   FINDResponses *responses = new FINDResponses();
 
   Uint16 presentationContex = 0;
@@ -285,21 +320,27 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   if ( presentationContex == 0 )
     {
     logger.error ( "Failed to find acceptable presentation context" );
+    emit progress("Failed to find acceptable presentation context");
     }
   else
     {
     logger.info ( "Found useful presentation context" );
+    emit progress("Found useful presentation context");
     }
+  emit progress(40);
 
   OFCondition status = d->SCU.sendFINDRequest ( presentationContex, d->query, responses );
   if ( status.good() )
     {
     logger.debug ( "Find succeded" );
+    emit progress("Find succeded");
     }
   else
     {
     logger.error ( "Find failed" );
+    emit progress("Find failed");
     }
+  emit progress(50);
 
   for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )
     {
@@ -316,15 +357,19 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
 
   // Now search each Study
   d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
+  float progressRatio = 25. / d->StudyInstanceUIDList.count();
+  int i = 0;
   foreach ( QString StudyInstanceUID, d->StudyInstanceUIDList )
     {
     logger.debug ( "Starting Series C-FIND for Series: " + StudyInstanceUID );
+    emit progress(QString("Starting Series C-FIND for Series: ") + StudyInstanceUID);
+    emit progress(50 + (progressRatio * i++));
+
     d->query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() );
     responses = new FINDResponses();
     status = d->SCU.sendFINDRequest ( 0, d->query, responses );
     if ( status.good() )
       {
-      logger.debug ( "Find succeded for Series: " + StudyInstanceUID );
       for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )
         {
         DcmDataset *dataset = (*it)->m_dataset;
@@ -333,13 +378,18 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
           database.insert ( dataset );
           }
         }
+      logger.debug ( "Find succeded for Series: " + StudyInstanceUID );
+      emit progress(QString("Find succeded for Series: ") + StudyInstanceUID);
       }
     else
       {
       logger.error ( "Find failed for Series: " + StudyInstanceUID );
+      emit progress(QString("Find failed for Series: ") + StudyInstanceUID);
       }
+    emit progress(50 + (progressRatio * i++));
     delete responses;
     }
   d->SCU.closeAssociation ( DUL_PEERREQUESTEDRELEASE );
+  emit progress(100);
 }
 

+ 4 - 1
Libs/DICOM/Core/ctkDICOMQuery.h

@@ -64,7 +64,6 @@ public:
   /// Access the list of study instance UIDs from the last query
   QStringList studyInstanceUIDQueried();
 
-
   ///
   // Filters are keyword/value pairs as generated by
   // the ctkDICOMWidgets in a human readable (and editable)
@@ -73,6 +72,10 @@ public:
   void setFilters(QMap<QString,QVariant>);
   QMap<QString,QVariant> filters();
 
+signals:
+  void progress(int progress);
+  void progress(const QString& message);
+
 protected:
   QScopedPointer<ctkDICOMQueryPrivate> d_ptr;
 

+ 122 - 42
Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp

@@ -1,11 +1,34 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+//Qt includes
 #include <QDebug>
+#include <QLabel>
+#include <QProgressDialog>
+#include <QSettings>
 #include <QTreeView>
 #include <QTabBar>
-#include <QSettings>
-#include <QHBoxLayout>
 
 /// CTK includes
 #include <ctkCheckableHeaderView.h>
+#include <ctkLogger.h>
 
 // ctkDICOMCore includes
 #include "ctkDICOMDatabase.h"
@@ -18,8 +41,6 @@
 #include "ctkDICOMQueryResultsTabWidget.h"
 #include "ui_ctkDICOMQueryRetrieveWidget.h"
 
-
-#include <ctkLogger.h>
 static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMQueryRetrieveWidget");
 
 //----------------------------------------------------------------------------
@@ -33,26 +54,43 @@ public:
   QMap<QString, ctkDICOMRetrieve*> RetrievalsByStudyUID;
   ctkDICOMDatabase QueryResultDatabase;
   QSharedPointer<ctkDICOMDatabase> RetrieveDatabase;
-  ctkDICOMModel model;
+  ctkDICOMModel    model;
+  
+  QProgressDialog* ProgressDialog;
+  QString          CurrentServer;
 };
 
 //----------------------------------------------------------------------------
 // ctkDICOMQueryRetrieveWidgetPrivate methods
 
-
 //----------------------------------------------------------------------------
 // ctkDICOMQueryRetrieveWidget methods
 
 //----------------------------------------------------------------------------
-ctkDICOMQueryRetrieveWidget::ctkDICOMQueryRetrieveWidget(QWidget* _parent):Superclass(_parent), 
-  d_ptr(new ctkDICOMQueryRetrieveWidgetPrivate)
+ctkDICOMQueryRetrieveWidget::ctkDICOMQueryRetrieveWidget(QWidget* parentWidget)
+  : Superclass(parentWidget) 
+  , d_ptr(new ctkDICOMQueryRetrieveWidgetPrivate)
 {
   Q_D(ctkDICOMQueryRetrieveWidget);
   
   d->setupUi(this);
 
+  d->ProgressDialog = 0;
   connect(d->QueryButton, SIGNAL(clicked()), this, SLOT(processQuery()));
   connect(d->RetrieveButton, SIGNAL(clicked()), this, SLOT(processRetrieve()));
+
+  d->results->setModel(&d->model);
+  d->model.setHeaderData(0, Qt::Horizontal, Qt::Unchecked, Qt::CheckStateRole);
+
+  QHeaderView* previousHeaderView = d->results->header();
+  ctkCheckableHeaderView* headerView = new ctkCheckableHeaderView(Qt::Horizontal, d->results);
+  headerView->setClickable(previousHeaderView->isClickable());
+  headerView->setMovable(previousHeaderView->isMovable());
+  headerView->setHighlightSections(previousHeaderView->highlightSections());
+  headerView->setPropagateToItems(true);
+  d->results->setHeader(headerView);
+  // headerView is hidden because it was created with a visisble parent widget 
+  headerView->setHidden(false);
 }
 
 //----------------------------------------------------------------------------
@@ -85,55 +123,70 @@ void ctkDICOMQueryRetrieveWidget::processQuery()
   }
 
   // for each of the selected server nodes, send the query
-  QStringList serverNodes = d->ServerNodeWidget->nodes();
-  foreach (QString server, serverNodes)
-  {
-    QMap<QString, QVariant> parameters = d->ServerNodeWidget->nodeParameters(server);
-    if ( parameters["CheckState"] == Qt::Checked )
+  QProgressDialog progress("Query DICOM servers", "Cancel", 0, 100, this,
+                           Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
+  // We don't want the progress dialog to resize itself, so we bypass the label
+  // by creating our own
+  QLabel* progressLabel = new QLabel("Initialization...");
+  progress.setLabel(progressLabel);
+  d->ProgressDialog = &progress;
+  progress.setWindowModality(Qt::WindowModal);
+  progress.setMinimumDuration(0);
+  progress.setValue(0);
+  foreach (d->CurrentServer, d->ServerNodeWidget->checkedNodes())
     {
-      // create a query for the current server
-      d->QueriesByServer[server] = new ctkDICOMQuery;
-      d->QueriesByServer[server]->setCallingAETitle(d->ServerNodeWidget->callingAETitle());
-      d->QueriesByServer[server]->setCalledAETitle(parameters["AETitle"].toString());
-      d->QueriesByServer[server]->setHost(parameters["Address"].toString());
-      d->QueriesByServer[server]->setPort(parameters["Port"].toInt());
-
-      // populate the query with the current search options
-      d->QueriesByServer[server]->setFilters( d->QueryWidget->parameters() );
+    if (progress.wasCanceled())
+      {
+      break;
+      }
+    QMap<QString, QVariant> parameters = d->ServerNodeWidget->nodeParameters(d->CurrentServer);
+    // if we are here it's because the server node was checked
+    Q_ASSERT(parameters["CheckState"] == Qt::Checked );
+    // create a query for the current server
+    ctkDICOMQuery* query = new ctkDICOMQuery;
+    d->QueriesByServer[d->CurrentServer] = query;
+    query->setCallingAETitle(d->ServerNodeWidget->callingAETitle());
+    query->setCalledAETitle(parameters["AETitle"].toString());
+    query->setHost(parameters["Address"].toString());
+    query->setPort(parameters["Port"].toInt());
+
+    // populate the query with the current search options
+    query->setFilters( d->QueryWidget->parameters() );
 
-      try
+    try
       {
-        // run the query against the selected server and put results in database
-        d->QueriesByServer[server]->query ( d->QueryResultDatabase );
+      connect(query, SIGNAL(progress(QString)),
+              //&progress, SLOT(setLabelText(QString)));
+              progressLabel, SLOT(setText(QString)));
+      // for some reasons, setLabelText() doesn't refresh the dialog.
+      connect(query, SIGNAL(progress(int)),
+              this, SLOT(onQueryProgressChanged(int)));
+      // run the query against the selected server and put results in database
+      query->query ( d->QueryResultDatabase );
+      disconnect(query, SIGNAL(progress(QString)),
+                 //&progress, SLOT(setLabelText(QString)));
+                 progressLabel, SLOT(setText(QString)));
+      disconnect(query, SIGNAL(progress(int)),
+                 this, SLOT(onQueryProgressChanged(int)));
       }
-      catch (std::exception e)
+    catch (std::exception e)
       {
-        logger.error ( "Query error: " + parameters["Name"].toString() );
+      logger.error ( "Query error: " + parameters["Name"].toString() );
+      progress.setLabelText("Query error: " + parameters["Name"].toString());
       }
 
-      foreach( QString studyUID, d->QueriesByServer[server]->studyInstanceUIDQueried() )
+    foreach( QString studyUID, query->studyInstanceUIDQueried() )
       {
-        d->QueriesByStudyUID[studyUID] = d->QueriesByServer[server];
+      d->QueriesByStudyUID[studyUID] = query;
       }
     }
-  }
   
   // checkable headers - allow user to select the patient/studies to retrieve
-  d->results->setModel(&d->model);
   d->model.setDatabase(d->QueryResultDatabase.database());
 
-  d->model.setHeaderData(0, Qt::Horizontal, Qt::Unchecked, Qt::CheckStateRole);
-  QHeaderView* previousHeaderView = d->results->header();
-  ctkCheckableHeaderView* headerView = new ctkCheckableHeaderView(Qt::Horizontal, d->results);
-  headerView->setClickable(previousHeaderView->isClickable());
-  headerView->setMovable(previousHeaderView->isMovable());
-  headerView->setHighlightSections(previousHeaderView->highlightSections());
-  headerView->setPropagateToItems(true);
-  d->results->setHeader(headerView);
-  // headerView is hidden because it was created with a visisble parent widget 
-  headerView->setHidden(false);
-
   d->RetrieveButton->setEnabled(d->model.rowCount());
+  progress.setValue(progress.maximum());
+  d->ProgressDialog = 0;
 }
 
 //----------------------------------------------------------------------------
@@ -172,3 +225,30 @@ void ctkDICOMQueryRetrieveWidget::processRetrieve()
     logger.info ( "Retrieve success" );
   }
 }
+
+//----------------------------------------------------------------------------
+void ctkDICOMQueryRetrieveWidget::onQueryProgressChanged(int value)
+{
+  Q_D(ctkDICOMQueryRetrieveWidget);
+  if (d->ProgressDialog == 0)
+    {
+    return;
+    }
+  QStringList servers = d->ServerNodeWidget->checkedNodes();
+  int serverIndex = servers.indexOf(d->CurrentServer);
+  if (serverIndex < 0)
+    {
+    return;
+    }
+  float serverProgress = 100. / servers.size();
+  d->ProgressDialog->setValue( (serverIndex + (value / 101.)) * serverProgress);
+  if (d->ProgressDialog->width() != 500)
+    {
+    QPoint pp = this->mapToGlobal(QPoint(0,0));
+    pp = QPoint(pp.x() + (this->width() - d->ProgressDialog->width()) / 2,
+                pp.y() + (this->height() - d->ProgressDialog->height())/ 2);
+    d->ProgressDialog->move(pp - QPoint((500 - d->ProgressDialog->width())/2, 0));
+    d->ProgressDialog->resize(500, d->ProgressDialog->height());
+    }
+  //d->CurrentServerqApp->processEvents();
+}

+ 6 - 3
Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.h

@@ -41,9 +41,12 @@ public:
   virtual ~ctkDICOMQueryRetrieveWidget();
 
 public slots:
-    void setRetrieveDatabase(QSharedPointer<ctkDICOMDatabase> retrieveDatabase);
-    void processQuery();
-    void processRetrieve();
+  void setRetrieveDatabase(QSharedPointer<ctkDICOMDatabase> retrieveDatabase);
+  void processQuery();
+  void processRetrieve();
+
+protected slots:
+  void onQueryProgressChanged(int value);
 
 protected:
   QScopedPointer<ctkDICOMQueryRetrieveWidgetPrivate> d_ptr;

+ 29 - 10
Libs/DICOM/Widgets/ctkDICOMServerNodeWidget.cpp

@@ -50,8 +50,9 @@ public:
 // ctkDICOMServerNodeWidget methods
 
 //----------------------------------------------------------------------------
-ctkDICOMServerNodeWidget::ctkDICOMServerNodeWidget(QWidget* _parent):Superclass(_parent),
-  d_ptr(new ctkDICOMServerNodeWidgetPrivate)
+ctkDICOMServerNodeWidget::ctkDICOMServerNodeWidget(QWidget* parentWidget)
+  : Superclass(parentWidget)
+  , d_ptr(new ctkDICOMServerNodeWidgetPrivate)
 {
   Q_D(ctkDICOMServerNodeWidget);
  
@@ -232,23 +233,41 @@ QMap<QString,QVariant> ctkDICOMServerNodeWidget::parameters()
 }
 
 //----------------------------------------------------------------------------
-QStringList ctkDICOMServerNodeWidget::nodes()
+QStringList ctkDICOMServerNodeWidget::nodes()const
 {
-  Q_D(ctkDICOMServerNodeWidget);
+  Q_D(const ctkDICOMServerNodeWidget);
 
-  int count = d->NodeTable->rowCount();
+  const int count = d->NodeTable->rowCount();
   QStringList nodes;
   for (int row = 0; row < count; row++)
-  {
-    nodes << d->NodeTable->item(row,0)->text();
-  }
+    {
+    nodes << d->NodeTable->item(row,NameColumn)->text();
+    }
   return nodes;
 }
 
 //----------------------------------------------------------------------------
-QMap<QString, QVariant> ctkDICOMServerNodeWidget::nodeParameters(QString &node)
+QStringList ctkDICOMServerNodeWidget::checkedNodes()const
 {
-  Q_D(ctkDICOMServerNodeWidget);
+  Q_D(const ctkDICOMServerNodeWidget);
+
+  const int count = d->NodeTable->rowCount();
+  QStringList nodes;
+  for (int row = 0; row < count; row++)
+    {
+    QTableWidgetItem* item = d->NodeTable->item(row,NameColumn);
+    if (item && item->checkState() == Qt::Checked)
+      {
+      nodes << item->text();
+      }
+    }
+  return nodes;
+}
+
+//----------------------------------------------------------------------------
+QMap<QString, QVariant> ctkDICOMServerNodeWidget::nodeParameters(const QString &node)const
+{
+  Q_D(const ctkDICOMServerNodeWidget);
 
   QMap<QString, QVariant> parameters;
 

+ 5 - 3
Libs/DICOM/Widgets/ctkDICOMServerNodeWidget.h

@@ -40,10 +40,12 @@ public:
   explicit ctkDICOMServerNodeWidget(QWidget* parent=0);
   virtual ~ctkDICOMServerNodeWidget();
 
-  QString callingAETitle();
+  QString                callingAETitle();
   QMap<QString,QVariant> parameters();
-  QStringList nodes();
-  QMap<QString,QVariant> nodeParameters(QString &node);
+
+  QStringList            nodes()const;
+  QStringList            checkedNodes()const;
+  QMap<QString,QVariant> nodeParameters(const QString &node)const ;
 
 public slots:
   void addNode ();

+ 2 - 3
Libs/Widgets/ctkCheckableHeaderView.cpp

@@ -289,13 +289,12 @@ void ctkCheckableHeaderView::updateHeaderData(Qt::Orientation orient,
 void ctkCheckableHeaderView::updateHeaders(int firstSection, int lastSection)
 {
   Q_D(ctkCheckableHeaderView);
-  if(d->HeaderIsUpdating)
+  QAbstractItemModel *current = this->model();
+  if(d->HeaderIsUpdating || !current)
     {
     return;
     }
   d->HeaderIsUpdating = true;
-  QAbstractItemModel *current = this->model();
-  Q_ASSERT(current);
 
   firstSection = qBound(0, firstSection, this->count() -1);
   lastSection = qBound(0, lastSection, this->count() -1);