浏览代码

Sample app to create a GUI for a CLI module.

Sascha Zelzer 13 年之前
父节点
当前提交
c20de1723e
共有 26 个文件被更改,包括 1332 次插入29 次删除
  1. 11 1
      Applications/ctkCLIPluginExplorer/CMakeLists.txt
  2. 91 8
      Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMain.cpp
  3. 155 0
      Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.cpp
  4. 69 0
      Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.h
  5. 98 0
      Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.ui
  6. 6 0
      Applications/ctkCLIPluginExplorer/resources/ctkCLIPluginExplorer.qrc
  7. 二进制
      Applications/ctkCLIPluginExplorer/resources/run.png
  8. 二进制
      Applications/ctkCLIPluginExplorer/resources/stop.png
  9. 12 4
      Libs/ModuleDescription/CMakeLists.txt
  10. 1 1
      Libs/ModuleDescription/ctkModuleDescriptionParser.cpp
  11. 3 3
      Libs/ModuleDescription/ctkModuleDescriptionValidator.cpp
  12. 2 2
      Libs/ModuleDescription/ctkModuleDescriptionValidator.h
  13. 104 6
      Libs/ModuleDescription/ctkModuleManager.cpp
  14. 30 2
      Libs/ModuleDescription/ctkModuleManager.h
  15. 25 2
      Libs/ModuleDescription/ctkModuleObjectHierarchyReader.cpp
  16. 7 0
      Libs/ModuleDescription/ctkModuleObjectHierarchyReader.h
  17. 59 0
      Libs/ModuleDescription/ctkModuleProcessException.cpp
  18. 56 0
      Libs/ModuleDescription/ctkModuleProcessException.h
  19. 97 0
      Libs/ModuleDescription/ctkModuleProcessFuture.cpp
  20. 138 0
      Libs/ModuleDescription/ctkModuleProcessFuture.h
  21. 74 0
      Libs/ModuleDescription/ctkModuleProcessRunner.cpp
  22. 57 0
      Libs/ModuleDescription/ctkModuleProcessRunner_p.h
  23. 74 0
      Libs/ModuleDescription/ctkModuleReference.cpp
  24. 62 0
      Libs/ModuleDescription/ctkModuleReference.h
  25. 44 0
      Libs/ModuleDescription/ctkModuleReferencePrivate.cpp
  26. 57 0
      Libs/ModuleDescription/ctkModuleReferencePrivate.h

+ 11 - 1
Applications/ctkCLIPluginExplorer/CMakeLists.txt

@@ -6,20 +6,28 @@ project(ctkCLIPluginExplorer)
 
 
 set(KIT_SRCS
 set(KIT_SRCS
   ctkCLIPluginExplorerMain.cpp
   ctkCLIPluginExplorerMain.cpp
+  ctkCLIPluginExplorerMainWindow.h
+  ctkCLIPluginExplorerMainWindow.cpp
 )
 )
 
 
 # Headers that should run through moc
 # Headers that should run through moc
 set(KIT_MOC_SRCS
 set(KIT_MOC_SRCS
-  )
+  ctkCLIPluginExplorerMainWindow.h
+)
 
 
 # UI files
 # UI files
 set(KIT_UI_FORMS
 set(KIT_UI_FORMS
+  ctkCLIPluginExplorerMainWindow.ui
 )
 )
 
 
 # Resources
 # Resources
 set(KIT_resources
 set(KIT_resources
+  resources/ctkCLIPluginExplorer.qrc
 )
 )
 
 
+set(QT_USE_QTUITOOLS 1)
+include(${QT_USE_FILE})
+
 # Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake
 # Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake
 # The following macro will read the target libraries from the file 'target_libraries.cmake'
 # The following macro will read the target libraries from the file 'target_libraries.cmake'
 ctkFunctionGetTargetLibraries(KIT_target_libraries)
 ctkFunctionGetTargetLibraries(KIT_target_libraries)
@@ -33,6 +41,8 @@ ctkMacroBuildApp(
   RESOURCES ${KIT_resources}
   RESOURCES ${KIT_resources}
   )
   )
 
 
+target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES})
+
 # Testing
 # Testing
 if(BUILD_TESTING)
 if(BUILD_TESTING)
 #   add_subdirectory(Testing)
 #   add_subdirectory(Testing)

+ 91 - 8
Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMain.cpp

@@ -20,23 +20,106 @@
 =============================================================================*/
 =============================================================================*/
 
 
 // Qt includes
 // Qt includes
-#include <QCoreApplication>
+#include <QApplication>
 #include <QDebug>
 #include <QDebug>
 #include <QFile>
 #include <QFile>
+#include <QBuffer>
+#include <QWidget>
+
+#include <QtXmlPatterns/QXmlQuery>
+#include <QtUiTools/QUiLoader>
 
 
 // CTK includes
 // CTK includes
-#include <ctkModuleDescription.h>
+#include <ctkCommandLineParser.h>
+#include <ctkModuleDescriptionValidator.h>
+
+#include "ctkCLIPluginExplorerMainWindow.h"
 
 
 int main(int argc, char** argv)
 int main(int argc, char** argv)
 {
 {
-  QCoreApplication app(argc, argv);
+  QApplication myApp(argc, argv);
+
+  ctkCommandLineParser cmdLineParser;
+  cmdLineParser.setArgumentPrefix("--", "-");
+  cmdLineParser.setStrictModeEnabled(true);
+
+  cmdLineParser.addArgument("cli", "", QVariant::String, "Path to a CLI module (executable)");
+  cmdLineParser.addArgument("cli-xml", "", QVariant::String, "Path to a CLI XML description.");
+
+  cmdLineParser.addArgument("validate-plugin", "", QVariant::String, "Path to a CLI plug-in");
+  cmdLineParser.addArgument("validate-xml", "", QVariant::String, "Path to a CLI XML description.");
+  cmdLineParser.addArgument("verbose", "v", QVariant::Bool, "Be verbose.");
+  cmdLineParser.addArgument("help", "h", QVariant::Bool, "Print this help text.");
+
+  bool parseOkay = false;
+  QHash<QString, QVariant> args = cmdLineParser.parseArguments(argc, argv, &parseOkay);
+
+  QTextStream out(stdout, QIODevice::WriteOnly);
+
+  if(!parseOkay)
+  {
+    out << "Error parsing command line arguments: " << cmdLineParser.errorString() << '\n';
+    return EXIT_FAILURE;
+  }
+
+  if (args.contains("help"))
+  {
+    out << "Usage:\n" << cmdLineParser.helpText();
+    out.flush();
+    return EXIT_SUCCESS;
+  }
+
+  if (args.contains("validate-xml"))
+  {
+    QFile input(args["validate-xml"].toString());
+    if (!input.exists())
+    {
+      qCritical() << "XML description does not exist:" << input.fileName();
+      return EXIT_FAILURE;
+    }
+    input.open(QIODevice::ReadOnly);
+
+    ctkModuleDescriptionValidator validator(&input);
+    if (!validator.validate())
+    {
+      qCritical() << validator.errorString();
+      return EXIT_FAILURE;
+    }
+
+    if (args.contains("verbose"))
+    {
+      qDebug() << "=================================================";
+      qDebug() << "****          Transformed input              ****";
+      qDebug() << "=================================================";
+      qDebug() << validator.output();
+    }
+    return EXIT_SUCCESS;
+  }
+
+
+  //ctkModuleDescription* descr = ctkModuleDescription::parse(&input);
+
+  ctkCLIPluginExplorerMainWindow mainWindow;
 
 
-  QIODevice* input = new QFile("/home/sascha/tmp/slicer_md1.xml");
+  if (args.contains("cli-xml"))
+  {
+    QFile input(args["cli-xml"].toString());
+    if (!input.exists())
+    {
+      qCritical() << "XML description does not exist:" << input.fileName();
+      return EXIT_FAILURE;
+    }
+    input.open(QIODevice::ReadOnly);
+    QByteArray xml = input.readAll();
 
 
-  ctkModuleDescription* descr = ctkModuleDescription::parse(input);
+    mainWindow.testModuleXML(xml);
+  }
+  else if (args.contains("cli"))
+  {
+    mainWindow.addModule(args["cli"].toString());
+  }
 
 
-  QTextStream cout(stdout, QIODevice::WriteOnly);
-  cout << *descr;
+  mainWindow.show();
 
 
-  return 0;
+  return myApp.exec();
 }
 }

+ 155 - 0
Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.cpp

@@ -0,0 +1,155 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkCLIPluginExplorerMainWindow.h"
+#include "ui_ctkCLIPluginExplorerMainWindow.h"
+
+#include <ctkModuleDescriptionValidator.h>
+#include <ctkModuleManager.h>
+#include <ctkModuleProcessFuture.h>
+
+#include <QFile>
+#include <QBuffer>
+#include <QUiLoader>
+#include <QDebug>
+
+class ctkModuleDescriptionDefaultFactory : public ctkModuleDescriptionFactory
+{
+public:
+
+  QObject* createObjectRepresentationFromXML(const QByteArray &xmlDescription)
+  {
+    return cachedObjectTree(xmlDescription);
+  }
+
+  QObject* createGUIFromXML(const QByteArray &xmlDescription)
+  {
+    return cachedObjectTree(xmlDescription);
+  }
+
+private:
+
+  QObject* cachedObjectTree(const QByteArray& xmlDescription)
+  {
+    QObject* root = cache[xmlDescription];
+    if (root != 0) return root;
+
+    QBuffer input;
+    input.setData(xmlDescription);
+    input.open(QIODevice::ReadOnly);
+
+    ctkModuleDescriptionValidator validator(&input);
+    if (!validator.validateXSLTOutput())
+    {
+      qCritical() << validator.errorString();
+      return 0;
+    }
+
+    QUiLoader uiLoader;
+    QByteArray uiBlob;
+    uiBlob.append(validator.output());
+    qDebug() << validator.output();
+    QBuffer uiForm(&uiBlob);
+
+    root = uiLoader.load(&uiForm);
+    cache[xmlDescription] = root;
+    return root;
+  }
+
+  // TODO: remove entry if QObject was deleted
+  QHash<QByteArray, QObject*> cache;
+};
+
+
+ctkCLIPluginExplorerMainWindow::ctkCLIPluginExplorerMainWindow(QWidget *parent) :
+  QMainWindow(parent),
+  ui(new Ui::ctkCLIPluginExplorerMainWindow),
+  factory(new ctkModuleDescriptionDefaultFactory),
+  moduleManager(factory)
+{
+  ui->setupUi(this);
+}
+
+ctkCLIPluginExplorerMainWindow::~ctkCLIPluginExplorerMainWindow()
+{
+  delete ui;
+  delete factory;
+}
+
+void ctkCLIPluginExplorerMainWindow::addModuleTab(const ctkModuleReference& moduleRef)
+{
+  if (moduleRef.widgetTree() == 0) return;
+
+  QWidget* widget = qobject_cast<QWidget*>(moduleRef.widgetTree());
+  int tabIndex = ui->mainTabWidget->addTab(widget, widget->objectName());
+  mapTabToModuleRef[tabIndex] = moduleRef;
+}
+
+void ctkCLIPluginExplorerMainWindow::addModule(const QString &location)
+{
+  ctkModuleReference ref = moduleManager.addModule(location);
+  if (ref.isValid())
+  {
+    addModuleTab(ref);
+  }
+}
+
+void ctkCLIPluginExplorerMainWindow::testModuleXML(const QByteArray &xml)
+{
+  QObject* root = factory->createGUIFromXML(xml);
+  if (root)
+  {
+    QWidget* widget = qobject_cast<QWidget*>(root);
+    int tabIndex = ui->mainTabWidget->addTab(widget, widget->objectName());
+    //mapTabToModuleRef[tabIndex] = moduleRef;
+  }
+}
+
+void ctkCLIPluginExplorerMainWindow::on_actionRun_triggered()
+{
+  qDebug() << "Creating module command line...";
+
+  QStringList cmdLineArgs = ctkModuleManager::createCommandLineArgs(ui->mainTabWidget->currentWidget());
+  qDebug() << cmdLineArgs;
+
+  ctkModuleReference moduleRef = mapTabToModuleRef[ui->mainTabWidget->currentIndex()];
+  if (!moduleRef.isValid())
+  {
+    qWarning() << "Invalid module reference";
+    return;
+  }
+
+  connect(&futureWatcher, SIGNAL(finished()), this, SLOT(futureFinished()));
+  ctkModuleProcessFuture future = moduleManager.run(moduleRef);
+  futureWatcher.setFuture(future);
+}
+
+void ctkCLIPluginExplorerMainWindow::futureFinished()
+{
+  qDebug() << "*** Future finished";
+  qDebug() << "stdout:" << futureWatcher.future().standardOutput();
+  qDebug() << "stderr:" << futureWatcher.future().standardError();
+}
+
+ctkModuleReference ctkCLIPluginExplorerMainWindow::moduleReference(int tabIndex)
+{
+  return mapTabToModuleRef[tabIndex];
+}

+ 69 - 0
Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.h

@@ -0,0 +1,69 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKCLIPLUGINEXPLORERMAINWINDOW_H
+#define CTKCLIPLUGINEXPLORERMAINWINDOW_H
+
+#include <QMainWindow>
+
+#include <ctkModuleManager.h>
+
+class ctkModuleDescriptionDefaultFactory;
+
+namespace Ui {
+class ctkCLIPluginExplorerMainWindow;
+}
+
+class ctkCLIPluginExplorerMainWindow : public QMainWindow
+{
+  Q_OBJECT
+  
+public:
+  explicit ctkCLIPluginExplorerMainWindow(QWidget *parent = 0);
+  ~ctkCLIPluginExplorerMainWindow();
+
+  void addModule(const QString& location);
+  void testModuleXML(const QByteArray& xml);
+
+protected Q_SLOTS:
+
+  void on_actionRun_triggered();
+
+  void futureFinished();
+
+protected:
+
+  void addModuleTab(const ctkModuleReference& moduleRef);
+
+  ctkModuleReference moduleReference(int tabIndex);
+  
+private:
+  Ui::ctkCLIPluginExplorerMainWindow *ui;
+
+  ctkModuleDescriptionDefaultFactory* factory;
+  ctkModuleManager moduleManager;
+
+  QHash<int, ctkModuleReference> mapTabToModuleRef;
+
+  ctkModuleProcessFutureWatcher futureWatcher;
+};
+
+#endif // CTKCLIPLUGINEXPLORERMAINWINDOW_H

+ 98 - 0
Applications/ctkCLIPluginExplorer/ctkCLIPluginExplorerMainWindow.ui

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkCLIPluginExplorerMainWindow</class>
+ <widget class="QMainWindow" name="ctkCLIPluginExplorerMainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>CTK CLI Plug-in Explorer</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <property name="margin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QTabWidget" name="mainTabWidget">
+      <property name="currentIndex">
+       <number>-1</number>
+      </property>
+      <property name="elideMode">
+       <enum>Qt::ElideMiddle</enum>
+      </property>
+      <property name="tabsClosable">
+       <bool>true</bool>
+      </property>
+      <property name="movable">
+       <bool>true</bool>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>25</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <widget class="QToolBar" name="toolBar">
+   <property name="windowTitle">
+    <string>toolBar</string>
+   </property>
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+   <addaction name="actionRun"/>
+   <addaction name="actionStop"/>
+  </widget>
+  <action name="actionRun">
+   <property name="icon">
+    <iconset resource="resources/ctkCLIPluginExplorer.qrc">
+     <normaloff>:/icons/run.png</normaloff>:/icons/run.png</iconset>
+   </property>
+   <property name="text">
+    <string>Run</string>
+   </property>
+   <property name="toolTip">
+    <string>Run CLI Plug-in</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+R</string>
+   </property>
+  </action>
+  <action name="actionStop">
+   <property name="icon">
+    <iconset resource="resources/ctkCLIPluginExplorer.qrc">
+     <normaloff>:/icons/stop.png</normaloff>:/icons/stop.png</iconset>
+   </property>
+   <property name="text">
+    <string>Stop</string>
+   </property>
+   <property name="toolTip">
+    <string>Stop CLI Plug-in</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+C</string>
+   </property>
+  </action>
+ </widget>
+ <resources>
+  <include location="resources/ctkCLIPluginExplorer.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 6 - 0
Applications/ctkCLIPluginExplorer/resources/ctkCLIPluginExplorer.qrc

@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/icons">
+        <file>run.png</file>
+        <file>stop.png</file>
+    </qresource>
+</RCC>

二进制
Applications/ctkCLIPluginExplorer/resources/run.png


二进制
Applications/ctkCLIPluginExplorer/resources/stop.png


+ 12 - 4
Libs/ModuleDescription/CMakeLists.txt

@@ -28,14 +28,22 @@ set(KIT_SRCS
   ctkModuleParameterGroup.h
   ctkModuleParameterGroup.h
   ctkModuleParameterGroup.cpp
   ctkModuleParameterGroup.cpp
   ctkModuleParameterParsers_p.h
   ctkModuleParameterParsers_p.h
+  ctkModuleProcessException.h
+  ctkModuleProcessException.cpp
+  ctkModuleProcessFuture.h
+  ctkModuleProcessFuture.cpp
+  ctkModuleProcessRunner_p.h
+  ctkModuleProcessRunner.cpp
+  ctkModuleReference.h
+  ctkModuleReference.cpp
+  ctkModuleReferencePrivate.h
+  ctkModuleReferencePrivate.cpp
  )
  )
 
 
 # Headers that should run through moc
 # Headers that should run through moc
 set(KIT_MOC_SRCS
 set(KIT_MOC_SRCS
-  #ctkModuleDescriptionConverterInterface.h
-  #ctkModuleDescriptionConverter.h
-  #ctkModuleDescriptionExecutionInterface.h
-  #ctkModuleDescriptionExecution.h
+  ctkModuleProcessRunner_p.h
+  ctkModuleReferencePrivate.h
 )
 )
 
 
 # UI files
 # UI files

+ 1 - 1
Libs/ModuleDescription/ctkModuleDescriptionParser.cpp

@@ -151,7 +151,7 @@ void ctkModuleDescriptionParser::validate()
 
 
   QXmlSchema schema;
   QXmlSchema schema;
   schema.setMessageHandler(&errorHandler);
   schema.setMessageHandler(&errorHandler);
-  schema.load(QUrl::fromLocalFile(":ctkModuleDescription.xsd"));
+  schema.load(QUrl::fromLocalFile(":/ctkModuleDescription.xsd"));
 
 
   bool res = schema.isValid();
   bool res = schema.isValid();
   if (!res)
   if (!res)

+ 3 - 3
Libs/ModuleDescription/ctkModuleDescriptionValidator.cpp

@@ -115,10 +115,10 @@ void ctkModuleDescriptionValidator::setXSLTransformation(QIODevice *transformati
 
 
 bool ctkModuleDescriptionValidator::validate()
 bool ctkModuleDescriptionValidator::validate()
 {
 {
-  return validateInput() && validateOutput();
+  return validateXMLInput() && validateXSLTOutput();
 }
 }
 
 
-bool ctkModuleDescriptionValidator::validateInput()
+bool ctkModuleDescriptionValidator::validateXMLInput()
 {
 {
   _errorStr.clear();
   _errorStr.clear();
 
 
@@ -161,7 +161,7 @@ bool ctkModuleDescriptionValidator::validateInput()
   return true;
   return true;
 }
 }
 
 
-bool ctkModuleDescriptionValidator::validateOutput()
+bool ctkModuleDescriptionValidator::validateXSLTOutput()
 {
 {
   _errorStr.clear();
   _errorStr.clear();
   _output.clear();
   _output.clear();

+ 2 - 2
Libs/ModuleDescription/ctkModuleDescriptionValidator.h

@@ -44,8 +44,8 @@ public:
   void setXSLTransformation(QIODevice* transformation);
   void setXSLTransformation(QIODevice* transformation);
 
 
   bool validate();
   bool validate();
-  bool validateInput();
-  bool validateOutput();
+  bool validateXMLInput();
+  bool validateXSLTOutput();
 
 
   bool error() const;
   bool error() const;
   QString errorString() const;
   QString errorString() const;

+ 104 - 6
Libs/ModuleDescription/ctkModuleManager.cpp

@@ -21,23 +21,121 @@
 
 
 #include "ctkModuleManager.h"
 #include "ctkModuleManager.h"
 
 
+#include "ctkModuleDescriptionValidator.h"
 #include "ctkModuleObjectHierarchyReader.h"
 #include "ctkModuleObjectHierarchyReader.h"
+#include "ctkModuleProcessRunner_p.h"
+#include "ctkModuleReferencePrivate.h"
 
 
-#include <QDebug>
+#include <QStringList>
+#include <QBuffer>
 
 
-ctkModuleManager::ctkModuleManager()
+#include <QProcess>
+#include <QFuture>
+
+QString normalizeFlag(const QString& flag)
+{
+  return flag.trimmed().remove(QRegExp("^-*"));
+}
+
+ctkModuleManager::ctkModuleManager(ctkModuleDescriptionFactory *descriptionFactory)
+  : descriptionFactory(descriptionFactory)
 {
 {
 }
 }
 
 
-QString ctkModuleManager::createCommandLine(QObject *hierarchy)
+QStringList ctkModuleManager::createCommandLineArgs(QObject *hierarchy)
 {
 {
   ctkModuleObjectHierarchyReader reader(hierarchy);
   ctkModuleObjectHierarchyReader reader(hierarchy);
 
 
-  QString cmdLine;
+  QStringList cmdLineArgs;
+  QHash<int, QString> indexedArgs;
   while(reader.readNextParameter())
   while(reader.readNextParameter())
   {
   {
-    qDebug() << "Found parameter:" << reader.name() << "," << reader.value();
+    if (reader.index() > -1)
+    {
+      indexedArgs.insert(reader.index(), reader.value());
+    }
+    else
+    {
+      QString argFlag;
+      if (reader.longFlag().isEmpty())
+      {
+        argFlag = QString("-") + normalizeFlag(reader.flag());
+      }
+      else
+      {
+        argFlag = QString("--") + normalizeFlag(reader.longFlag());
+      }
+
+      QStringList args;
+      if (reader.isMultiple())
+      {
+        args = reader.value().split(',', QString::SkipEmptyParts);
+      }
+      else
+      {
+        args.push_back(reader.value());
+      }
+
+      foreach(QString arg, args)
+      {
+        cmdLineArgs << argFlag << arg;
+      }
+    }
+  }
+
+  QList<int> indexes = indexedArgs.keys();
+  qSort(indexes.begin(), indexes.end());
+  foreach(int index, indexes)
+  {
+    cmdLineArgs << indexedArgs[index];
+  }
+
+  return cmdLineArgs;
+}
+
+ctkModuleReference ctkModuleManager::addModule(const QString& location)
+{
+  QProcess process;
+  process.setReadChannel(QProcess::StandardOutput);
+  process.start(location, QStringList("--xml"));
+
+  ctkModuleReference ref;
+  ref.d->loc = location;
+  if (!process.waitForFinished() || process.exitStatus() == QProcess::CrashExit ||
+      process.error() != QProcess::UnknownError)
+  {
+    qWarning() << "The executable at" << location << "could not be started:" << process.errorString();
+    return ref;
   }
   }
 
 
-  return cmdLine;
+  process.waitForReadyRead();
+  QByteArray xml = process.readAllStandardOutput();
+
+  qDebug() << xml;
+
+  // validate the outputted xml description
+  QBuffer input(&xml);
+  input.open(QIODevice::ReadOnly);
+
+  ctkModuleDescriptionValidator validator(&input);
+  if (!validator.validateXMLInput())
+  {
+    qCritical() << validator.errorString();
+    return ref;
+  }
+
+  ref.d->xml = xml;
+  ref.d->objectRepresentation = descriptionFactory->createObjectRepresentationFromXML(ref.d->xml);
+  ref.d->setGUI(descriptionFactory->createGUIFromXML(ref.d->xml));
+
+  cache[location] = ref;
+  return ref;
+}
+
+ctkModuleProcessFuture ctkModuleManager::run(const ctkModuleReference& moduleRef)
+{
+  // TODO: manage memory
+  QStringList args = createCommandLineArgs(moduleRef.d->objectRepresentation);
+  ctkModuleProcessRunner* moduleProcess = new ctkModuleProcessRunner(moduleRef.location(), args);
+  return moduleProcess->start();
 }
 }

+ 30 - 2
Libs/ModuleDescription/ctkModuleManager.h

@@ -22,18 +22,46 @@
 #ifndef CTKMODULEMANAGER_H
 #ifndef CTKMODULEMANAGER_H
 #define CTKMODULEMANAGER_H
 #define CTKMODULEMANAGER_H
 
 
+#include <QStringList>
 #include <QString>
 #include <QString>
 
 
 #include <ctkModuleDescriptionExport.h>
 #include <ctkModuleDescriptionExport.h>
 
 
+#include "ctkModuleReference.h"
+#include "ctkModuleProcessFuture.h"
+
 class QObject;
 class QObject;
 
 
+struct ctkModuleDescriptionFactory
+{
+  virtual ~ctkModuleDescriptionFactory() {}
+
+  virtual QObject* createGUIFromXML(const QByteArray& xmlDescription)
+  {
+    Q_UNUSED(xmlDescription)
+    return 0;
+  }
+
+  virtual QObject* createObjectRepresentationFromXML(const QByteArray& xmlDescription) = 0;
+};
+
+/// The methods in this class are for playing around... no API design yet
 class CTK_MODULDESC_EXPORT ctkModuleManager
 class CTK_MODULDESC_EXPORT ctkModuleManager
 {
 {
 public:
 public:
-  ctkModuleManager();
+  ctkModuleManager(ctkModuleDescriptionFactory* descriptionFactory);
+
+  ctkModuleReference addModule(const QString& location);
+
+  static QStringList createCommandLineArgs(QObject* hierarchy);
+
+  static ctkModuleProcessFuture run(const ctkModuleReference& moduleRef);
+
+private:
+
+  ctkModuleDescriptionFactory* descriptionFactory;
 
 
-  static QString createCommandLine(QObject* hierarchy);
+  QHash<QString, ctkModuleReference> cache;
 };
 };
 
 
 #endif // CTKMODULEMANAGER_H
 #endif // CTKMODULEMANAGER_H

+ 25 - 2
Libs/ModuleDescription/ctkModuleObjectHierarchyReader.cpp

@@ -150,14 +150,37 @@ QString ctkModuleObjectHierarchyReader::label() const
   default: return QString();
   default: return QString();
   }
   }
 }
 }
-#include <QDebug>
+
 QString ctkModuleObjectHierarchyReader::value() const
 QString ctkModuleObjectHierarchyReader::value() const
 {
 {
   QString valProp = property("valueProperty").toString();
   QString valProp = property("valueProperty").toString();
-  qDebug() << "valProp:" << valProp;
   return property(valProp).toString();
   return property(valProp).toString();
 }
 }
 
 
+QString ctkModuleObjectHierarchyReader::flag() const
+{
+  QVariant v = property("flag");
+  return v.isValid() ? v.toString() : QString();
+}
+
+QString ctkModuleObjectHierarchyReader::longFlag() const
+{
+  QVariant v = property("longflag");
+  return v.isValid() ? v.toString() : QString();
+}
+
+int ctkModuleObjectHierarchyReader::index() const
+{
+  QVariant v = property("index");
+  return v.isValid() ? v.toInt() : -1;
+}
+
+bool ctkModuleObjectHierarchyReader::isMultiple() const
+{
+  QVariant v = property("multiple");
+  return v.isValid() ? v.toBool() : false;
+}
+
 QVariant ctkModuleObjectHierarchyReader::property(const QString &propName) const
 QVariant ctkModuleObjectHierarchyReader::property(const QString &propName) const
 {
 {
   if (d->currentObject == 0) return QVariant();
   if (d->currentObject == 0) return QVariant();

+ 7 - 0
Libs/ModuleDescription/ctkModuleObjectHierarchyReader.h

@@ -59,6 +59,13 @@ public:
   QString label() const;
   QString label() const;
   QString value() const;
   QString value() const;
 
 
+  QString flag() const;
+  QString longFlag() const;
+  int index() const;
+
+  bool isMultiple() const;
+
+
   QVariant property(const QString& propName) const;
   QVariant property(const QString& propName) const;
 
 
   TokenType readNext() const;
   TokenType readNext() const;

+ 59 - 0
Libs/ModuleDescription/ctkModuleProcessException.cpp

@@ -0,0 +1,59 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkModuleProcessException.h"
+
+ctkModuleProcessException::ctkModuleProcessException(const QString& msg, int code,
+                                                     QProcess::ExitStatus status)
+  : msg(msg), code(code), status(status)
+{}
+
+int ctkModuleProcessException::exitCode() const
+{
+  return code;
+}
+
+QProcess::ExitStatus ctkModuleProcessException::exitStatus() const
+{
+  return status;
+}
+
+QString ctkModuleProcessException::message() const
+{
+  return msg;
+}
+
+const char* ctkModuleProcessException::what() const throw()
+{
+  static std::string strMsg;
+  strMsg = msg.toStdString();
+  return strMsg.c_str();
+}
+
+void ctkModuleProcessException::raise() const
+{
+  throw *this;
+}
+
+ctkModuleProcessException* ctkModuleProcessException::clone() const
+{
+  return new ctkModuleProcessException(*this);
+}

+ 56 - 0
Libs/ModuleDescription/ctkModuleProcessException.h

@@ -0,0 +1,56 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKMODULEPROCESSEXCEPTION_H
+#define CTKMODULEPROCESSEXCEPTION_H
+
+#include <qtconcurrentexception.h>
+
+#include <QProcess>
+
+class ctkModuleProcessException : public QtConcurrent::Exception
+{
+public:
+
+  ctkModuleProcessException(const QString& msg, int code = 0,
+                            QProcess::ExitStatus status = QProcess::NormalExit);
+
+  ~ctkModuleProcessException() throw() {}
+
+  int exitCode() const;
+
+  QProcess::ExitStatus exitStatus() const;
+
+  QString message() const;
+
+  const char* what() const throw();
+
+  void raise() const;
+  ctkModuleProcessException* clone() const;
+
+private:
+
+  QString msg;
+  int code;
+  QProcess::ExitStatus status;
+};
+
+#endif // CTKMODULEPROCESSEXCEPTION_H

+ 97 - 0
Libs/ModuleDescription/ctkModuleProcessFuture.cpp

@@ -0,0 +1,97 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkModuleProcessFuture.h"
+
+struct ctkModuleProcessFutureInterfacePrivate
+{
+  ctkModuleProcessFutureInterfacePrivate()
+    : refCount(1), _exitCode(0), _exitStatus(QProcess::NormalExit),
+      _processError(QProcess::UnknownError)
+  {}
+
+  QAtomicInt refCount;
+
+  int _exitCode;
+  QProcess::ExitStatus _exitStatus;
+  QProcess::ProcessError _processError;
+  QString _errorString;
+  QString _stdOut;
+  QString _stdErr;
+};
+
+ctkModuleProcessFutureInterface::QFutureInterface(State initialState)
+  : QFutureInterfaceBase(initialState), d(new ctkModuleProcessFutureInterfacePrivate)
+{ }
+
+ctkModuleProcessFutureInterface::QFutureInterface(const ctkModuleProcessFutureInterface& other)
+  : QFutureInterfaceBase(other), d(other.d)
+{
+  d->refCount.ref();
+}
+
+ctkModuleProcessFutureInterface ctkModuleProcessFutureInterface::canceledResult()
+{ return ctkModuleProcessFutureInterface(State(Started | Finished | Canceled)); }
+
+ctkModuleProcessFutureInterface& ctkModuleProcessFutureInterface::operator=(const ctkModuleProcessFutureInterface& other)
+{
+  QFutureInterfaceBase::operator=(other);
+  other.d->refCount.ref();
+  if(!d->refCount.deref()) delete d;
+  d = other.d;
+  return *this;
+}
+
+int ctkModuleProcessFutureInterface::exitCode() const
+{ QMutexLocker lock(this->mutex()); return d->_exitCode; }
+
+void ctkModuleProcessFutureInterface::reportExitCode(int code)
+{ QMutexLocker lock(this->mutex()); d->_exitCode = code; }
+
+QProcess::ExitStatus ctkModuleProcessFutureInterface::exitStatus() const
+{ QMutexLocker lock(this->mutex()); return d->_exitStatus; }
+
+void ctkModuleProcessFutureInterface::reportExitStatus(QProcess::ExitStatus status)
+{ QMutexLocker lock(this->mutex()); d->_exitStatus = status; }
+
+QProcess::ProcessError ctkModuleProcessFutureInterface::error() const
+{ QMutexLocker lock(this->mutex()); return d->_processError; }
+
+void ctkModuleProcessFutureInterface::reportProcessError(QProcess::ProcessError procErr)
+{ QMutexLocker lock(this->mutex()); d->_processError = procErr; }
+
+QString ctkModuleProcessFutureInterface::errorString() const
+{ QMutexLocker lock(this->mutex()); return d->_errorString; }
+
+void ctkModuleProcessFutureInterface::reportErrorString(const QString& errorStr)
+{ QMutexLocker lock(this->mutex()); d->_errorString = errorStr; }
+
+QString ctkModuleProcessFutureInterface::standardOutput() const
+{ QMutexLocker lock(this->mutex()); return d->_stdOut; }
+
+void ctkModuleProcessFutureInterface::reportStandardOutput(const QString& stdOut)
+{ QMutexLocker lock(this->mutex()); d->_stdOut = stdOut; }
+
+QString ctkModuleProcessFutureInterface::standardError() const
+{ QMutexLocker lock(this->mutex()); return d->_stdErr; }
+
+void ctkModuleProcessFutureInterface::reportStandardError(const QString& stdErr)
+{ QMutexLocker lock(this->mutex()); d->_stdErr = stdErr; }

+ 138 - 0
Libs/ModuleDescription/ctkModuleProcessFuture.h

@@ -0,0 +1,138 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKMODULEPROCESSFUTURE_H
+#define CTKMODULEPROCESSFUTURE_H
+
+#include <ctkModuleDescriptionExport.h>
+
+#include <QFutureInterface>
+#include <QFutureWatcher>
+#include <QProcess>
+
+class ctkModuleProcessFutureInterfacePrivate;
+
+struct ctkModuleProcessDummy {};
+typedef QFutureWatcher<ctkModuleProcessDummy> ctkModuleProcessFutureWatcher;
+
+
+template<>
+class CTK_MODULDESC_EXPORT QFutureInterface<ctkModuleProcessDummy> : public QFutureInterfaceBase
+{
+
+public:
+
+  QFutureInterface(State initialState = NoState);
+  QFutureInterface(const QFutureInterface& other);
+
+  static QFutureInterface canceledResult();
+
+  QFutureInterface& operator=(const QFutureInterface& other);
+
+  int exitCode() const;
+  void reportExitCode(int code);
+
+  QProcess::ExitStatus exitStatus() const;
+  void reportExitStatus(QProcess::ExitStatus status);
+
+  QProcess::ProcessError error() const;
+  void reportProcessError(QProcess::ProcessError procErr);
+
+  QString errorString() const;
+  void reportErrorString(const QString& errorStr);
+
+  QString standardOutput() const;
+  void reportStandardOutput(const QString& stdOut);
+
+  QString standardError() const;
+  void reportStandardError(const QString& stdErr);
+
+private:
+
+  ctkModuleProcessFutureInterfacePrivate* d;
+};
+
+typedef QFutureInterface<ctkModuleProcessDummy> ctkModuleProcessFutureInterface;
+
+
+template<>
+class QFuture<ctkModuleProcessDummy>
+{
+
+public:
+
+  QFuture()
+    : d(ctkModuleProcessFutureInterface::canceledResult())
+  { }
+
+  explicit QFuture(ctkModuleProcessFutureInterface *p) // internal
+    : d(*p)
+  { }
+
+  QFuture(const QFuture &other)
+    : d(other.d)
+  { }
+
+  ~QFuture()
+  { }
+
+  QFuture& operator=(const QFuture& other);
+  bool operator==(const QFuture& other) const { return (d == other.d); }
+  bool operator!=(const QFuture& other) const { return (d != other.d); }
+
+  void cancel() { d.cancel(); }
+  bool isCanceled() const { return d.isCanceled(); }
+
+  bool isStarted() const { return d.isStarted(); }
+  bool isFinished() const { return d.isFinished(); }
+  bool isRunning() const { return d.isRunning(); }
+
+  int exitCode() const { return d.exitCode(); }
+  int exitStatus() const { return d.exitStatus(); }
+  QProcess::ProcessError error() const { return d.error(); }
+  QString errorString() const { return d.errorString(); }
+
+  QString standardOutput() const { return d.standardOutput(); }
+  QString standardError() const { return d.standardError(); }
+
+  int progressValue() const { return d.progressValue(); }
+  int progressMinimum() const { return d.progressMinimum(); }
+  int progressMaximum() const { return d.progressMaximum(); }
+  QString progressText() const { return d.progressText(); }
+  void waitForFinished() { d.waitForFinished(); }
+
+private:
+
+  friend class QFutureWatcher<ctkModuleProcessDummy>;
+
+  mutable ctkModuleProcessFutureInterface d;
+};
+
+typedef QFuture<ctkModuleProcessDummy> ctkModuleProcessFuture;
+
+
+inline ctkModuleProcessFuture& ctkModuleProcessFuture::operator=(const ctkModuleProcessFuture& other)
+{
+  d = other.d;
+  return *this;
+}
+
+#endif // CTKMODULEPROCESSFUTURE_H

+ 74 - 0
Libs/ModuleDescription/ctkModuleProcessRunner.cpp

@@ -0,0 +1,74 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkModuleProcessRunner_p.h"
+
+#include "ctkModuleProcessException.h"
+
+
+ctkModuleProcessRunner::ctkModuleProcessRunner(const QString& location, const QStringList& args)
+  : process(), location(location), args(args)
+{
+}
+
+ctkModuleProcessFuture ctkModuleProcessRunner::start()
+{
+  this->reportStarted();
+  ctkModuleProcessFuture future(this);
+  run();
+  return future;
+}
+
+void ctkModuleProcessRunner::run()
+{
+  connect(&process, SIGNAL(started()), this, SLOT(processStarted()));
+  connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+  connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
+
+  process.start(location, args);
+}
+
+void ctkModuleProcessRunner::processStarted()
+{
+  qDebug() << "Reporting process started";
+  this->reportStarted();
+}
+
+void ctkModuleProcessRunner::processFinished(int exitCode, QProcess::ExitStatus status)
+{
+  Q_UNUSED(exitCode)
+  Q_UNUSED(status)
+  qDebug() << "Reporting process finished";
+  this->reportExitCode(exitCode);
+  this->reportExitStatus(status);
+  this->reportProcessError(process.error());
+  this->reportErrorString(process.errorString());
+  this->reportStandardOutput(process.readAllStandardOutput());
+  this->reportStandardError(process.readAllStandardError());
+  this->reportFinished();
+}
+
+void ctkModuleProcessRunner::processError(QProcess::ProcessError)
+{
+  qDebug() << "Reporting process error";
+  this->reportException(ctkModuleProcessException(process.errorString(), process.exitCode(),
+                                                  process.exitStatus()));
+}

+ 57 - 0
Libs/ModuleDescription/ctkModuleProcessRunner_p.h

@@ -0,0 +1,57 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKMODULEPROCESSRUNNER_P_H
+#define CTKMODULEPROCESSRUNNER_P_H
+
+#include <QObject>
+#include <QProcess>
+
+#include "ctkModuleProcessFuture.h"
+
+class ctkModuleProcessRunner : public QObject, public QRunnable, public ctkModuleProcessFutureInterface
+{
+  Q_OBJECT
+
+public:
+
+  ctkModuleProcessRunner(const QString& location, const QStringList& args);
+
+  ctkModuleProcessFuture start();
+
+  void run();
+
+protected Q_SLOTS:
+
+  void processStarted();
+
+  void processFinished(int exitCode, QProcess::ExitStatus status);
+
+  void processError(QProcess::ProcessError);
+
+private:
+
+  QProcess process;
+  const QString location;
+  const QStringList args;
+};
+
+#endif // CTKMODULEPROCESSRUNNER_P_H

+ 74 - 0
Libs/ModuleDescription/ctkModuleReference.cpp

@@ -0,0 +1,74 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkModuleReference.h"
+#include "ctkModuleReferencePrivate.h"
+
+ctkModuleReference::ctkModuleReference()
+  : d(new ctkModuleReferencePrivate)
+{}
+
+ctkModuleReference::~ctkModuleReference()
+{
+  if (!d->ref.deref()) delete d;
+}
+
+ctkModuleReference::ctkModuleReference(const ctkModuleReference &ref)
+  : d(ref.d)
+{
+  d->ref.ref();
+}
+
+ctkModuleReference &ctkModuleReference::operator =(const ctkModuleReference &ref)
+{
+  ctkModuleReferencePrivate* curr = d;
+  d = ref.d;
+  d->ref.ref();
+
+  if (!curr->ref.deref()) delete curr;
+
+  return *this;
+}
+
+ctkModuleReference::operator bool()
+{
+  return isValid();
+}
+
+bool ctkModuleReference::isValid()
+{
+  return !(d->xml.isEmpty() || d->objectRepresentation == 0);
+}
+
+QByteArray ctkModuleReference::xmlDescription() const
+{
+  return d->xml;
+}
+
+QString ctkModuleReference::location() const
+{
+  return d->loc;
+}
+
+QObject* ctkModuleReference::widgetTree() const
+{
+  return d->gui;
+}

+ 62 - 0
Libs/ModuleDescription/ctkModuleReference.h

@@ -0,0 +1,62 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKMODULEREFERENCE_H
+#define CTKMODULEREFERENCE_H
+
+#include <ctkModuleDescriptionExport.h>
+
+#include <QByteArray>
+#include <QString>
+
+class QObject;
+
+class ctkModuleReferencePrivate;
+
+class CTK_MODULDESC_EXPORT ctkModuleReference
+{
+public:
+
+  ctkModuleReference();
+  ~ctkModuleReference();
+
+  ctkModuleReference(const ctkModuleReference& ref);
+  ctkModuleReference& operator=(const ctkModuleReference& ref);
+
+  operator bool();
+
+  bool isValid();
+
+  QByteArray xmlDescription() const;
+
+  QString location() const;
+
+  QObject* widgetTree() const;
+
+private:
+
+  friend class ctkModuleManager;
+
+  ctkModuleReferencePrivate* d;
+
+};
+
+#endif // CTKMODULEREFERENCE_H

+ 44 - 0
Libs/ModuleDescription/ctkModuleReferencePrivate.cpp

@@ -0,0 +1,44 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 "ctkModuleReferencePrivate.h"
+
+ctkModuleReferencePrivate::ctkModuleReferencePrivate()
+  : objectRepresentation(0), ref(1), gui(0)
+{}
+
+ctkModuleReferencePrivate::~ctkModuleReferencePrivate()
+{
+  objectRepresentation->deleteLater();
+  if (gui) gui->deleteLater();
+}
+
+void ctkModuleReferencePrivate::setGUI(QObject* gui)
+{
+  if (this->gui) disconnect(gui);
+  this->gui = gui;
+  connect(this->gui, SIGNAL(destroyed()), this, SLOT(guiDestroyed()));
+}
+
+void ctkModuleReferencePrivate::guiDestroyed()
+{
+  gui = 0;
+}

+ 57 - 0
Libs/ModuleDescription/ctkModuleReferencePrivate.h

@@ -0,0 +1,57 @@
+/*=============================================================================
+  
+  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.apache.org/licenses/LICENSE-2.0
+    
+  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 CTKMODULEREFERENCEPRIVATE_H
+#define CTKMODULEREFERENCEPRIVATE_H
+
+#include <QByteArray>
+#include <QObject>
+
+class ctkModuleReferencePrivate : public QObject
+{
+  Q_OBJECT
+
+public:
+
+  ctkModuleReferencePrivate();
+
+  ~ctkModuleReferencePrivate();
+
+  void setGUI(QObject* gui);
+
+  QByteArray xml;
+  QString loc;
+  QObject* objectRepresentation;
+
+  QAtomicInt ref;
+
+private:
+
+  friend class ctkModuleReference;
+
+  QObject* gui;
+
+private Q_SLOTS:
+
+  void guiDestroyed();
+};
+
+#endif // CTKMODULEREFERENCEPRIVATE_H