Procházet zdrojové kódy

Merge branch '199-create-a-directory-list-widget' into cli-module-support

Sascha Zelzer před 13 roky
rodič
revize
b02a88df12
68 změnil soubory, kde provedl 1766 přidání a 565 odebrání
  1. 4 4
      Applications/ctkEventBusDemo/CMakeLists.txt
  2. 2 2
      Applications/ctkEventBusDemo/ctkEventBusDemoMain.cpp
  3. 12 12
      Applications/ctkEventBusDemo/MainWindow.cpp
  4. 9 9
      Applications/ctkEventBusDemo/MainWindow.h
  5. 1 1
      Applications/ctkEventBusDemo/MainWindow.ui
  6. 0 0
      CMake/CTKConfig.cmake.in
  7. 0 0
      CMake/CTKConfigVersion.cmake.in
  8. 0 0
      CMake/CTKPluginUseFile.cmake.in
  9. 0 0
      CMake/CTestConfigSubProject.cmake.in
  10. 3 3
      CMake/LastConfigureStep/CTKGenerateCTKConfig.cmake
  11. 0 0
      CMake/UseCTK.cmake.in
  12. 0 0
      CMake/ctkConfig.h.in
  13. 1 1
      CMake/ctkFunctionGeneratePluginUseFile.cmake
  14. 11 2
      CMakeExternals/PythonQt.cmake
  15. 3 3
      CMakeLists.txt
  16. 25 2
      Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp
  17. 12 0
      Libs/Core/ctkCommandLineParser.cpp
  18. 2 0
      Libs/DICOM/Core/CMakeLists.txt
  19. 2 0
      Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt
  20. 131 0
      Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp
  21. 15 8
      Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp
  22. 9 0
      Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp
  23. 522 408
      Libs/DICOM/Core/ctkDICOMDatabase.cpp
  24. 29 8
      Libs/DICOM/Core/ctkDICOMDatabase.h
  25. 1 1
      Libs/DICOM/Core/ctkDICOMDataset.cpp
  26. 57 20
      Libs/DICOM/Core/ctkDICOMIndexer.cpp
  27. 2 1
      Libs/DICOM/Core/ctkDICOMIndexer.h
  28. 56 0
      Libs/DICOM/Core/ctkDICOMIndexer_p.h
  29. 4 1
      Libs/DICOM/Core/ctkDICOMRetrieve.cpp
  30. 50 1
      Libs/DICOM/Core/ctkDICOMTester.cpp
  31. 4 0
      Libs/DICOM/Core/ctkDICOMTester.h
  32. 12 9
      Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp
  33. 10 10
      Libs/PluginFramework/CMakeLists.txt
  34. 1 1
      Libs/PluginFramework/ctkLDAPSearchFilter.cpp
  35. 3 4
      Libs/PluginFramework/ctkPlugin.cpp
  36. 3 3
      Libs/PluginFramework/ctkPluginContext.cpp
  37. 4 5
      Libs/PluginFramework/ctkPluginFramework.cpp
  38. 1 2
      Libs/PluginFramework/ctkPluginFrameworkContext.cpp
  39. 1 1
      Libs/PluginFramework/ctkPluginFrameworkListeners.cpp
  40. 2 3
      Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp
  41. 3 3
      Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h
  42. 1 1
      Libs/PluginFramework/ctkPluginTracker.tpp
  43. 1 1
      Libs/PluginFramework/ctkPluginTrackerPrivate.h
  44. 0 0
      Libs/PluginFramework/ctkPluginTracker_p.tpp
  45. 3 4
      Libs/PluginFramework/ctkPluginPrivate.cpp
  46. 0 0
      Libs/PluginFramework/ctkPlugin_p.h
  47. 3 4
      Libs/PluginFramework/ctkPlugins.cpp
  48. 2 3
      Libs/PluginFramework/ctkRequirePlugin.cpp
  49. 6 6
      Libs/PluginFramework/ctkServiceReference.cpp
  50. 4 5
      Libs/PluginFramework/ctkServiceReferencePrivate.cpp
  51. 0 0
      Libs/PluginFramework/ctkServiceReference_p.h
  52. 6 7
      Libs/PluginFramework/ctkServiceRegistration.cpp
  53. 1 1
      Libs/PluginFramework/ctkServiceRegistrationPrivate.cpp
  54. 0 0
      Libs/PluginFramework/ctkServiceRegistration_p.h
  55. 1 1
      Libs/PluginFramework/ctkServiceTracker.tpp
  56. 1 1
      Libs/PluginFramework/ctkServiceTrackerPrivate.h
  57. 0 0
      Libs/PluginFramework/ctkServiceTracker_p.tpp
  58. 1 1
      Libs/PluginFramework/ctkServices.cpp
  59. 1 1
      Libs/PluginFramework/ctkServices_p.h
  60. 5 0
      Libs/Scripting/Python/Core/Testing/Cpp/ctkAbstractPythonManagerTest.cpp
  61. 1 1
      Libs/Scripting/Python/Core/ctk_library_options.cmake
  62. 9 0
      Libs/Widgets/CMakeLists.txt
  63. 97 0
      Libs/Widgets/Resources/UI/ctkDirectoryListWidget.ui
  64. 245 0
      Libs/Widgets/ctkDirectoryListView.cpp
  65. 96 0
      Libs/Widgets/ctkDirectoryListView.h
  66. 146 0
      Libs/Widgets/ctkDirectoryListWidget.cpp
  67. 69 0
      Libs/Widgets/ctkDirectoryListWidget.h
  68. 60 0
      Libs/Widgets/ctkDirectoryListWidget_p.h

+ 4 - 4
Applications/ctkEventBusDemo/CMakeLists.txt

@@ -6,18 +6,18 @@ project(ctkEventBusDemo)
 
 set(KIT_SRCS
   ctkEventBusDemoMain.cpp
-  MainWindow.cpp
-  MainWindow.h
+  ctkEventBusDemoMainWindow.cpp
+  ctkEventBusDemoMainWindow.h
 )
 
 # Headers that should run through moc
 set(KIT_MOC_SRCS
-  MainWindow.h
+  ctkEventBusDemoMainWindow.h
 )
 
 # UI files
 set(KIT_UI_FORMS
-  MainWindow.ui
+  ctkEventBusDemoMainWindow.ui
 )
 
 # Resources

+ 2 - 2
Applications/ctkEventBusDemo/ctkEventBusDemoMain.cpp

@@ -34,7 +34,7 @@
 #include <QWidget>
 #include <QFileInfo>
 
-#include "MainWindow.h"
+#include "ctkEventBusDemoMainWindow.h"
 
 int main(int argv, char** argc) {
   QApplication app(argv, argc);
@@ -104,7 +104,7 @@ int main(int argv, char** argc) {
   ctkServiceReference ebr = framework->getPluginContext()->getServiceReference("ctkEventAdminBus");
   ctkEventAdminBus *eb = framework->getPluginContext()->getService<ctkEventAdminBus>(ebr);
 
-  MainWindow win(eb);
+  ctkEventBusDemoMainWindow win(eb);
   win.show();
 
   return app.exec();

+ 12 - 12
Applications/ctkEventBusDemo/MainWindow.cpp

@@ -1,30 +1,30 @@
-#include "MainWindow.h"
-#include "ui_MainWindow.h"
+#include "ctkEventBusDemoMainWindow.h"
+#include "ui_ctkEventBusDemoMainWindow.h"
 
 #include "ctkEventAdminBus.h"
 
-MainWindow::MainWindow(QWidget *parent)
-  :  QMainWindow(parent), ui(new Ui::MainWindow)
+ctkEventBusDemoMainWindow::ctkEventBusDemoMainWindow(QWidget *parent)
+  :  QMainWindow(parent), ui(new Ui::ctkEventBusDemoMainWindow)
 {
     ui->setupUi(this);
 }
 
-MainWindow::MainWindow(ctkEventAdminBus *bus, QWidget *parent)
+ctkEventBusDemoMainWindow::ctkEventBusDemoMainWindow(ctkEventAdminBus *bus, QWidget *parent)
   : QMainWindow(parent),
-    ui(new Ui::MainWindow),  m_EventBus(bus)
+    ui(new Ui::ctkEventBusDemoMainWindow),  m_EventBus(bus)
 {
     ui->setupUi(this);
     connectEvents();
 }
 
 
-MainWindow::~MainWindow()
+ctkEventBusDemoMainWindow::~ctkEventBusDemoMainWindow()
 {
     delete handler;
     delete ui;
 }
 
-void MainWindow::connectEvents() {
+void ctkEventBusDemoMainWindow::connectEvents() {
     handler = new ctkEventDemo();
     connect(ui->btnSend, SIGNAL(released()), this, SLOT(sendEvent()));
     connect(handler, SIGNAL(updateMessageSignal(QString)), this, SLOT(updateMessage(QString)));
@@ -37,7 +37,7 @@ void MainWindow::connectEvents() {
     m_EventBus->subscribeSlot(handler, "receiveEvent(QVariantList)", dic);
 }
 
-void MainWindow::sendEvent() {
+void ctkEventBusDemoMainWindow::sendEvent() {
 
     QString textToDisplay("Me: ");
     textToDisplay.append(ui->txtParameter->property("plainText").toString());
@@ -63,7 +63,7 @@ void MainWindow::sendEvent() {
     m_EventBus->sendEvent(event);
 }
 
-void MainWindow::changeEvent(QEvent *e)
+void ctkEventBusDemoMainWindow::changeEvent(QEvent *e)
 {
     QMainWindow::changeEvent(e);
     switch (e->type()) {
@@ -75,11 +75,11 @@ void MainWindow::changeEvent(QEvent *e)
     }
 }
 
-void MainWindow::updateMessage(QString message) {
+void ctkEventBusDemoMainWindow::updateMessage(QString message) {
     ui->textBrowser->append(message);
 }
 
-void MainWindow::connectClient() {
+void ctkEventBusDemoMainWindow::connectClient() {
     bool result, resultClient, resultServer;
     resultClient = m_EventBus->createServer("XMLRPC", ui->portLineEdit->text().toInt());
     m_EventBus->startListen();

+ 9 - 9
Applications/ctkEventBusDemo/MainWindow.h

@@ -1,5 +1,5 @@
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
+#ifndef CTKEVENTBUSDEMOMAINWINDOW_H
+#define CTKEVENTBUSDEMOMAINWINDOW_H
 
 #include <QMainWindow>
 #include <QVariant>
@@ -7,7 +7,7 @@
 class ctkEventAdminBus;
 
 namespace Ui {
-    class MainWindow;
+    class ctkEventBusDemoMainWindow;
 }
 
 class ctkEventDemo : public QObject {
@@ -21,12 +21,12 @@ public Q_SLOTS:
     void receiveEvent(QVariantList l);
 };
 
-class MainWindow : public QMainWindow {
+class ctkEventBusDemoMainWindow : public QMainWindow {
     Q_OBJECT
 public:
-    MainWindow(QWidget *parent = 0);
-    MainWindow(ctkEventAdminBus *bus, QWidget *parent = 0);
-    ~MainWindow();
+    ctkEventBusDemoMainWindow(QWidget *parent = 0);
+    ctkEventBusDemoMainWindow(ctkEventAdminBus *bus, QWidget *parent = 0);
+    ~ctkEventBusDemoMainWindow();
 
 public Q_SLOTS:
     void sendEvent();
@@ -38,10 +38,10 @@ protected:
     void connectEvents();
 
 private:
-    Ui::MainWindow *ui;
+    Ui::ctkEventBusDemoMainWindow *ui;
     ctkEventAdminBus *m_EventBus;
 
     ctkEventDemo *handler;
 };
 
-#endif // MAINWINDOW_H
+#endif // CTKEVENTBUSDEMOMAINWINDOW_H

+ 1 - 1
Applications/ctkEventBusDemo/MainWindow.ui

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <class>MainWindow</class>
+ <class>ctkEventBusDemoMainWindow</class>
  <widget class="QMainWindow" name="MainWindow">
   <property name="geometry">
    <rect>

CTKConfig.cmake.in → CMake/CTKConfig.cmake.in


CTKConfigVersion.cmake.in → CMake/CTKConfigVersion.cmake.in


CTKPluginUseFile.cmake.in → CMake/CTKPluginUseFile.cmake.in


CTestConfigSubProject.cmake.in → CMake/CTestConfigSubProject.cmake.in


+ 3 - 3
CMake/LastConfigureStep/CTKGenerateCTKConfig.cmake

@@ -171,11 +171,11 @@ set(CTK_BUILD_TYPE_CONFIG ${CMAKE_BUILD_TYPE})
 
 #-----------------------------------------------------------------------------
 # Configure CTKConfig.cmake for the build tree.
-configure_file(${CTK_SOURCE_DIR}/CTKConfig.cmake.in
+configure_file(${CTK_SOURCE_DIR}/CMake/CTKConfig.cmake.in
                ${CTK_SUPERBUILD_BINARY_DIR}/CTKConfig.cmake @ONLY IMMEDIATE)
-configure_file(${CTK_SOURCE_DIR}/CTKConfigVersion.cmake.in
+configure_file(${CTK_SOURCE_DIR}/CMake/CTKConfigVersion.cmake.in
                ${CTK_SUPERBUILD_BINARY_DIR}/CTKConfigVersion.cmake @ONLY IMMEDIATE)
-configure_file(${CTK_SOURCE_DIR}/ctkConfig.h.in
+configure_file(${CTK_SOURCE_DIR}/CMake/ctkConfig.h.in
                ${CTK_CONFIG_H_INCLUDE_DIR}/ctkConfig.h @ONLY IMMEDIATE)
 
 #-----------------------------------------------------------------------------

UseCTK.cmake.in → CMake/UseCTK.cmake.in


ctkConfig.h.in → CMake/ctkConfig.h.in


+ 1 - 1
CMake/ctkFunctionGeneratePluginUseFile.cmake

@@ -53,5 +53,5 @@ set(${plugin}_LIBRARY_DIRS \"${${plugin}_LIBRARY_DIRS}\")")
   set(_ctk_plugin_libraries_variable ${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES)
   set(_ctk_plugin_libraries ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES})
 
-  configure_file("${CTK_CMAKE_DIR}/../CTKPluginUseFile.cmake.in" "${filename}" @ONLY)
+  configure_file("${CTK_CMAKE_DIR}/../CMake/CTKPluginUseFile.cmake.in" "${filename}" @ONLY)
 endfunction()

+ 11 - 2
CMakeExternals/PythonQt.cmake

@@ -33,18 +33,27 @@ if(${add_project})
       endif()
 
       # Enable Qt libraries PythonQt wrapping if required
-      foreach(qtlib core gui network opengl sql svg uitools webkit xml xmlpatterns)
+      set(qtlibs core gui network opengl sql svg uitools webkit xml)
+      foreach(qtlib All ${qtlibs})
         string(TOUPPER ${qtlib} qtlib_uppercase)
         list(APPEND ep_PythonQt_args -DPythonQt_Wrap_Qt${qtlib}:BOOL=${CTK_LIB_Scripting/Python/Core_PYTHONQT_WRAP_QT${qtlib_uppercase}})
       endforeach()
 
+      # Force wrap option to ON if WRAP_QTALL was set to ON
+      if(${CTK_LIB_Scripting/Python/Core_PYTHONQT_WRAP_QTALL})
+        foreach(qtlib ${qtlibs})
+          string(TOUPPER ${qtlib} qtlib_uppercase)
+          set(CTK_LIB_Scripting/Python/Core_PYTHONQT_WRAP_QT${qtlib_uppercase} ON CACHE BOOL "Enable Scripting/Python/Core Library PYTHONQT_WRAP_QT${qtlib_uppercase} option" FORCE)
+        endforeach()
+      endif()
+
       # Python is required
       find_package(PythonLibs)
       if(NOT PYTHONLIBS_FOUND)
         message(FATAL_ERROR "error: Python is required to build ${PROJECT_NAME}")
       endif()
 
-      set(revision_tag 9104fa924859f4a865016f2138c06ec856f449d4)
+      set(revision_tag 47738f9c8c5d3ffa77c8f2e1844f899e5b548f0c)
       if(${proj}_REVISION_TAG)
         set(revision_tag ${${proj}_REVISION_TAG})
       endif()

+ 3 - 3
CMakeLists.txt

@@ -728,8 +728,8 @@ IF (subproject_count EQUAL 0)
   set(CTEST_PROJECT_SUBPROJECTS CTKCore)
 endif()
 
-# Configure CTestConfigSubProject.cmake used that could be used by CTest scripts
-configure_file(${CTK_SOURCE_DIR}/CTestConfigSubProject.cmake.in
+# Configure CTestConfigSubProject.cmake that could be used by CTest scripts
+configure_file(${CTK_SOURCE_DIR}/CMake/CTestConfigSubProject.cmake.in
                ${CTK_BINARY_DIR}/CTestConfigSubProject.cmake)
 
 #-----------------------------------------------------------------------------
@@ -824,7 +824,7 @@ endif()
 #-----------------------------------------------------------------------------
 # Configure files with settings
 #
-configure_file(${CTK_SOURCE_DIR}/UseCTK.cmake.in
+configure_file(${CTK_SOURCE_DIR}/CMake/UseCTK.cmake.in
                ${CTK_SUPERBUILD_BINARY_DIR}/UseCTK.cmake COPYONLY IMMEDIATE)
 
 set(CTK_CONFIG_H_INCLUDE_DIR ${CTK_BINARY_DIR})

+ 25 - 2
Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp

@@ -87,17 +87,19 @@ int ctkCommandLineParserTest1(int, char*[])
     return EXIT_FAILURE;
     }
 
-  // Test3 - check if adding QString, int, and QStringList arguments works
+  // Test3 - check if adding QString, int, double, and QStringList arguments works
   QStringList arguments3;
   arguments3 << "ctkCommandLineParserTest1";
   arguments3 << "--test-string" << "TestingIsGood";
   arguments3 << "--test-string2"<< "CTKSuperRocks";
   arguments3 << "--test-integer"<< "-3";
+//  arguments3 << "--test-double"<< "-3.14";
   arguments3 << "--test-stringlist"<< "item1" << "item2" << "item3";
   ctkCommandLineParser parser3;
   parser3.addArgument("--test-string", "", QVariant::String, "This is a test string");
   parser3.addArgument("--test-string2", "", QVariant::String, "This is a test string2", "CTKGood");
   parser3.addArgument("--test-integer", "", QVariant::Int, "This is a test integer");
+//  parser3.addArgument("--test-double", "", QVariant::Double, "This is a test double");
   parser3.addArgument("--test-stringlist", "", QVariant::StringList,
                                 "This is a test stringlist");
   ok = false;
@@ -132,6 +134,14 @@ int ctkCommandLineParserTest1(int, char*[])
     return EXIT_FAILURE;
     }
 
+  //double expectedTestDouble = -3.14;
+  //if (parsedArgs["--test-double"].toDouble() != expectedTestDouble)
+  //  {
+  //  qCritical() << "Test3 - Failed - testDouble" << parsedArgs["--test-double"].toDouble()
+  //      << ", expectedTestDouble" << expectedTestDouble;
+  //  return EXIT_FAILURE;
+  //  }
+
   QStringList expectedTestStringlist;
   expectedTestStringlist << "item1" << "item2" << "item3";
   if (parsedArgs["--test-stringlist"].toStringList() != expectedTestStringlist)
@@ -579,11 +589,24 @@ int ctkCommandLineParserTest1(int, char*[])
 
   // ==================== QSettings tests ====================
 
-  QSettings settings;
+  QSettings settings("CommonTK","ctkCommandLineParserTest1");
   settings.setValue("long-settings-argument", 5);
   settings.setValue("s", "settings-short");
   settings.setValue("invalid", QVariant());
 
+  //  Check that QSettings worked
+  if(settings.status() != QSettings::NoError)
+  {
+    qCritical() << "QSettings tests setup - QSettings::status() returned " << settings.status() << ".";
+    return EXIT_FAILURE;
+  }
+
+  if (settings.value("long-settings-argument") != 5)
+  {
+    qCritical() << "QSettings tests setup - Could not store long-settings-argument in QSettings.";
+    return EXIT_FAILURE;
+  }
+
   // Test14 - Check that QSettings are used
   ctkCommandLineParser parser14(&settings);
   parser14.setArgumentPrefix("--", "-");

+ 12 - 0
Libs/Core/ctkCommandLineParser.cpp

@@ -81,6 +81,13 @@ public:
         ExactMatchFailedMessage = "A negative or positive integer is expected.";
         }
         break;
+      case QVariant::Double:
+        {
+        NumberOfParametersToProcess = 1;
+        RegularExpression = "-?[0-9]*\\.?[0-9]+";
+        ExactMatchFailedMessage = "A double is expected.";
+        }
+        break;
       default:
         ExactMatchFailedMessage = QString("Type %1 not supported.").arg(static_cast<int>(type));
       }
@@ -155,6 +162,11 @@ bool CommandLineParserArgumentDescription::addParameter(const QString& value)
       Value.setValue(value.toInt());
       }
       break;
+    case QVariant::Double:
+      {
+      Value.setValue(value.toDouble());
+      }
+      break;
     default:
       return false;
     }

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

@@ -19,6 +19,7 @@ set(KIT_SRCS
   ctkDICOMFilterProxyModel.h
   ctkDICOMIndexer.cpp
   ctkDICOMIndexer.h
+  ctkDICOMIndexer_p.h
   ctkDICOMModel.cpp
   ctkDICOMModel.h
   ctkDICOMPersonName.cpp
@@ -42,6 +43,7 @@ set(KIT_MOC_SRCS
   ctkDICOMAbstractThumbnailGenerator.h
   ctkDICOMDatabase.h
   ctkDICOMIndexer.h
+  ctkDICOMIndexer_p.h
   ctkDICOMFilterProxyModel.h
   ctkDICOMModel.h
   ctkDICOMQuery.h

+ 2 - 0
Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt

@@ -3,6 +3,7 @@ set(KIT ${PROJECT_NAME})
 create_test_sourcelist(Tests ${KIT}CppTests.cpp
   ctkDICOMCoreTest1.cpp
   ctkDICOMDatabaseTest1.cpp
+  ctkDICOMDatabaseTest2.cpp
   ctkDICOMDatasetTest1.cpp
   ctkDICOMIndexerTest1.cpp
   ctkDICOMModelTest1.cpp
@@ -29,6 +30,7 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME})
 
 # ctkDICOMDatabase
 SIMPLE_TEST(ctkDICOMDatabaseTest1)
+SIMPLE_TEST(ctkDICOMDatabaseTest2 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA)
 SIMPLE_TEST(ctkDICOMDatasetTest1)
 SIMPLE_TEST(ctkDICOMIndexerTest1 )
 

+ 131 - 0
Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp

@@ -0,0 +1,131 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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 <QCoreApplication>
+#include <QDir>
+#include <QTimer>
+
+// ctkDICOMCore includes
+#include "ctkDICOMDatabase.h"
+
+// STD includes
+#include <iostream>
+#include <cstdlib>
+
+
+int ctkDICOMDatabaseTest2( int argc, char * argv [] )
+{
+  QCoreApplication app(argc, argv);
+
+  if (argc < 2)
+    {
+    std::cerr << "ctkDICOMDatabaseTest2: missing dicom filePath argument";
+    std::cerr << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  QString dicomFilePath(argv[1]);
+
+  ctkDICOMDatabase database;
+  QDir databaseDirectory = QDir::temp();
+  QFileInfo databaseFile(databaseDirectory, QString("database.test"));
+  database.openDatabase(databaseFile.absoluteFilePath());
+
+  if (!database.lastError().isEmpty())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << qPrintable(database.lastError()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if (!database.database().isValid())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << "invalid sql database" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if (database.isInMemory())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << "database should not be in memory" << std::endl;
+    return EXIT_FAILURE;    
+    }
+
+  bool res = database.initializeDatabase();
+  
+  if (!res)
+    {
+    std::cerr << "ctkDICOMDatabase::initializeDatabase() failed." << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  //
+  // Test that the tag interface works to parse ascii
+  //
+  QString tag("0008,103e");
+  unsigned short group, element;
+  if ( !database.tagToGroupElement(tag, group, element) )
+    {
+    std::cerr << "ctkDICOMDatabase: could not parse tag" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if ( group != 0x8 || element != 0x103e )
+    {
+    std::cerr << "ctkDICOMDatabase: expected: " << "0008,103e" << std::endl;
+    std::cerr << "ctkDICOMDatabase: got: " << group << " " << element << std::endl;
+    std::cerr << "ctkDICOMDatabase: parsed tag does not match group/element" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  //
+  // Basic test:
+  // - insert the file specified on the command line
+  // - ask for tag values and compare to known results
+  //
+  database.insert(dicomFilePath, false, false);
+  QString instanceUID("1.2.840.113619.2.135.3596.6358736.4843.1115808177.83");
+
+  QString foundFile = database.fileForInstance(instanceUID);
+
+  if (foundFile != dicomFilePath)
+    {
+    std::cerr << "ctkDICOMDatabase: didn't get back the original file path" << std::endl;
+    return EXIT_FAILURE;
+    }
+  
+
+  QString knownSeriesDescription("3D Cor T1 FAST IR-prepped GRE");
+
+  QString foundSeriesDescription = database.instanceValue(instanceUID, tag);
+
+  if (foundSeriesDescription != knownSeriesDescription)
+    {
+    std::cerr << "ctkDICOMDatabase: invalid element value returned" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  database.closeDatabase();
+  database.initializeDatabase();
+
+  return EXIT_SUCCESS;
+}

+ 15 - 8
Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest1.cpp

@@ -38,21 +38,23 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
   ctkDICOMRetrieve retrieve;
 
   // check default values
-  if (!retrieve.callingAETitle().isEmpty() ||
-      !retrieve.calledAETitle().isEmpty() ||
+  std::cerr << "Checking Defaults\n";
+  if (retrieve.callingAETitle() != "ANY-SCU" ||
+      retrieve.calledAETitle() != "ANY-SCP" ||
       !retrieve.host().isEmpty() ||
-      retrieve.port() != 0 ||
+      retrieve.port() != 104 ||
       !retrieve.moveDestinationAETitle().isEmpty())
     {
     std::cerr << "ctkDICOMRetrieve::ctkDICOMRetrieve() failed: "
-              << qPrintable(retrieve.callingAETitle()) << " "
-              << qPrintable(retrieve.calledAETitle()) << " "
-              << qPrintable(retrieve.host()) << " "
-              << retrieve.port() << " "
-              << qPrintable(retrieve.moveDestinationAETitle()) << std::endl;
+              << "callingAETitle: " << qPrintable(retrieve.callingAETitle()) << " "
+              << "calledAETitle: " << qPrintable(retrieve.calledAETitle()) << " "
+              << "host: " << qPrintable(retrieve.host()) << " "
+              << "port: " << retrieve.port() << " "
+              << "moveDestinationAETitle: " << qPrintable(retrieve.moveDestinationAETitle()) << std::endl;
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Set Variables\n";
   retrieve.setCallingAETitle("CallingAETitle");
   if (retrieve.callingAETitle() != "CallingAETitle")
     {
@@ -85,6 +87,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Set Database\n";
   QSharedPointer<ctkDICOMDatabase> dicomDatabase(new ctkDICOMDatabase);
   retrieve.setDatabase(dicomDatabase);
 
@@ -95,6 +98,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Move Series\n";
   bool res = retrieve.moveSeries(QString(), QString());
   if (res)
     {
@@ -103,6 +107,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Move Study\n";
   res = retrieve.moveStudy(QString());
   if (res)
     {
@@ -111,6 +116,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Get Series\n";
   res = retrieve.getSeries(QString(), QString());
   if (res)
     {
@@ -119,6 +125,7 @@ int ctkDICOMRetrieveTest1( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "Get Study\n";
   res = retrieve.getStudy(QString());
   if (res)
     {

+ 9 - 0
Libs/DICOM/Core/Testing/Cpp/ctkDICOMRetrieveTest2.cpp

@@ -44,6 +44,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] )
   QCoreApplication app(argc, argv);
 
   ctkDICOMTester tester;
+  std::cerr << "ctkDICOMRetrieveTest2: Starting dcmqrscp\n";
   tester.startDCMQRSCP();
   
   QStringList arguments = app.arguments();
@@ -54,16 +55,19 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] )
     ctkDICOMRetrieveTest2PrintUsage();
     return EXIT_FAILURE;
     }
+  std::cerr << "ctkDICOMRetrieveTest2: Storing data to dcmqrscp\n";
   tester.storeData(arguments);
 
   ctkDICOMDatabase queryDatabase;
 
+  std::cerr << "ctkDICOMRetrieveTest2: Setting up query\n";
   ctkDICOMQuery query;
   query.setCallingAETitle("CTK_AE");
   query.setCalledAETitle("CTK_AE");
   query.setHost("localhost");
   query.setPort(tester.dcmqrscpPort());
 
+  std::cerr << "ctkDICOMRetrieveTest2: Running query\n";
   bool res = query.query(queryDatabase);
   if (!res)
     {
@@ -77,6 +81,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  std::cerr << "ctkDICOMRetrieveTest2: Setting up retrieve database\n";
   QSharedPointer<ctkDICOMDatabase> retrieveDatabase(new ctkDICOMDatabase);
   retrieveDatabase->openDatabase( "./ctkDICOM.sql" );
 
@@ -89,8 +94,10 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] )
 
   retrieve.setDatabase(retrieveDatabase);
 
+  std::cerr << "ctkDICOMRetrieveTest2: Retrieving\n";
   foreach(const QString& study, query.studyInstanceUIDQueried())
     {
+    std::cerr << "ctkDICOMRetrieveTest2: Retrieving " << study.toStdString() << "\n";
     bool res = retrieve.moveStudy(study);
     if (!res)
       {
@@ -101,5 +108,7 @@ int ctkDICOMRetrieveTest2( int argc, char * argv [] )
       }
     }
 
+  std::cerr << "ctkDICOMRetrieveTest2: Exit success\n";
+
   return EXIT_SUCCESS;
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 522 - 408
Libs/DICOM/Core/ctkDICOMDatabase.cpp


+ 29 - 8
Libs/DICOM/Core/ctkDICOMDatabase.h

@@ -113,16 +113,21 @@ public:
   ///
   /// \brief database accessors
   Q_INVOKABLE QStringList patients ();
-  Q_INVOKABLE QStringList studiesForPatient (QString patientUID);
-  Q_INVOKABLE QStringList seriesForStudy (QString studyUID);
-  Q_INVOKABLE QStringList filesForSeries (QString seriesUID);
+  Q_INVOKABLE QStringList studiesForPatient (const QString patientUID);
+  Q_INVOKABLE QStringList seriesForStudy (const QString studyUID);
+  Q_INVOKABLE QStringList filesForSeries (const QString seriesUID);
+  Q_INVOKABLE QString fileForInstance (const QString sopInstanceUID);
 
   ///
   /// \brief load the header from a file and allow access to elements
-  Q_INVOKABLE void loadInstanceHeader (QString sopInstanceUID);
-  Q_INVOKABLE void loadFileHeader (QString fileName);
+  /// @param sopInstanceUID A string with the uid for a given instance
+  ///                       (corresponding file will be found via database)
+  /// @param fileName Full path to a dicom file to load.
+  /// @param key A group,element tag in zero-filled hex
+  Q_INVOKABLE void loadInstanceHeader (const QString sopInstanceUID);
+  Q_INVOKABLE void loadFileHeader (const QString fileName);
   Q_INVOKABLE QStringList headerKeys ();
-  Q_INVOKABLE QString headerValue (QString key);
+  Q_INVOKABLE QString headerValue (const QString key);
 
   /// Insert into the database if not already exsting.
   /// @param dataset The dataset to store into the database. Usually, this is
@@ -139,7 +144,7 @@ public:
   ///                  does only make sense if a full object is received.
   /// @param @generateThumbnail If true, a thumbnail is generated.
   ///
-  void insert( const ctkDICOMDataset& ctkDataset, bool storeFile, bool generateThumbnail);
+  Q_INVOKABLE void insert( const ctkDICOMDataset& ctkDataset, bool storeFile, bool generateThumbnail);
   void insert ( DcmDataset *dataset, bool storeFile = true, bool generateThumbnail = true);
   Q_INVOKABLE void insert ( const QString& filePath, bool storeFile = true, bool generateThumbnail = true, bool createHierarchy = true, const QString& destinationDirectoryName = QString() );
   
@@ -147,12 +152,28 @@ public:
   bool fileExistsAndUpToDate(const QString& filePath);
 
   /// remove the series from the database, including images and
-  /// thumbnails  
+  /// thumbnails
   Q_INVOKABLE bool removeSeries(const QString& seriesInstanceUID);
   Q_INVOKABLE bool removeStudy(const QString& studyInstanceUID);
   Q_INVOKABLE bool removePatient(const QString& patientID);
   bool cleanup();
 
+  ///
+  /// \brief access element values for given instance
+  /// @param sopInstanceUID A string with the uid for a given instance
+  ///                       (corresponding file will be found via database)
+  /// @param fileName Full path to a dicom file to load.
+  /// @param key A group,element tag in zero-filled hex
+  /// @param group The group portion of the tag as an integer
+  /// @param element The element portion of the tag as an integer
+  /// @Returns empty string is element is missing
+  Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const QString tag);
+  Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const unsigned short group, const unsigned short element);
+  Q_INVOKABLE QString fileValue (const QString fileName, const QString tag);
+  Q_INVOKABLE QString fileValue (const QString fileName, const unsigned short group, const unsigned short element);
+  bool tagToGroupElement (const QString tag, unsigned short& group, unsigned short& element);
+
+
 Q_SIGNALS:
   void databaseChanged();
 

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

@@ -446,7 +446,7 @@ QString ctkDICOMDataset::GetAllElementValuesAsString( const DcmTag& tag ) const
     }
   }
 
-  return qsl.join("|");
+  return qsl.join("\\");
 }
 
 

+ 57 - 20
Libs/DICOM/Core/ctkDICOMIndexer.cpp

@@ -31,11 +31,13 @@
 #include <QFileInfo>
 #include <QDebug>
 #include <QPixmap>
-
+#include <QtConcurrentRun>
 
 // ctkDICOM includes
 #include "ctkLogger.h"
 #include "ctkDICOMIndexer.h"
+#include "ctkDICOMIndexer_p.h"
+#include "ctkDICOMDatabase.h"
 
 // DCMTK includes
 #include <dcmtk/dcmdata/dcfilefo.h>
@@ -49,35 +51,62 @@
 #include <dcmtk/dcmimgle/dcmimage.h>  /* for class DicomImage */
 #include <dcmtk/dcmimage/diregist.h>  /* include support for color images */
 
+class AddFileFunctor
+{
+public:
+     AddFileFunctor(ctkDICOMIndexer* indexer, ctkDICOMDatabase& database,
+                    const QString& destinationDirectoryName = "")
+       : Indexer(indexer), Database(database), DestinationDirectoryName(destinationDirectoryName) { }
+
+     bool operator()(const QString &filePath)
+     {
+         Indexer->addFile(Database,filePath,DestinationDirectoryName);
+         return false; // make sure it is removed;
+     }
+
+     ctkDICOMIndexer* Indexer;
+     ctkDICOMDatabase& Database;
+     QString DestinationDirectoryName;
+
+ };
+
 
 //------------------------------------------------------------------------------
 static ctkLogger logger("org.commontk.dicom.DICOMIndexer" );
 //------------------------------------------------------------------------------
 
-//------------------------------------------------------------------------------
-class ctkDICOMIndexerPrivate
-{
-public:
-  ctkDICOMIndexerPrivate();
-  ~ctkDICOMIndexerPrivate();
-
-  ctkDICOMAbstractThumbnailGenerator* thumbnailGenerator;
-  bool                    Canceled;
-
-};
 
 //------------------------------------------------------------------------------
 // ctkDICOMIndexerPrivate methods
 
 //------------------------------------------------------------------------------
-ctkDICOMIndexerPrivate::ctkDICOMIndexerPrivate()
+ctkDICOMIndexerPrivate::ctkDICOMIndexerPrivate(ctkDICOMIndexer& o) : q_ptr(&o), Canceled(false), CurrentPercentageProgress(-1)
 {
-  this->Canceled = false;
+  Q_Q(ctkDICOMIndexer);
+  connect(&DirectoryImportWatcher,SIGNAL(progressValueChanged(int)),this,SLOT(OnProgress(int)));
+  connect(&DirectoryImportWatcher,SIGNAL(finished()),q,SIGNAL(indexingComplete()));
+  connect(&DirectoryImportWatcher,SIGNAL(canceled()),q,SIGNAL(indexingComplete()));
 }
 
 //------------------------------------------------------------------------------
 ctkDICOMIndexerPrivate::~ctkDICOMIndexerPrivate()
 {
+  DirectoryImportWatcher.cancel();
+  DirectoryImportWatcher.waitForFinished();
+
+}
+
+void ctkDICOMIndexerPrivate::OnProgress(int progress)
+{
+  Q_Q(ctkDICOMIndexer);
+
+  int newPercentageProgress = ( 100 * DirectoryImportFuture.progressValue() ) / DirectoryImportFuture.progressMaximum();
+  if (newPercentageProgress != CurrentPercentageProgress)
+    {
+      CurrentPercentageProgress = newPercentageProgress;
+      emit q->progress(newPercentageProgress);
+    }
+
 }
 
 //------------------------------------------------------------------------------
@@ -86,7 +115,7 @@ ctkDICOMIndexerPrivate::~ctkDICOMIndexerPrivate()
 // ctkDICOMIndexer methods
 
 //------------------------------------------------------------------------------
-ctkDICOMIndexer::ctkDICOMIndexer(QObject *parent):d_ptr(new ctkDICOMIndexerPrivate)
+ctkDICOMIndexer::ctkDICOMIndexer(QObject *parent):d_ptr(new ctkDICOMIndexerPrivate(*this))
 {
   Q_UNUSED(parent);
 }
@@ -97,10 +126,11 @@ ctkDICOMIndexer::~ctkDICOMIndexer()
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMIndexer::addFile(ctkDICOMDatabase& ctkDICOMDatabase, 
-                                   const QString& filePath,
+void ctkDICOMIndexer::addFile(ctkDICOMDatabase& database,
+                                   const QString filePath,
                                    const QString& destinationDirectoryName)
 {
+  std::cout << filePath.toStdString();
   if (!destinationDirectoryName.isEmpty())
   {
     logger.warn("Ignoring destinationDirectoryName parameter, just taking it as indication we should copy!");
@@ -108,7 +138,7 @@ void ctkDICOMIndexer::addFile(ctkDICOMDatabase& ctkDICOMDatabase,
 
   emit indexingFilePath(filePath);
 
-  ctkDICOMDatabase.insert(filePath, !destinationDirectoryName.isEmpty(), true);
+  database.insert(filePath, !destinationDirectoryName.isEmpty(), true);
 }
 
 //------------------------------------------------------------------------------
@@ -118,6 +148,11 @@ void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& ctkDICOMDatabase,
 {
   Q_D(ctkDICOMIndexer);
 
+  // currently it is not supported to have multiple
+  // parallel directory imports so the second call blocks
+  //
+  d->DirectoryImportWatcher.waitForFinished();
+
   const std::string src_directory(directoryName.toStdString());
 
   OFList<OFString> originalDcmtkFileNames;
@@ -158,9 +193,11 @@ void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& ctkDICOMDatabase,
       emit progress( currentProgress );
     }
     QString filePath((*iter).c_str());
-    this->addFile(ctkDICOMDatabase, filePath, destinationDirectoryName);
+    d->FilesToIndex << filePath;
     ++iter;
   }
+  d->DirectoryImportFuture = QtConcurrent::filter(d->FilesToIndex,AddFileFunctor(this,ctkDICOMDatabase,destinationDirectoryName));
+  d->DirectoryImportWatcher.setFuture(d->DirectoryImportFuture);
 }
 
 //------------------------------------------------------------------------------
@@ -208,5 +245,5 @@ void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& dicomDatabase, const QSt
 void ctkDICOMIndexer::cancel()
 {
   Q_D(ctkDICOMIndexer);
-  d->Canceled = true;
+  d->DirectoryImportWatcher.cancel();
 }

+ 2 - 1
Libs/DICOM/Core/ctkDICOMIndexer.h

@@ -58,7 +58,7 @@ public:
   /// Scan the file using Dcmtk and populate the database with all the
   /// DICOM fields accordingly.
   ///
-  Q_INVOKABLE void addFile(ctkDICOMDatabase& database, const QString& filePath,
+  Q_INVOKABLE void addFile(ctkDICOMDatabase& database, const QString filePath,
                     const QString& destinationDirectoryName = "");
 
   Q_INVOKABLE void refreshDatabase(ctkDICOMDatabase& database, const QString& directoryName);
@@ -68,6 +68,7 @@ Q_SIGNALS:
   void indexingFileNumber(int);
   void indexingFilePath(QString);
   void progress(int);
+  void indexingComplete();
 
 public Q_SLOTS:
   void cancel();

+ 56 - 0
Libs/DICOM/Core/ctkDICOMIndexer_p.h

@@ -0,0 +1,56 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) German Cancer Research Center
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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 CTKDICOMINDEXERPRIVATE_H
+#define CTKDICOMINDEXERPRIVATE_H
+
+#include <QObject>
+
+#include "ctkDICOMIndexer.h"
+
+//------------------------------------------------------------------------------
+class ctkDICOMIndexerPrivate : public QObject
+{
+  Q_OBJECT
+
+  Q_DECLARE_PUBLIC(ctkDICOMIndexer);
+
+protected:
+  ctkDICOMIndexer* const q_ptr;
+
+public:
+  ctkDICOMIndexerPrivate(ctkDICOMIndexer&);
+  ~ctkDICOMIndexerPrivate();
+
+public Q_SLOTS:
+
+  void OnProgress(int progress);
+public:
+
+  ctkDICOMAbstractThumbnailGenerator* thumbnailGenerator;
+  bool                    Canceled;
+  QStringList FilesToIndex;
+  QFutureWatcher<void> DirectoryImportWatcher;
+  QFuture<void> DirectoryImportFuture;
+  int CurrentPercentageProgress;
+};
+
+
+#endif // CTKDICOMINDEXERPRIVATE_H

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

@@ -207,7 +207,10 @@ ctkDICOMRetrievePrivate::ctkDICOMRetrievePrivate(ctkDICOMRetrieve& obj)
 ctkDICOMRetrievePrivate::~ctkDICOMRetrievePrivate()
 {
   // At least now be kind to the server and release association
-  this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION);
+  if (this->SCU.isConnected())
+    {
+    this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION);
+    }
 }
 
 //------------------------------------------------------------------------------

+ 50 - 1
Libs/DICOM/Core/ctkDICOMTester.cpp

@@ -48,13 +48,17 @@ public:
   QString findDCMQRSCPExecutable()const;
   QString findDCMQRSCPConfigFile()const;
   QString findStoreSCUExecutable()const;
+  QString findStoreSCPExecutable()const;
   void printProcessOutputs(const QString& program, QProcess* process)const;
   
   QProcess*   DCMQRSCPProcess;
+  QProcess*   STORESCPProcess;
   QString     DCMQRSCPExecutable;
   QString     DCMQRSCPConfigFile;
   int         DCMQRSCPPort;
+  int         STORESCPPort;
   QString     StoreSCUExecutable;
+  QString     StoreSCPExecutable;
 };
 
 //------------------------------------------------------------------------------
@@ -63,17 +67,42 @@ public:
 //------------------------------------------------------------------------------
 ctkDICOMTesterPrivate::ctkDICOMTesterPrivate(ctkDICOMTester& o): q_ptr(&o)
 {
+  Q_Q(ctkDICOMTester);
+
   this->DCMQRSCPProcess = 0;
   this->DCMQRSCPExecutable = this->findDCMQRSCPExecutable();
   this->DCMQRSCPConfigFile = this->findDCMQRSCPConfigFile();
   this->DCMQRSCPPort = 11112;
+  this->STORESCPPort = 11113;
   this->StoreSCUExecutable = this->findStoreSCUExecutable();
+  this->StoreSCPExecutable = this->findStoreSCPExecutable();
+
+  // Start the storescp process and keep it active as long as this
+  // class exists
+  this->STORESCPProcess = new QProcess(q);
+  // usage of storescp:
+  //  storescp 11113
+  QStringList storescpArgs;
+  storescpArgs << QString::number(this->STORESCPPort);
+  
+  this->STORESCPProcess->start(this->StoreSCPExecutable, storescpArgs);
 }
 
 //------------------------------------------------------------------------------
 ctkDICOMTesterPrivate::~ctkDICOMTesterPrivate()
 {
-  delete this->DCMQRSCPProcess;
+  this->STORESCPProcess->terminate();
+  if (!this->STORESCPProcess->waitForFinished())
+    {
+    this->STORESCPProcess->kill();
+    }
+  this->STORESCPProcess = 0;
+
+  delete this->STORESCPProcess;
+  if (this->DCMQRSCPProcess)
+    {
+    delete this->DCMQRSCPProcess;
+    }
   this->DCMQRSCPProcess = 0;
 }
 
@@ -121,6 +150,12 @@ QString ctkDICOMTesterPrivate::findStoreSCUExecutable()const
 }
 
 //------------------------------------------------------------------------------
+QString ctkDICOMTesterPrivate::findStoreSCPExecutable()const
+{
+  return this->findFile(QStringList("storescp*"), "CMakeExternals/Install/bin");  
+}
+
+//------------------------------------------------------------------------------
 void ctkDICOMTesterPrivate::printProcessOutputs(const QString& program, QProcess* process)const
 {
   QTextStream out(stdout);
@@ -210,6 +245,20 @@ QString ctkDICOMTester::storeSCUExecutable()const
 }
 
 //------------------------------------------------------------------------------
+void ctkDICOMTester::setStoreSCPExecutable(const QString& storeSCP)
+{
+  Q_D(ctkDICOMTester);
+  d->StoreSCPExecutable = storeSCP;
+}
+
+//------------------------------------------------------------------------------
+QString ctkDICOMTester::storeSCPExecutable()const
+{
+  Q_D(const ctkDICOMTester);
+  return d->StoreSCPExecutable;
+}
+
+//------------------------------------------------------------------------------
 void ctkDICOMTester::setDCMQRSCPPort(int port)
 {
   Q_D(ctkDICOMTester);

+ 4 - 0
Libs/DICOM/Core/ctkDICOMTester.h

@@ -42,6 +42,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMTester : public QObject
   Q_PROPERTY(QString dcmqrscpExecutable READ dcmqrscpExecutable WRITE setDCMQRSCPExecutable)
   Q_PROPERTY(QString dcmqrscpConfigFile READ dcmqrscpConfigFile WRITE setDCMQRSCPConfigFile)
   Q_PROPERTY(QString storeSCUExecutable READ storeSCUExecutable WRITE setStoreSCUExecutable)
+  Q_PROPERTY(QString storeSCPExecutable READ storeSCPExecutable WRITE setStoreSCPExecutable)
   Q_PROPERTY(int dcmqrscpPort READ dcmqrscpPort WRITE setDCMQRSCPPort)
 public:
   ctkDICOMTester(QObject* parent = 0);
@@ -57,6 +58,9 @@ public:
   void setStoreSCUExecutable(const QString& storescu);
   QString storeSCUExecutable()const;
 
+  void setStoreSCPExecutable(const QString& storescp);
+  QString storeSCPExecutable()const;
+
   ///  Port number [0,65365] where the dcmqrscp and storescu communicate.
   /// Changing the port won't change the port of any running process.
   /// You must stop and restart any process you want to have its port changed

+ 12 - 9
Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp

@@ -424,30 +424,33 @@ void ctkDICOMAppWidget::onImportDirectory(QString directory)
       {
       targetDirectory = d->DICOMDatabase->databaseDirectory();
       }
-    QProgressDialog progress("DICOM Import", "Cancel", 0, 100, this,
+    QProgressDialog* progress = new QProgressDialog("DICOM Import", "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(tr("Initialization..."));
-    progress.setLabel(progressLabel);
+    progress->setLabel(progressLabel);
 #ifdef Q_WS_MAC
     // BUG: avoid deadlock of dialogs on mac
-    progress.setWindowModality(Qt::NonModal);
+    progress->setWindowModality(Qt::NonModal);
 #else
-    progress.setWindowModality(Qt::ApplicationModal);
+    progress->setWindowModality(Qt::ApplicationModal);
 #endif
-    progress.setMinimumDuration(0);
-    progress.setValue(0);
-    progress.show();
+    progress->setMinimumDuration(0);
+    progress->setValue(0);
+    progress->show();
 
-    connect(&progress, SIGNAL(canceled()), d->DICOMIndexer.data(), SLOT(cancel()));
+    connect(progress, SIGNAL(canceled()), d->DICOMIndexer.data(), SLOT(cancel()));
     connect(d->DICOMIndexer.data(), SIGNAL(indexingFilePath(QString)),
             progressLabel, SLOT(setText(QString)));
     connect(d->DICOMIndexer.data(), SIGNAL(progress(int)),
-            &progress, SLOT(setValue(int)));
+            progress, SLOT(setValue(int)));
     connect(d->DICOMIndexer.data(), SIGNAL(progress(int)),
             this, SLOT(onProgress(int)));
 
+    connect(d->DICOMIndexer.data(), SIGNAL(indexingComplete()),
+            progress, SLOT(close()));
+
     d->DICOMIndexer->addDirectory(*d->DICOMDatabase,directory,targetDirectory);
 
     d->DICOMModel.reset();

+ 10 - 10
Libs/PluginFramework/CMakeLists.txt

@@ -39,15 +39,15 @@ set(KIT_SRCS
   ctkPluginFrameworkLauncher.cpp
   ctkPluginFrameworkListeners.cpp
   ctkPluginFrameworkListeners_p.h
-  ctkPluginFrameworkPrivate.cpp
-  ctkPluginFrameworkPrivate_p.h
+  ctkPluginFramework_p.cpp
+  ctkPluginFramework_p.h
   ctkPluginFrameworkUtil.cpp
   ctkPluginFrameworkUtil_p.h
   ctkPluginLocalization.cpp
   ctkPluginManifest.cpp
   ctkPluginManifest_p.h
-  ctkPluginPrivate.cpp
-  ctkPluginPrivate_p.h
+  ctkPlugin_p.cpp
+  ctkPlugin_p.h
   ctkPlugins.cpp
   ctkPlugins_p.h
   ctkPluginStorage_p.h
@@ -55,17 +55,17 @@ set(KIT_SRCS
   ctkPluginStorageSQL_p.h
   ctkPluginTracker.h
   ctkPluginTracker.tpp
-  ctkPluginTrackerPrivate.h
-  ctkPluginTrackerPrivate.tpp
+  ctkPluginTracker_p.h
+  ctkPluginTracker_p.tpp
   ctkRequirePlugin.cpp
   ctkRequirePlugin_p.h
   ctkServiceEvent.cpp
   ctkServiceException.cpp
   ctkServiceFactory.h
   ctkServiceReference.cpp
-  ctkServiceReferencePrivate.cpp
+  ctkServiceReference_p.cpp
   ctkServiceRegistration.cpp
-  ctkServiceRegistrationPrivate.cpp
+  ctkServiceRegistration_p.cpp
   ctkServices.cpp
   ctkServices_p.h
   ctkServiceSlotEntry.cpp
@@ -73,8 +73,8 @@ set(KIT_SRCS
   ctkServiceTracker.h
   ctkServiceTracker.tpp
   ctkServiceTrackerCustomizer.h
-  ctkServiceTrackerPrivate.h
-  ctkServiceTrackerPrivate.tpp
+  ctkServiceTracker_p.h
+  ctkServiceTracker_p.tpp
   ctkTrackedPlugin_p.h
   ctkTrackedPlugin.tpp
   ctkTrackedPluginListener_p.h

+ 1 - 1
Libs/PluginFramework/ctkLDAPSearchFilter.cpp

@@ -22,7 +22,7 @@
 #include "ctkLDAPSearchFilter.h"
 
 #include "ctkLDAPExpr_p.h"
-#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceReference_p.h"
 
 //----------------------------------------------------------------------------
 class ctkLDAPSearchFilterData : public QSharedData

+ 3 - 4
Libs/PluginFramework/ctkPlugin.cpp

@@ -19,19 +19,18 @@
 
 =============================================================================*/
 
-#include "ctkPlugin.h"
+#include <QStringList>
 
+#include "ctkPlugin.h"
+#include "ctkPlugin_p.h"
 #include "ctkPluginContext.h"
 #include "ctkPluginContext_p.h"
 #include "ctkPluginFrameworkUtil_p.h"
-#include "ctkPluginPrivate_p.h"
 #include "ctkPluginArchive_p.h"
 #include "ctkPluginFrameworkContext_p.h"
 #include "ctkServices_p.h"
 #include "ctkUtils.h"
 
-#include <QStringList>
-
 //----------------------------------------------------------------------------
 ctkPlugin::ctkPlugin()
 : d_ptr(0)

+ 3 - 3
Libs/PluginFramework/ctkPluginContext.cpp

@@ -19,15 +19,15 @@
 
 =============================================================================*/
 
+#include "ctkPlugin_p.h"
 #include "ctkPluginContext.h"
 #include "ctkPluginContext_p.h"
-
-#include "ctkPluginPrivate_p.h"
 #include "ctkPluginFrameworkContext_p.h"
+
 #include "ctkServices_p.h"
 #include "ctkServiceRegistration.h"
 #include "ctkServiceReference.h"
-#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceReference_p.h"
 
 #include <stdexcept>
 

+ 4 - 5
Libs/PluginFramework/ctkPluginFramework.cpp

@@ -19,13 +19,12 @@
 
 =============================================================================*/
 
+#include "ctkPlugin_p.h"
+#include "ctkPluginArchive_p.h"
+#include "ctkPluginConstants.h"
 #include "ctkPluginFramework.h"
-
-#include "ctkPluginFrameworkPrivate_p.h"
-#include "ctkPluginPrivate_p.h"
+#include "ctkPluginFramework_p.h"
 #include "ctkPluginFrameworkContext_p.h"
-#include "ctkPluginConstants.h"
-#include "ctkPluginArchive_p.h"
 
 #include "service/event/ctkEvent.h"
 

+ 1 - 2
Libs/PluginFramework/ctkPluginFrameworkContext.cpp

@@ -20,9 +20,8 @@
 =============================================================================*/
 
 #include "ctkPluginFrameworkContext_p.h"
-
 #include "ctkPluginFrameworkUtil_p.h"
-#include "ctkPluginFrameworkPrivate_p.h"
+#include "ctkPluginFramework_p.h"
 #include "ctkPluginArchive_p.h"
 #include "ctkPluginStorageSQL_p.h"
 #include "ctkPluginConstants.h"

+ 1 - 1
Libs/PluginFramework/ctkPluginFrameworkListeners.cpp

@@ -25,7 +25,7 @@
 #include "ctkPluginFrameworkContext_p.h"
 #include "ctkPluginConstants.h"
 #include "ctkLDAPExpr_p.h"
-#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceReference_p.h"
 
 #include <QStringListIterator>
 #include <QDebug>

+ 2 - 3
Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp

@@ -19,12 +19,11 @@
 
 =============================================================================*/
 
-#include "ctkPluginFrameworkPrivate_p.h"
-
-#include "ctkPluginFramework.h"
 #include "ctkPluginConstants.h"
 #include "ctkPluginContext.h"
 #include "ctkPluginContext_p.h"
+#include "ctkPluginFramework.h"
+#include "ctkPluginFramework_p.h"
 #include "ctkPluginFrameworkContext_p.h"
 #include "ctkPluginFrameworkUtil_p.h"
 

+ 3 - 3
Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h

@@ -22,11 +22,11 @@
 #ifndef CTKPLUGINFRAMEWORKPRIVATE_P_H
 #define CTKPLUGINFRAMEWORKPRIVATE_P_H
 
-#include "ctkPluginPrivate_p.h"
-#include "ctkPluginFramework.h"
-
 #include <QMutex>
 
+#include "ctkPlugin_p.h"
+#include "ctkPluginFramework.h"
+
 class ctkPluginFrameworkContext;
 
 /**

+ 1 - 1
Libs/PluginFramework/ctkPluginTracker.tpp

@@ -21,7 +21,7 @@
 
 
 #include "ctkPluginContext.h"
-#include "ctkPluginTrackerPrivate.h"
+#include "ctkPluginTracker_p.h"
 #include "ctkTrackedPlugin_p.h"
 
 #include <QDebug>

+ 1 - 1
Libs/PluginFramework/ctkPluginTrackerPrivate.h

@@ -94,6 +94,6 @@ private:
   ctkPluginTracker<T> * const q_ptr;
 };
 
-#include "ctkPluginTrackerPrivate.tpp"
+#include "ctkPluginTracker_p.tpp"
 
 #endif // CTKPLUGINTRACKERPRIVATE_H

Libs/PluginFramework/ctkPluginTrackerPrivate.tpp → Libs/PluginFramework/ctkPluginTracker_p.tpp


+ 3 - 4
Libs/PluginFramework/ctkPluginPrivate.cpp

@@ -19,8 +19,7 @@
 
 =============================================================================*/
 
-#include "ctkPluginPrivate_p.h"
-
+#include "ctkPlugin_p.h"
 #include "ctkPluginConstants.h"
 #include "ctkPluginDatabaseException.h"
 #include "ctkPluginArchive_p.h"
@@ -29,9 +28,9 @@
 #include "ctkPluginActivator.h"
 #include "ctkPluginContext_p.h"
 
-#include "ctkServices_p.h"
-#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceReference_p.h"
 #include "ctkServiceRegistration.h"
+#include "ctkServices_p.h"
 
 // for ctk::msecsTo() - remove after switching to Qt 4.7
 #include <ctkUtils.h>

Libs/PluginFramework/ctkPluginPrivate_p.h → Libs/PluginFramework/ctkPlugin_p.h


+ 3 - 4
Libs/PluginFramework/ctkPlugins.cpp

@@ -19,19 +19,18 @@
 
 =============================================================================*/
 
-#include "ctkPlugins_p.h"
+#include <QUrl>
 
-#include "ctkPluginPrivate_p.h"
+#include "ctkPlugin_p.h"
 #include "ctkPluginArchive_p.h"
 #include "ctkPluginException.h"
 #include "ctkPluginFrameworkContext_p.h"
+#include "ctkPlugins_p.h"
 #include "ctkVersionRange_p.h"
 
 #include <stdexcept>
 #include <iostream>
 
-#include <QUrl>
-
 //----------------------------------------------------------------------------
 void ctkPlugins::checkIllegalState() const
 {

+ 2 - 3
Libs/PluginFramework/ctkRequirePlugin.cpp

@@ -19,10 +19,9 @@
 
 =============================================================================*/
 
-#include "ctkRequirePlugin_p.h"
-
+#include "ctkPlugin_p.h"
 #include "ctkPluginConstants.h"
-#include "ctkPluginPrivate_p.h"
+#include "ctkRequirePlugin_p.h"
 
 //----------------------------------------------------------------------------
 ctkRequirePlugin::ctkRequirePlugin(ctkPluginPrivate* requestor,

+ 6 - 6
Libs/PluginFramework/ctkServiceReference.cpp

@@ -19,16 +19,16 @@
 
 =============================================================================*/
 
-#include "ctkServiceReference.h"
-#include "ctkServiceReferencePrivate.h"
-#include "ctkServiceRegistrationPrivate.h"
-#include "ctkPluginPrivate_p.h"
-#include "ctkPluginConstants.h"
-
 #include <QStringList>
 #include <QMutexLocker>
 #include <QDebug>
 
+#include "ctkPlugin_p.h"
+#include "ctkPluginConstants.h"
+#include "ctkServiceReference.h"
+#include "ctkServiceReference_p.h"
+#include "ctkServiceRegistration_p.h"
+
 //----------------------------------------------------------------------------
 ctkServiceReference::ctkServiceReference()
   : d_ptr(new ctkServiceReferencePrivate(0))

+ 4 - 5
Libs/PluginFramework/ctkServiceReferencePrivate.cpp

@@ -19,19 +19,18 @@
 
 =============================================================================*/
 
-#include "ctkServiceReferencePrivate.h"
+#include "ctkServiceReference_p.h"
 
 #include <QObject>
 #include <QMutexLocker>
 
+#include "ctkPlugin_p.h"
 #include "ctkPluginConstants.h"
+#include "ctkPluginFrameworkContext_p.h"
 #include "ctkServiceFactory.h"
 #include "ctkServiceException.h"
-#include "ctkPluginPrivate_p.h"
-
 #include "ctkServices_p.h"
-#include "ctkServiceRegistrationPrivate.h"
-#include "ctkPluginFrameworkContext_p.h"
+#include "ctkServiceRegistration_p.h"
 
 //----------------------------------------------------------------------------
 ctkServiceReferencePrivate::ctkServiceReferencePrivate(ctkServiceRegistrationPrivate* reg)

Libs/PluginFramework/ctkServiceReferencePrivate.h → Libs/PluginFramework/ctkServiceReference_p.h


+ 6 - 7
Libs/PluginFramework/ctkServiceRegistration.cpp

@@ -19,18 +19,17 @@
 
 =============================================================================*/
 
-#include "ctkServiceRegistration.h"
-#include "ctkServiceRegistrationPrivate.h"
+#include <QMutex>
+
 #include "ctkPluginFrameworkContext_p.h"
-#include "ctkPluginPrivate_p.h"
+#include "ctkPlugin_p.h"
 #include "ctkPluginConstants.h"
-
-#include "ctkServices_p.h"
 #include "ctkServiceFactory.h"
+#include "ctkServiceRegistration.h"
+#include "ctkServiceRegistration_p.h"
+#include "ctkServices_p.h"
 #include "ctkServiceSlotEntry_p.h"
 
-#include <QMutex>
-
 #include <stdexcept>
 
 //----------------------------------------------------------------------------

+ 1 - 1
Libs/PluginFramework/ctkServiceRegistrationPrivate.cpp

@@ -19,7 +19,7 @@
 
 =============================================================================*/
 
-#include "ctkServiceRegistrationPrivate.h"
+#include "ctkServiceRegistration_p.h"
 
 //----------------------------------------------------------------------------
 ctkServiceRegistrationPrivate::ctkServiceRegistrationPrivate(

Libs/PluginFramework/ctkServiceRegistrationPrivate.h → Libs/PluginFramework/ctkServiceRegistration_p.h


+ 1 - 1
Libs/PluginFramework/ctkServiceTracker.tpp

@@ -20,7 +20,7 @@
 =============================================================================*/
 
 
-#include "ctkServiceTrackerPrivate.h"
+#include "ctkServiceTracker_p.h"
 #include "ctkTrackedService_p.h"
 #include "ctkServiceException.h"
 #include "ctkPluginConstants.h"

+ 1 - 1
Libs/PluginFramework/ctkServiceTrackerPrivate.h

@@ -167,6 +167,6 @@ private:
 
 };
 
-#include "ctkServiceTrackerPrivate.tpp"
+#include "ctkServiceTracker_p.tpp"
 
 #endif // CTKSERVICETRACKERPRIVATE_H

Libs/PluginFramework/ctkServiceTrackerPrivate.tpp → Libs/PluginFramework/ctkServiceTracker_p.tpp


+ 1 - 1
Libs/PluginFramework/ctkServices.cpp

@@ -31,7 +31,7 @@
 #include "ctkPluginConstants.h"
 #include "ctkPluginFrameworkContext_p.h"
 #include "ctkServiceException.h"
-#include "ctkServiceRegistrationPrivate.h"
+#include "ctkServiceRegistration_p.h"
 #include "ctkLDAPExpr_p.h"
 
 //----------------------------------------------------------------------------

+ 1 - 1
Libs/PluginFramework/ctkServices_p.h

@@ -28,8 +28,8 @@
 #include <QMutex>
 #include <QStringList>
 
+#include "ctkPlugin_p.h"
 #include "ctkServiceRegistration.h"
-#include "ctkPluginPrivate_p.h"
 
 
 /**

+ 5 - 0
Libs/Scripting/Python/Core/Testing/Cpp/ctkAbstractPythonManagerTest.cpp

@@ -11,6 +11,11 @@
 #include <iostream>
 
 //-----------------------------------------------------------------------------
+#if QT_VERSION < 0x040700
+  Q_DECLARE_METATYPE(QVariant)
+#endif
+
+//-----------------------------------------------------------------------------
 class ctkAbstractPythonManagerTester: public QObject
 {
   Q_OBJECT

+ 1 - 1
Libs/Scripting/Python/Core/ctk_library_options.cmake

@@ -7,6 +7,7 @@
 
 set(ctk_library_options
   PYTHONQT_USE_VTK:OFF
+  PYTHONQT_WRAP_QTALL:OFF
   PYTHONQT_WRAP_QTCORE:OFF
   PYTHONQT_WRAP_QTGUI:OFF
   PYTHONQT_WRAP_QTNETWORK:OFF
@@ -16,5 +17,4 @@ set(ctk_library_options
   PYTHONQT_WRAP_QTUITOOLS:OFF
   PYTHONQT_WRAP_QTWEBKIT:OFF
   PYTHONQT_WRAP_QTXML:OFF
-  PYTHONQT_WRAP_QTXMLPATTERNS:OFF
   )

+ 9 - 0
Libs/Widgets/CMakeLists.txt

@@ -63,6 +63,11 @@ set(KIT_SRCS
   ctkDateRangeWidget.h
   ctkDirectoryButton.cpp
   ctkDirectoryButton.h
+  ctkDirectoryListView.cpp
+  ctkDirectoryListView.h
+  ctkDirectoryListWidget.cpp
+  ctkDirectoryListWidget.h
+  ctkDirectoryListWidget_p.h
   ctkDoubleRangeSlider.cpp
   ctkDoubleRangeSlider.h
   ctkDoubleSlider.cpp
@@ -203,6 +208,9 @@ set(KIT_MOC_SRCS
   ctkCrosshairLabel.h
   ctkDateRangeWidget.h
   ctkDirectoryButton.h
+  ctkDirectoryListView.h
+  ctkDirectoryListWidget.h
+  ctkDirectoryListWidget_p.h
   ctkDoubleRangeSlider.h
   ctkDoubleSlider.h
   ctkDynamicSpacer.h
@@ -275,6 +283,7 @@ set(KIT_UI_FORMS
   Resources/UI/ctkThumbnailLabel.ui
   Resources/UI/ctkThumbnailListWidget.ui
   Resources/UI/ctkWorkflowGroupBox.ui
+  Resources/UI/ctkDirectoryListWidget.ui
   )
 
 # Resources

+ 97 - 0
Libs/Widgets/Resources/UI/ctkDirectoryListWidget.ui

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkDirectoryListWidget</class>
+ <widget class="QWidget" name="ctkDirectoryListWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>435</width>
+    <height>294</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="ctkDirectoryListView" name="DirectoryList" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="GroupBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Paths</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QPushButton" name="AddButton">
+        <property name="text">
+         <string>Add</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="RemoveButton">
+        <property name="text">
+         <string>Remove</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="ButtonSpacer">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>40</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="ctkExpandButton" name="ExpandButton" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ctkDirectoryListView</class>
+   <extends>QWidget</extends>
+   <header>ctkDirectoryListView.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>ctkExpandButton</class>
+   <extends>QWidget</extends>
+   <header>ctkExpandButton.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

+ 245 - 0
Libs/Widgets/ctkDirectoryListView.cpp

@@ -0,0 +1,245 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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.
+
+  This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
+  and was partially funded by NIH grant 3P41RR013218-12S1
+
+=========================================================================*/
+
+// Qt includes
+#include <QFileInfo>
+#include <QHBoxLayout>
+#include <QListView>
+#include <QStandardItemModel>
+
+// QtGUI includes
+#include "ctkDirectoryListView.h"
+
+// --------------------------------------------------------------------------
+// ctkDirectoryListViewPrivate
+
+//-----------------------------------------------------------------------------
+class ctkDirectoryListViewPrivate
+{
+  Q_DECLARE_PUBLIC(ctkDirectoryListView);
+protected:
+  ctkDirectoryListView* const q_ptr;
+
+public:
+  ctkDirectoryListViewPrivate(ctkDirectoryListView& object);
+  void init();
+
+  void addDirectory(const QString& path);
+
+  enum
+    {
+    AbsolutePathRole = Qt::UserRole + 1
+    };
+
+  QListView*         ListView;
+  QStandardItemModel DirectoryListModel;
+};
+
+// --------------------------------------------------------------------------
+// ctkDirectoryListViewPrivate methods
+
+// --------------------------------------------------------------------------
+ctkDirectoryListViewPrivate::ctkDirectoryListViewPrivate(ctkDirectoryListView& object)
+  :q_ptr(&object)
+{
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListViewPrivate::init()
+{
+  Q_Q(ctkDirectoryListView);
+
+  this->ListView = new QListView();
+  this->ListView->setSelectionBehavior(QAbstractItemView::SelectRows);
+  this->ListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+  this->ListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+  QHBoxLayout * layout = new QHBoxLayout();
+  layout->setContentsMargins(0, 0, 0, 0);
+  layout->addWidget(this->ListView);
+  q->setLayout(layout);
+
+  this->ListView->setModel(&this->DirectoryListModel);
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListViewPrivate::addDirectory(const QString& path)
+{
+  Q_Q(ctkDirectoryListView);
+  QString absolutePath = QFileInfo(path).absoluteFilePath();
+  if (!QFile::exists(absolutePath) || q->hasDirectory(absolutePath))
+    {
+    return;
+    }
+  QStandardItem * item = new QStandardItem(path);
+  item->setData(QVariant(absolutePath), Qt::ToolTipRole);
+  item->setData(QVariant(absolutePath), ctkDirectoryListViewPrivate::AbsolutePathRole);
+  this->DirectoryListModel.appendRow(item);
+}
+
+// --------------------------------------------------------------------------
+// ctkDirectoryListView methods
+
+// --------------------------------------------------------------------------
+ctkDirectoryListView::ctkDirectoryListView(QWidget* _parent)
+  : Superclass(_parent)
+  , d_ptr(new ctkDirectoryListViewPrivate(*this))
+{
+  Q_D(ctkDirectoryListView);
+  d->init();
+}
+
+// --------------------------------------------------------------------------
+ctkDirectoryListView::~ctkDirectoryListView()
+{
+}
+
+// --------------------------------------------------------------------------
+QStringList ctkDirectoryListView::directoryList(bool absolutePath)const
+{
+  Q_D(const ctkDirectoryListView);
+  QStringList directoryList;
+  int role = Qt::DisplayRole;
+  if (absolutePath)
+    {
+    role = ctkDirectoryListViewPrivate::AbsolutePathRole;
+    }
+  for(int i = 0; i < d->DirectoryListModel.rowCount(); ++i)
+    {
+    directoryList << d->DirectoryListModel.data(d->DirectoryListModel.index(i, 0), role).toString();
+    }
+  return directoryList;
+}
+
+// --------------------------------------------------------------------------
+QStringList ctkDirectoryListView::selectedDirectoryList(bool absolutePath)const
+{
+  Q_D(const ctkDirectoryListView);
+  QStringList directoryList;
+  int role = Qt::DisplayRole;
+  if (absolutePath)
+    {
+    role = ctkDirectoryListViewPrivate::AbsolutePathRole;
+    }
+  QModelIndexList selectedIndexes = d->ListView->selectionModel()->selectedRows();
+  foreach(const QModelIndex& index, selectedIndexes)
+    {
+    directoryList << d->DirectoryListModel.data(index, role).toString();
+    }
+  return directoryList;
+}
+
+// --------------------------------------------------------------------------
+bool ctkDirectoryListView::hasDirectory(const QString& path)const
+{
+  Q_D(const ctkDirectoryListView);
+  QString absolutePath = QFileInfo(path).absoluteFilePath();
+  QModelIndexList foundIndexes = d->DirectoryListModel.match(
+        d->DirectoryListModel.index(0, 0), ctkDirectoryListViewPrivate::AbsolutePathRole,
+        QVariant(absolutePath));
+  Q_ASSERT(foundIndexes.size() < 2);
+  return (foundIndexes.size() != 0);
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::addDirectory(const QString& path)
+{
+  Q_D(ctkDirectoryListView);
+  d->addDirectory(path);
+  emit this->directoryListChanged();
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::removeDirectory(const QString& path)
+{
+  Q_D(ctkDirectoryListView);
+  QList<QStandardItem*> foundItems = d->DirectoryListModel.findItems(path);
+  Q_ASSERT(foundItems.count() < 2);
+  if (foundItems.count() == 1)
+    {
+    d->DirectoryListModel.removeRow(foundItems.at(0)->row());
+    emit this->directoryListChanged();
+    }
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::removeSelectedDirectories()
+{
+  Q_D(ctkDirectoryListView);
+
+  QModelIndexList selectedIndexes = d->ListView->selectionModel()->selectedRows();
+  bool selectedCount = selectedIndexes.count();
+  while(selectedIndexes.count() > 0)
+    {
+    d->DirectoryListModel.removeRow(selectedIndexes.at(0).row());
+    selectedIndexes = d->ListView->selectionModel()->selectedRows();
+    }
+  if (selectedCount)
+    {
+    emit this->directoryListChanged();
+    }
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::selectAllDirectories()
+{
+  Q_D(ctkDirectoryListView);
+  d->ListView->selectAll();
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::clearDirectorySelection()
+{
+  Q_D(ctkDirectoryListView);
+  d->ListView->clearSelection();
+}
+
+// --------------------------------------------------------------------------
+void ctkDirectoryListView::setDirectoryList(const QStringList& paths)
+{
+  Q_D(ctkDirectoryListView);
+
+  if (paths.count() == this->directoryList().count())
+    {
+    int found = 0;
+    foreach(const QString& path, paths)
+      {
+      if (this->hasDirectory(path))
+        {
+        ++found;
+        }
+      }
+    if (found == paths.count())
+      {
+      return;
+      }
+    }
+
+  d->DirectoryListModel.removeRows(0, d->DirectoryListModel.rowCount());
+
+  foreach(const QString& path, paths)
+    {
+    d->addDirectory(path);
+    }
+  emit this->directoryListChanged();
+}
+

+ 96 - 0
Libs/Widgets/ctkDirectoryListView.h

@@ -0,0 +1,96 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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.
+
+  This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
+  and was partially funded by NIH grant 3P41RR013218-12S1
+
+=========================================================================*/
+
+#ifndef __ctkDirectoryListView_h
+#define __ctkDirectoryListView_h
+
+// Qt includes
+#include <QWidget>
+
+// QtGUI includes
+#include "ctkWidgetsExport.h"
+
+class ctkDirectoryListViewPrivate;
+
+class CTK_WIDGETS_EXPORT ctkDirectoryListView : public QWidget
+{
+  Q_OBJECT
+  Q_PROPERTY(QStringList directoryList READ directoryList WRITE setDirectoryList NOTIFY directoryListChanged)
+public:
+  /// Superclass typedef
+  typedef QWidget Superclass;
+
+  /// Constructor
+  explicit ctkDirectoryListView(QWidget* parent = 0);
+
+  /// Destructor
+  virtual ~ctkDirectoryListView();
+
+  /// Return True if the \a path has already been added
+  bool hasDirectory(const QString& path)const;
+
+  QStringList directoryList(bool absolutePath = false)const;
+
+  QStringList selectedDirectoryList(bool absolutePath = false)const;
+
+public slots:
+
+  /// If \a path exists, add it to the view and emit signal directoryListChanged().
+  /// \sa directoryListChanged()
+  void addDirectory(const QString& path);
+
+  /// Remove all entries and set \a paths has current list.
+  /// The signal directoryListChanged() is emitted if the current list of directories is
+  /// different from the provided one.
+  /// \sa addDirectory(), directoryListChanged()
+  void setDirectoryList(const QStringList& paths);
+
+  /// Remove \a path from the list.
+  /// The signal directoryListChanged() is emitted if the path was in the list.
+  /// \sa directoryListChanged()
+  void removeDirectory(const QString& path);
+
+  /// \sa selectAllDirectories()
+  void removeSelectedDirectories();
+
+  /// Select all directories.
+  void selectAllDirectories();
+
+  /// Clear the current directory selection.
+  void clearDirectorySelection();
+
+signals:
+  /// This signal is emitted when a directory is added to the view.
+  void directoryListChanged();
+
+protected:
+  QScopedPointer<ctkDirectoryListViewPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkDirectoryListView)
+  Q_DISABLE_COPY(ctkDirectoryListView)
+
+};
+
+#endif
+

+ 146 - 0
Libs/Widgets/ctkDirectoryListWidget.cpp

@@ -0,0 +1,146 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) University College London.
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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.
+
+=========================================================================*/
+
+#include <iostream>
+
+// Qt includes
+#include <QDebug>
+#include <QFileDialog>
+
+// CTK includes
+#include "ctkDirectoryListWidget.h"
+#include "ctkDirectoryListWidget_p.h"
+
+//-----------------------------------------------------------------------------
+// ctkDirectoryListWidgetPrivate methods
+
+//-----------------------------------------------------------------------------
+ctkDirectoryListWidgetPrivate::~ctkDirectoryListWidgetPrivate()
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkDirectoryListWidgetPrivate::ctkDirectoryListWidgetPrivate(ctkDirectoryListWidget& object)
+  : QObject(&object), q_ptr(&object)
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::init()
+{
+  Q_Q(ctkDirectoryListWidget);
+  this->setupUi(q);
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::setupUi(QWidget * widget)
+{
+  this->Ui_ctkDirectoryListWidget::setupUi(widget);
+
+  this->ExpandButton->setChecked(false);
+  this->ExpandButton->setMirrorOnExpand(true);
+  this->GroupBox->hide();
+
+  QObject::connect(this->AddButton, SIGNAL(clicked()),
+                   this, SLOT(onAddClicked()));
+  QObject::connect(this->RemoveButton, SIGNAL(clicked()),
+                   this, SLOT(onRemoveClicked()));
+  QObject::connect(this->ExpandButton, SIGNAL(clicked(bool)),
+                   this, SLOT(onExpandClicked(bool)));
+  QObject::connect(this->DirectoryList, SIGNAL(directoryListChanged()),
+                   this, SLOT(onDirectoryListChanged()));
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::setDirectoryList(const QStringList& list)
+{
+  this->DirectoryList->setDirectoryList(list);
+}
+
+//-----------------------------------------------------------------------------
+QStringList ctkDirectoryListWidgetPrivate::directoryList() const
+{
+  return this->DirectoryList->directoryList(true); // true for absolute path.
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::onAddClicked()
+{
+  QString path = QFileDialog::getExistingDirectory(
+        this->DirectoryList, tr("Select folder"),
+        QString(""));
+  // An empty directory means that the user cancelled the dialog.
+  if (path.isEmpty())
+    {
+    return;
+    }
+  this->DirectoryList->addDirectory(path);
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::onRemoveClicked()
+{
+  this->DirectoryList->removeSelectedDirectories();
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::onExpandClicked(bool state)
+{
+  this->GroupBox->setVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidgetPrivate::onDirectoryListChanged()
+{
+  Q_Q(ctkDirectoryListWidget);
+  emit (q->directoryListChanged(this->DirectoryList->directoryList()));
+}
+
+//-----------------------------------------------------------------------------
+// ctkDirectoryListWidget methods
+
+//-----------------------------------------------------------------------------
+ctkDirectoryListWidget::~ctkDirectoryListWidget()
+{
+}
+
+//-----------------------------------------------------------------------------
+ctkDirectoryListWidget::ctkDirectoryListWidget(QWidget* newParent)
+  : Superclass(newParent)
+  , d_ptr(new ctkDirectoryListWidgetPrivate(*this))
+{
+  Q_D(ctkDirectoryListWidget);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryListWidget::setDirectoryList(const QStringList& list)
+{
+  Q_D(ctkDirectoryListWidget);
+  d->setDirectoryList(list);
+}
+
+//-----------------------------------------------------------------------------
+QStringList ctkDirectoryListWidget::directoryList() const
+{
+  Q_D(const ctkDirectoryListWidget);
+  return d->directoryList();
+}
+

+ 69 - 0
Libs/Widgets/ctkDirectoryListWidget.h

@@ -0,0 +1,69 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) University College London.
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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 __ctkDirectoryListWidget_h
+#define __ctkDirectoryListWidget_h
+
+// Qt includes
+#include <QWidget>
+#include <QStringList>
+
+// QtGUI includes
+#include "ctkWidgetsExport.h"
+
+class ctkDirectoryListWidgetPrivate;
+
+/**
+ * \class ctkDirectoryListWidget
+ * \brief A widget to maintain a list of directories, with add and remove buttons,
+ * such as might be used in a settings panel to select a series of directories to search.
+ *
+ * \author m.clarkson@ucl.ac.uk
+ */
+class CTK_WIDGETS_EXPORT ctkDirectoryListWidget : public QWidget
+{
+  Q_OBJECT
+  Q_PROPERTY(QStringList directoryList READ directoryList WRITE setDirectoryList)
+
+public:
+  /// Superclass typedef
+  typedef QWidget Superclass;
+  ctkDirectoryListWidget(QWidget* parent = 0);
+  virtual ~ctkDirectoryListWidget();
+
+  /// Set the directory list, which will overwrite any existing list.
+  void setDirectoryList(const QStringList& list);
+  QStringList directoryList() const;
+
+public Q_SLOTS:
+
+Q_SIGNALS:
+  /// directoryListChanged emmitted whenever the list of directories is changed.
+  void directoryListChanged(const QStringList& directories);
+
+protected:
+  QScopedPointer<ctkDirectoryListWidgetPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkDirectoryListWidget)
+  Q_DISABLE_COPY(ctkDirectoryListWidget)
+};
+
+#endif

+ 60 - 0
Libs/Widgets/ctkDirectoryListWidget_p.h

@@ -0,0 +1,60 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) University College London.
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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 __ctkDirectoryListWidget_p_h
+#define __ctkDirectoryListWidget_p_h
+
+// Qt includes
+#include <QObject>
+#include <QStringList>
+
+// CTK includes
+#include "ctkDirectoryListWidget.h"
+#include "ui_ctkDirectoryListWidget.h"
+
+//-----------------------------------------------------------------------------
+/// \ingroup Widgets
+
+class ctkDirectoryListWidgetPrivate : public QObject, public Ui_ctkDirectoryListWidget
+{
+  Q_OBJECT
+  Q_DECLARE_PUBLIC(ctkDirectoryListWidget)
+protected:
+  ctkDirectoryListWidget* const q_ptr;
+public:
+  explicit ctkDirectoryListWidgetPrivate(ctkDirectoryListWidget& object);
+  virtual ~ctkDirectoryListWidgetPrivate();
+
+  void init();
+  void setupUi(QWidget * parent);
+
+  void setDirectoryList(const QStringList& list);
+  QStringList directoryList() const;
+
+public Q_SLOTS:
+  void onAddClicked();
+  void onRemoveClicked();
+  void onExpandClicked(bool);
+  void onDirectoryListChanged();
+
+public:
+};
+
+#endif