Kaynağa Gözat

Merge branch 'dicom-queries' of github.com:commontk/CTK into dicom-queries

nherlambang 14 yıl önce
ebeveyn
işleme
a14bc11eed

+ 3 - 0
Libs/DICOM/Core/CMakeLists.txt

@@ -9,6 +9,8 @@ SET(KIT_export_directive "CTK_DICOM_CORE_EXPORT")
   
 # Source files
 SET(KIT_SRCS
+  ctkDICOMImage.cpp
+  ctkDICOMImage.h
   ctkDICOMIndexer.cpp
   ctkDICOMIndexer.h
   ctkDICOMDatabase.cpp
@@ -24,6 +26,7 @@ SET(KIT_SRCS
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
   ctkDICOMDatabase.h
+  ctkDICOMImage.h
   ctkDICOMModel.h
   ctkDICOMQuery.h
   ctkDICOMRetrieve.h

+ 6 - 1
Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt

@@ -1,8 +1,9 @@
 SET(KIT ${PROJECT_NAME})
 
 CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cpp
-  ctkDICOMModelTest1.cpp
+  ctkDICOMImageTest1.cpp
   ctkDICOMTest1.cpp
+  ctkDICOMImageTest1.cpp
   )
 
 SET (TestsToRun ${Tests})
@@ -23,6 +24,10 @@ ENDMACRO( SIMPLE_TEST  )
 #
 # Add Tests
 #
+ADD_TEST( ctkDICOMImageTest1 ${KIT_TESTS}
+          ctkDICOMImageTest1 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA)
+SET_PROPERTY(TEST ctkDICOMImageTest1 PROPERTY LABELS ${PROJECT_NAME})
+
 
 ADD_TEST( ctkDICOMModelTest1 ${KIT_TESTS}
           ctkDICOMModelTest1 ${CMAKE_CURRENT_BINARY_DIR}/dicom.db

+ 40 - 0
Libs/DICOM/Core/Testing/Cpp/ctkDICOMImageTest1.cpp

@@ -0,0 +1,40 @@
+
+// Qt includes
+#include <QApplication>
+#include <QLabel>
+
+
+// ctkDICOMCore includes
+#include "ctkDICOMImage.h"
+
+// DCMTK includes
+#include <dcmimage.h>
+
+// STD includes
+#include <iostream>
+
+
+int ctkDICOMImageTest1( int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  if (argc <= 1)
+    {
+    std::cerr << "Warning, no dicom file given. Test stops" << std::endl;
+    std::cerr << "Usage: qctkDICOMImageTest1 <dicom file>" << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  DicomImage dcmtkImage(argv[1]);
+  ctkDICOMImage ctkImage(&dcmtkImage);
+
+  QLabel qtImage;
+  qtImage.setPixmap(ctkImage.getPixmap(0));
+  qtImage.show();
+
+  if (argc > 2 && QString(argv[2]) == "-I")
+    {
+      return app.exec();
+    }
+  return EXIT_SUCCESS;
+}

+ 118 - 0
Libs/DICOM/Core/ctkDICOMImage.cpp

@@ -0,0 +1,118 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  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 <QString>
+
+// ctkDICOMCore includes
+#include "ctkDICOMImage.h"
+#include "ctkLogger.h"
+
+// DCMTK includes
+#include "dcmimage.h"
+#include <ofbmanip.h>
+
+static ctkLogger logger ( "org.commontk.dicom.DICOMImage" );
+struct Node;
+
+//------------------------------------------------------------------------------
+class ctkDICOMImagePrivate
+{
+  Q_DECLARE_PUBLIC(ctkDICOMImage);
+protected:
+  ctkDICOMImage* const q_ptr;
+  
+public:
+  ctkDICOMImagePrivate(ctkDICOMImage&);
+  virtual ~ctkDICOMImagePrivate();
+
+  ::DicomImage* DicomImage;
+};
+
+//------------------------------------------------------------------------------
+ctkDICOMImagePrivate::ctkDICOMImagePrivate(ctkDICOMImage& o):q_ptr(&o)
+{
+
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMImagePrivate::~ctkDICOMImagePrivate()
+{
+
+}
+
+
+//------------------------------------------------------------------------------
+ctkDICOMImage::ctkDICOMImage(DicomImage* dicomImage, QObject* parentValue): d_ptr(new ctkDICOMImagePrivate(*this))
+{
+  Q_UNUSED(parentValue);
+  Q_D(ctkDICOMImage);
+  d->DicomImage = dicomImage;
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMImage::~ctkDICOMImage()
+{
+}
+
+unsigned long ctkDICOMImage::frameCount() const
+{
+  Q_D(const ctkDICOMImage);
+  if (d->DicomImage)
+  {
+    return d->DicomImage->getFrameCount();
+  }
+  return 0;
+}
+
+QPixmap ctkDICOMImage::getPixmap(int frame)
+{
+  Q_D(ctkDICOMImage);
+
+  // this way of converting the dicom image to a qpixmap was adopted from some code from
+  // the DCMTK forum, posted by Joerg Riesmayer, see http://forum.dcmtk.org/viewtopic.php?t=120
+  QPixmap pixmap;
+  if ((d->DicomImage != NULL) && (d->DicomImage->getStatus() == EIS_Normal))
+  {
+    /* get image extension */
+    const unsigned long width = d->DicomImage->getWidth();
+    const unsigned long height = d->DicomImage->getHeight();
+    QString header = QString("P5 %1 %2 255\n").arg(width).arg(height);
+    const unsigned long offset = header.length();
+    const unsigned long length = width * height + offset;
+    /* create output buffer for DicomImage class */
+    QByteArray buffer;
+    buffer.append(header);
+    buffer.resize(length);
+
+    /* copy PGM header to buffer */
+    d->DicomImage->setWindow(0);
+    if (d->DicomImage->getOutputData(static_cast<void *>(buffer.data() + offset), length - offset, 8, frame))
+    {
+      if (!pixmap.loadFromData(buffer, "PGM", Qt::AvoidDither))
+      {
+        logger.error("Pixmap couldn't created");
+      }
+    }
+  }
+  return pixmap;
+}

+ 52 - 0
Libs/DICOM/Core/ctkDICOMImage.h

@@ -0,0 +1,52 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  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.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMImage_h
+#define __ctkDICOMImage_h
+
+// Qt includes 
+#include <QObject>
+#include <QPixmap>
+
+#include "ctkDICOMCoreExport.h"
+
+class ctkDICOMImagePrivate;
+class DicomImage;
+
+class CTK_DICOM_CORE_EXPORT ctkDICOMImage : public QObject
+{
+  Q_OBJECT
+public:
+  explicit ctkDICOMImage(DicomImage* dicomImage, QObject* parent = 0);
+  virtual ~ctkDICOMImage();
+  QPixmap getPixmap(int frame = 0);
+  unsigned long frameCount() const;
+  Q_PROPERTY(unsigned long frameCount READ frameCount);
+
+protected:
+  QScopedPointer<ctkDICOMImagePrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkDICOMImage);
+  Q_DISABLE_COPY(ctkDICOMImage);
+};
+
+#endif

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

@@ -199,12 +199,8 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   d->query->insertEmptyElement ( DCM_StudyInstanceUID );
   d->query->insertEmptyElement ( DCM_StudyDescription );
   d->query->insertEmptyElement ( DCM_StudyDate );
-  d->query->insertEmptyElement ( DCM_StudyID );
-  d->query->insertEmptyElement ( DCM_PatientID );
-  d->query->insertEmptyElement ( DCM_PatientsName );
   d->query->insertEmptyElement ( DCM_SeriesNumber );
   d->query->insertEmptyElement ( DCM_SeriesDescription );
-  d->query->insertEmptyElement ( DCM_StudyInstanceUID );
   d->query->insertEmptyElement ( DCM_SeriesInstanceUID );
   d->query->insertEmptyElement ( DCM_StudyTime );
   d->query->insertEmptyElement ( DCM_SeriesDate );
@@ -215,6 +211,7 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   d->query->insertEmptyElement ( DCM_NumberOfSeriesRelatedInstances ); // Number of images in the series
   d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedInstances ); // Number of images in the series
   d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedSeries ); // Number of images in the series
+
   d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" );
 
   FINDResponses *responses = new FINDResponses();

+ 6 - 25
Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryRetrieveWidget.ui

@@ -30,32 +30,13 @@
         </property>
         <layout class="QVBoxLayout" name="verticalLayout_2">
          <item>
-          <widget class="QFrame" name="frame_3">
-           <property name="frameShape">
-            <enum>QFrame::StyledPanel</enum>
+          <widget class="ctkDICOMServerNodeWidget" name="serverNodeWidget" native="true">
+           <property name="minimumSize">
+            <size>
+             <width>300</width>
+             <height>200</height>
+            </size>
            </property>
-           <property name="frameShadow">
-            <enum>QFrame::Raised</enum>
-           </property>
-           <layout class="QVBoxLayout" name="verticalLayout_3">
-            <item>
-             <widget class="QLabel" name="label_2">
-              <property name="text">
-               <string>Query Targets</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="ctkDICOMServerNodeWidget" name="serverNodeWidget" native="true">
-              <property name="minimumSize">
-               <size>
-                <width>300</width>
-                <height>200</height>
-               </size>
-              </property>
-             </widget>
-            </item>
-           </layout>
           </widget>
          </item>
         </layout>

+ 21 - 19
Libs/DICOM/Widgets/Resources/UI/ctkDICOMQueryWidget.ui

@@ -33,53 +33,55 @@
        <property name="currentIndex">
         <number>0</number>
        </property>
-       <widget class="QWidget" name="Any">
-        <attribute name="title">
-         <string>Any</string>
-        </attribute>
-        <layout class="QVBoxLayout" name="verticalLayout_6">
-         <item>
-          <widget class="QLineEdit" name="AnySearch"/>
-         </item>
-        </layout>
-       </widget>
        <widget class="QWidget" name="Name">
         <attribute name="title">
          <string>Name</string>
         </attribute>
+        <attribute name="toolTip">
+         <string>Search by Person Name</string>
+        </attribute>
         <layout class="QVBoxLayout" name="verticalLayout_7">
          <item>
           <widget class="QLineEdit" name="NameSearch"/>
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="Tag">
+       <widget class="QWidget" name="Study">
         <attribute name="title">
-         <string>Tag</string>
+         <string>Study</string>
+        </attribute>
+        <attribute name="toolTip">
+         <string>Search in Study Description</string>
         </attribute>
         <layout class="QVBoxLayout" name="verticalLayout_8">
          <item>
-          <widget class="QLineEdit" name="TagSearch"/>
+          <widget class="QLineEdit" name="StudySearch"/>
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="ID">
+       <widget class="QWidget" name="Series">
         <attribute name="title">
-         <string>ID</string>
+         <string>Series</string>
+        </attribute>
+        <attribute name="toolTip">
+         <string>Search in Series Description</string>
         </attribute>
         <layout class="QVBoxLayout" name="verticalLayout_9">
          <item>
-          <widget class="QLineEdit" name="IDSearch"/>
+          <widget class="QLineEdit" name="SeriesSearch"/>
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="Number">
+       <widget class="QWidget" name="ID">
         <attribute name="title">
-         <string>Number</string>
+         <string>ID</string>
+        </attribute>
+        <attribute name="toolTip">
+         <string>Search by Person ID</string>
         </attribute>
         <layout class="QVBoxLayout" name="verticalLayout_10">
          <item>
-          <widget class="QLineEdit" name="NumberSearch"/>
+          <widget class="QLineEdit" name="IDSearch"/>
          </item>
         </layout>
        </widget>

+ 15 - 5
Libs/DICOM/Widgets/Resources/UI/ctkDICOMServerNodeWidget.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>600</width>
-    <height>510</height>
+    <width>585</width>
+    <height>544</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -15,9 +15,19 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Calling AETitle</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="callingAETitle"/>
+   </item>
+   <item>
     <widget class="QLabel" name="label">
      <property name="text">
-      <string>Sources</string>
+      <string>Servers</string>
      </property>
     </widget>
    </item>
@@ -31,8 +41,8 @@
        <rect>
         <x>0</x>
         <y>0</y>
-        <width>580</width>
-        <height>414</height>
+        <width>565</width>
+        <height>392</height>
        </rect>
       </property>
       <layout class="QVBoxLayout" name="verticalLayout_2">

+ 43 - 0
Libs/DICOM/Widgets/Resources/UI/ctkDICOMThumbnailListWidget.ui

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkDICOMThumbnailListWidget</class>
+ <widget class="QWidget" name="ctkDICOMThumbnailListWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>224</width>
+    <height>237</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QScrollArea" name="scrollArea">
+     <property name="widgetResizable">
+      <bool>true</bool>
+     </property>
+     <widget class="QWidget" name="scrollAreaContentWidget">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>198</width>
+        <height>211</height>
+       </rect>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <layout class="QGridLayout" name="thumbnailLayout"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 44 - 47
Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp

@@ -3,6 +3,9 @@
 #include <QTabBar>
 #include <QSettings>
 
+/// CTK includes
+#include <ctkCheckableHeaderView.h>
+
 // ctkDICOMCore includes
 #include "ctkDICOMDatabase.h"
 #include "ctkDICOMModel.h"
@@ -25,6 +28,7 @@ public:
   ctkDICOMQueryRetrieveWidgetPrivate(){}
 
   QMap<QString, ctkDICOMQuery*> queries;
+  ctkDICOMModel model;
 };
 
 //----------------------------------------------------------------------------
@@ -70,50 +74,7 @@ void ctkDICOMQueryRetrieveWidget::setRetrieveDatabaseFileName(const QString& fil
 void ctkDICOMQueryRetrieveWidget::processQuery()
 {
   Q_D(ctkDICOMQueryRetrieveWidget);
-
-  ctkDICOMQuery query;
-
-  // TODO: convert widget to query parameters
-  // TODO: add interface to ctkDICOMQuery for specifying query params
-  //d->queryWidget->populateQuery();
-
-  QStringList nodes = d->serverNodeWidget->nodes();
-  foreach (QString node, nodes)
-  {
-    d->queries[node] = new ctkDICOMQuery;
-    QMap<QString, QString> parameters = d->serverNodeWidget->nodeParameters(node);
-    d->queries[node]->setCallingAETitle(node);
-  }
-
-#if 0
-TODO: map the server node options to the query classes
-
-  query.setCallingAETitle ( QString ( argv[2] ) );
-  query.setCalledAETitle ( QString ( argv[3] ) );
-  query.setHost ( QString ( argv[4] ) );
-  int port;
-  bool ok;
-  port = QString ( argv[5] ).toInt ( &ok );
-  if ( !ok )
-    {
-    std::cerr << "Could not convert " << argv[5] << " to an integer" << std::endl;
-    print_usage();
-    return EXIT_FAILURE;
-    }
-  query.setPort ( port );
-
-  try
-    {
-    query.query ( myCTK.database() );
-    }
-  catch (std::exception e)
-  {
-    return EXIT_FAILURE;
-  }
-  return EXIT_SUCCESS;
-#endif
-
-  // TODO: create a map of server locations to query results in the private class
+  
   ctkDICOMDatabase queryResultDatabase;
 
   try { queryResultDatabase.openDatabase( ":memory:" ); }
@@ -124,7 +85,43 @@ TODO: map the server node options to the query classes
     return;
   }
 
-  ctkDICOMModel model;
-  model.setDatabase(queryResultDatabase.database());
-  d->results->setModel(&model);
+  QStringList serverNodes = d->serverNodeWidget->nodes();
+  foreach (QString server, serverNodes)
+  {
+    QMap<QString, QString> parameters = d->serverNodeWidget->nodeParameters(server);
+    if ( parameters["CheckState"] == QVariant(Qt::Checked).toString() )
+    {
+      d->queries[server] = new ctkDICOMQuery;
+      d->queries[server]->setCallingAETitle(d->serverNodeWidget->callingAETitle());
+      d->queries[server]->setCalledAETitle(parameters["AETitle"]);
+      d->queries[server]->setHost(parameters["Address"]);
+      d->queries[server]->setPort(parameters["Port"].toInt());
+      // TODO: add interface to ctkDICOMQuery for specifying query params
+      // for now, query for everything
+
+      try
+      {
+        // run the query against the selected server and put results in database
+        d->queries[server]->query ( queryResultDatabase );
+      }
+      catch (std::exception e)
+      {
+        logger.error ( "Query error: " + parameters["Name"] );
+      }
+    }
+  }
+
+  // checkable headers.
+  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);
+
+  d->model.setDatabase(queryResultDatabase.database());
+  d->results->setModel(&d->model);
 }

+ 25 - 25
Libs/DICOM/Widgets/ctkDICOMServerNodeWidget.cpp

@@ -75,22 +75,19 @@ ctkDICOMServerNodeWidget::ctkDICOMServerNodeWidget(QWidget* _parent):Superclass(
   QMap<QString, QVariant> node;
   if ( settings.value("ServerNodeCount").toInt() == 0 )
   {
-    node["Name"] = "Local Database";
-    node["CheckState"] = Qt::Checked;
-    node["AETitle"] = "N/A";
-    node["Address"] = "N/A";
-    node["Port"] = "N/A";
-    settings.setValue("ServerNodeCount", 2);
+    settings.setValue("ServerNodeCount", 1);
     settings.setValue("ServerNodes/0", QVariant(node));
     node["Name"] = "ExampleHost";
-    node["CheckState"] = Qt::Unchecked;
-    node["AETitle"] = "CTK_AE";
+    node["CheckState"] = Qt::Checked;
+    node["AETitle"] = "ANY-SCP";
     node["Address"] = "localhost";
     node["Port"] = "11112";
     settings.setValue("ServerNodes/1", QVariant(node));
+    settings.setValue("CallingAETitle", "FINDSCU");
     settings.sync();
   }
 
+  d->callingAETitle->setText(settings.value("CallingAETitle").toString());
   int count = settings.value("ServerNodeCount").toInt();
   d->nodeTable->setRowCount(count);
   for (int row = 0; row < count; row++)
@@ -108,22 +105,16 @@ ctkDICOMServerNodeWidget::ctkDICOMServerNodeWidget(QWidget* _parent):Superclass(
     d->nodeTable->setItem(row, 3, newItem);
   }
 
-  connect(d->addButton
-    ,SIGNAL(clicked()),
-    this,
-    SLOT(addNode()));
-  connect(d->removeButton
-    ,SIGNAL(clicked()),
-    this,
-    SLOT(removeNode()));
-  connect(d->nodeTable,
-    SIGNAL(cellChanged(int,int)),
-    this,
-    SLOT(onCellChanged(int,int)));
-  connect(d->nodeTable,
-    SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)),
-    this,
-    SLOT(onCurrentItemChanged(QTableWidgetItem*, QTableWidgetItem*)));
+  connect(d->callingAETitle, SIGNAL(textChanged(const QString&)),
+    this, SLOT(saveSettings()));
+  connect(d->addButton, SIGNAL(clicked()),
+    this, SLOT(addNode()));
+  connect(d->removeButton, SIGNAL(clicked()),
+    this, SLOT(removeNode()));
+  connect(d->nodeTable, SIGNAL(cellChanged(int,int)),
+    this, SLOT(onCellChanged(int,int)));
+  connect(d->nodeTable, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)),
+    this, SLOT(onCurrentItemChanged(QTableWidgetItem*, QTableWidgetItem*)));
 }
 
 //----------------------------------------------------------------------------
@@ -195,10 +186,19 @@ void ctkDICOMServerNodeWidget::saveSettings()
     }
   }
   settings.setValue("ServerNodeCount", count);
+  settings.setValue("CallingAETitle", d->callingAETitle->text());
   settings.sync();
 }
 
 //----------------------------------------------------------------------------
+QString ctkDICOMServerNodeWidget::callingAETitle()
+{
+  Q_D(ctkDICOMServerNodeWidget);
+
+  return d->callingAETitle->text();
+}
+
+//----------------------------------------------------------------------------
 QStringList ctkDICOMServerNodeWidget::nodes()
 {
   Q_D(ctkDICOMServerNodeWidget);
@@ -231,7 +231,7 @@ QMap<QString, QString> ctkDICOMServerNodeWidget::nodeParameters(QString &node)
         {
           parameters[keys.at(k)] = d->nodeTable->item(row,k)->text();
         }
-        parameters["CheckState"] = d->nodeTable->item(row,0)->checkState();
+        parameters["CheckState"] = QVariant(d->nodeTable->item(row,0)->checkState()).toString();
       }
     }
   }

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

@@ -40,6 +40,7 @@ public:
   explicit ctkDICOMServerNodeWidget(QWidget* parent=0);
   virtual ~ctkDICOMServerNodeWidget();
 
+  QString callingAETitle();
   QStringList nodes();
   QMap<QString,QString> nodeParameters(QString &node);
 

+ 126 - 0
Libs/DICOM/Widgets/ctkDICOMThumbnailListWidget.cpp

@@ -0,0 +1,126 @@
+
+// Qt include
+#include <QGridLayout>
+#include <QResizeEvent>
+
+// ctkDICOMWidgets includes
+#include "ctkDICOMThumbnailListWidget.h"
+#include "ui_ctkDICOMThumbnailListWidget.h"
+#include "ctkDICOMThumbnailWidget.h"
+
+// STD includes
+#include <iostream>
+
+//----------------------------------------------------------------------------
+class ctkDICOMThumbnailListWidgetPrivate: public Ui_ctkDICOMThumbnailListWidget
+{
+public:
+  ctkDICOMThumbnailListWidgetPrivate(){}
+
+  void rearrangeThumbnails();
+
+  QList<ctkDICOMThumbnailWidget*> thumbnailList;
+  int maxColumnNum;
+  int thumbnailWidth;
+};
+
+//----------------------------------------------------------------------------
+// ctkDICOMThumbnailListWidgetPrivate methods
+
+void ctkDICOMThumbnailListWidgetPrivate::rearrangeThumbnails(){
+  int count = this->thumbnailList.count();
+
+  // clear all thumbnails from layout
+  for(int i = 0; i < count; i++){
+    this->thumbnailLayout->removeWidget(this->thumbnailList.at(i));
+  }
+
+  // add all thumbnails to layout with new arrangement
+  int row = 0;
+  int column = 0;
+  for(int i = 0; i < count; i++){
+    this->thumbnailLayout->addWidget(this->thumbnailList.at(i), row, column);
+    column++;
+    if(column >= this->maxColumnNum){
+      column = 0;
+      row++;
+    }
+  }
+}
+
+//----------------------------------------------------------------------------
+// ctkDICOMThumbnailListWidget methods
+
+//----------------------------------------------------------------------------
+ctkDICOMThumbnailListWidget::ctkDICOMThumbnailListWidget(QWidget* _parent):Superclass(_parent), 
+  d_ptr(new ctkDICOMThumbnailListWidgetPrivate)
+{
+  Q_D(ctkDICOMThumbnailListWidget);
+  
+  d->setupUi(this);
+  d->maxColumnNum = 4;
+  d->thumbnailWidth = 128;
+}
+
+//----------------------------------------------------------------------------
+ctkDICOMThumbnailListWidget::~ctkDICOMThumbnailListWidget()
+{
+}
+
+void ctkDICOMThumbnailListWidget::clearThumbnail(){
+  Q_D(ctkDICOMThumbnailListWidget);
+  
+  int count = d->thumbnailList.count();
+
+  // clear all thumbnails from layout
+  for(int i = 0; i < count; i++){
+    d->thumbnailLayout->removeWidget(d->thumbnailList.at(i));
+  }
+
+  d->thumbnailList.clear();
+}
+
+void ctkDICOMThumbnailListWidget::addThumbnail(ctkDICOMThumbnailWidget* widget){
+  Q_D(ctkDICOMThumbnailListWidget);
+  if(widget == NULL)return;
+
+  d->thumbnailList.push_back(widget);
+
+  int count = d->thumbnailList.count();
+
+  int lastRow = (count/d->maxColumnNum);
+  int lastColumn = (count%d->maxColumnNum);
+
+  int nextRow = (lastRow+((lastColumn+1)/d->maxColumnNum));
+  int nextColumn = (lastColumn+1)%d->maxColumnNum;
+
+  d->thumbnailLayout->addWidget(widget, nextRow, nextColumn);
+}
+
+void ctkDICOMThumbnailListWidget::addTestThumbnail(){
+  Q_D(ctkDICOMThumbnailListWidget);
+  for(int i = 0; i<11; i++){
+    ctkDICOMThumbnailWidget* widget = new ctkDICOMThumbnailWidget(this);
+    QString text("Thumbnail: ");
+    widget->setMaximumWidth(d->thumbnailWidth);
+    widget->setText(text);
+    this->addThumbnail(widget);
+  }
+}
+
+void ctkDICOMThumbnailListWidget::setThumbnailWidth(int width){
+  Q_D(ctkDICOMThumbnailListWidget);
+  
+  d->thumbnailWidth = width;
+
+  d->maxColumnNum = this->width()/d->thumbnailWidth;
+  d->rearrangeThumbnails();
+}
+
+void ctkDICOMThumbnailListWidget::resizeEvent ( QResizeEvent * event ){
+  Q_D(ctkDICOMThumbnailListWidget);
+  
+  d->maxColumnNum = event->size().width()/d->thumbnailWidth;
+  d->rearrangeThumbnails();
+}
+

+ 57 - 0
Libs/DICOM/Widgets/ctkDICOMThumbnailListWidget.h

@@ -0,0 +1,57 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMThumbnailsListWidget_h
+#define __ctkDICOMThumbnailsListWidget_h
+
+// Qt includes 
+#include <QWidget>
+
+#include "ctkDICOMWidgetsExport.h"
+
+class ctkDICOMThumbnailListWidgetPrivate;
+
+class ctkDICOMThumbnailWidget;
+
+class CTK_DICOM_WIDGETS_EXPORT ctkDICOMThumbnailListWidget : public QWidget
+{
+public:
+  typedef QWidget Superclass;
+  explicit ctkDICOMThumbnailListWidget(QWidget* parent=0);
+  virtual ~ctkDICOMThumbnailListWidget();
+
+  void setThumbnailWidth(int width);
+
+  void addTestThumbnail();
+  
+protected:
+  QScopedPointer<ctkDICOMThumbnailListWidgetPrivate> d_ptr;
+
+  void clearThumbnail();
+  void addThumbnail(ctkDICOMThumbnailWidget* widget);
+
+  virtual void resizeEvent(QResizeEvent * event);
+  
+private:
+  Q_DECLARE_PRIVATE(ctkDICOMThumbnailListWidget);
+  Q_DISABLE_COPY(ctkDICOMThumbnailListWidget);
+};
+
+#endif