浏览代码

ENH: Added class ctkCommandLineParser and correponding test to CTKCore

Usage:

QApplication app(argc, argv);
QString filename;
ctkCommandLineParser parser;
parser.addStringArgument("--filename", 0, &filename);
if (!parser.parseArguments(app.arguments()))
  {
  std::cerr << qPrintable(parser.errorString()) << std::endl;
  return EXIT_FAILURE;
  }
Jean-Christophe Fillion-Robin 15 年之前
父节点
当前提交
bff0c8236d

+ 2 - 0
Libs/Core/CMakeLists.txt

@@ -33,6 +33,8 @@ SET(KIT_SRCS
   ctkAbstractQObjectFactory.tpp
   ctkAbstractLibraryFactory.h
   ctkAbstractLibraryFactory.tpp
+  ctkCommandLineParser.cpp
+  ctkCommandLineParser.h
   ctkDependencyGraph.cpp
   ctkDependencyGraph.h
   ctkLogger.cpp

+ 4 - 2
Libs/Core/Testing/Cpp/CMakeLists.txt

@@ -6,6 +6,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cpp
   ctkAbstractObjectFactoryTest1.cpp
   ctkAbstractPluginFactoryTest1.cpp
   ctkAbstractQObjectFactoryTest1.cpp
+  ctkCommandLineParserTest1.cpp
   ctkModelTesterTest1.cpp
   ctkUtilsTest1.cpp
   ctkDependencyGraphTest1.cpp
@@ -36,8 +37,9 @@ ENDMACRO( SIMPLE_TEST  )
 # Add Tests
 #
 
-SIMPLE_TEST( ctkModelTesterTest1 )
-SIMPLE_TEST( ctkUtilsTest1 )
+SIMPLE_TEST( ctkCommandLineParserTest1 )
 SIMPLE_TEST( ctkDependencyGraphTest1 )
+SIMPLE_TEST( ctkModelTesterTest1 )
 SIMPLE_TEST( ctkPimplTest1 )
 SIMPLE_TEST( ctkSingletonTest1 )
+SIMPLE_TEST( ctkUtilsTest1 )

+ 440 - 0
Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp

@@ -0,0 +1,440 @@
+
+// Qt includes
+#include <QDebug>
+
+// CTK includes
+#include "ctkCommandLineParser.h"
+
+// STD includes
+#include <cstdlib>
+
+int ctkCommandLineParserTest1(int, char*[])
+{
+  // Test1 - Check if unparsedArguments() worked as expected
+  QStringList arguments1;
+  arguments1 << "ctkCommandLineParserTest1";
+  arguments1 << "--test-bool";
+  arguments1 << "--test-string";
+  arguments1 << "ctkrocks";
+  ctkCommandLineParser parser1;
+  if (!parser1.parseArguments(arguments1))
+    {
+    qCritical() << "Test1 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  QStringList expectedUnparsedArguments1;
+  expectedUnparsedArguments1 << "--test-bool" << "--test-string" << "ctkrocks";
+
+  if (parser1.unparsedArguments() != expectedUnparsedArguments1)
+    {
+    qCritical() << "unparsedArguments:" << parser1.unparsedArguments();
+    qCritical() << "expectedUnparsedArguments1:" << expectedUnparsedArguments1;
+    return EXIT_FAILURE;
+    }
+
+  // Test2 - Check if addBooleanArgument() worked as expected
+  QStringList arguments2;
+  arguments2 << "ctkCommandLineParserTest1";
+  arguments2 << "--test-bool";
+  arguments2 << "--test-string";
+  arguments2 << "ctkrocks";
+  bool testBool = false;
+  ctkCommandLineParser parser2;
+  parser2.addBooleanArgument("--test-bool", 0, &testBool, "This is a test bool", false);
+  if (!parser2.parseArguments(arguments2))
+    {
+    qCritical() << "Test2 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  QStringList expectedUnparsedArguments2;
+  expectedUnparsedArguments2 << "--test-string" << "ctkrocks";
+
+  if (parser2.unparsedArguments() != expectedUnparsedArguments2)
+    {
+    qCritical() << "Test2 - Failed - unparsedArguments:" << parser2.unparsedArguments()
+        << ", expectedUnparsedArguments2:" << expectedUnparsedArguments2;
+    return EXIT_FAILURE;
+    }
+
+  if (!testBool)
+    {
+    qCritical() << "Test2 - Failed to parse --test-bool";
+    return EXIT_FAILURE;
+    }
+
+  // Test3 - check if addStringArgument(), addIntegerArgument() and addStringListArgument() works
+  QStringList arguments3;
+  arguments3 << "ctkCommandLineParserTest1";
+  arguments3 << "--test-string" << "TestingIsGood";
+  arguments3 << "--test-string2"<< "CTKSuperRocks";
+  arguments3 << "--test-integer"<< "-3";
+  arguments3 << "--test-stringlist"<< "item1" << "item2" << "item3";
+  QString testString;
+  QString testString2;
+  int testInteger = 1;
+  QStringList testStringlist;
+  ctkCommandLineParser parser3;
+  parser3.addStringArgument("--test-string", 0, &testString, "This is a test string");
+  parser3.addStringArgument("--test-string2", 0, &testString2, "This is a test string2", "CTKGood");
+  parser3.addIntegerArgument("--test-integer", 0, &testInteger, "This is a test integer");
+  parser3.addStringListArgument("--test-stringlist", 0, &testStringlist,
+                                "This is a test stringlist");
+  if (!parser3.parseArguments(arguments3))
+    {
+    qCritical() << "Test3 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  QString expectedTestString = "TestingIsGood";
+  if (testString != expectedTestString)
+    {
+    qCritical() << "Test3 - Failed - testString" << testString
+        << ", expectedTestString" << expectedTestString;
+    return EXIT_FAILURE;
+    }
+
+  QString expectedTestString2 = "CTKSuperRocks";
+  if (testString2 != expectedTestString2)
+    {
+    qCritical() << "Test3 - Failed - testString2" << testString2
+        << ", expectedTestString2" << expectedTestString2;
+    return EXIT_FAILURE;
+    }
+
+  int expectedTestInteger = -3;
+  if (testInteger != expectedTestInteger)
+    {
+    qCritical() << "Test3 - Failed - testInteger" << testInteger
+        << ", expectedTestInteger" << expectedTestInteger;
+    return EXIT_FAILURE;
+    }
+
+  QStringList expectedTestStringlist;
+  expectedTestStringlist << "item1" << "item2" << "item3";
+  if (testStringlist != expectedTestStringlist)
+    {
+    qCritical() << "Test3 - Failed - testStringlist" << testStringlist
+        << ", expectedTestStringlist" << expectedTestStringlist;
+    return EXIT_FAILURE;
+    }
+
+  // Test4 - check if helpText() works as expected
+  ctkCommandLineParser parser4;
+  QString helpString;
+  QString helpString1;
+  QString helpString2;
+  QString helpString3;
+  parser4.addStringArgument("--help-string", 0, &helpString, "This is an help string");
+  parser4.addStringArgument("--help-string-med", 0, &helpString1, "");
+  parser4.addStringArgument("--help-string-long", "-hs2", &helpString2, "This is an help string too !");
+  parser4.addStringArgument(0, "-hs3", &helpString3, "This is an help string too for sure !?");
+
+  QString expectedHelpString;
+  QTextStream streamExpectedHelpString(&expectedHelpString);
+  streamExpectedHelpString << "  --help-string..........This is an help string\n"
+                            << "  --help-string-med\n"
+                            << "  -hs2\n"
+                            << "  --help-string-long.....This is an help string too !\n"
+                            << "  -hs3...................This is an help string too for sure !?\n";
+
+  if (expectedHelpString != parser4.helpText('.'))
+    {
+    qCritical() << "Test4 - Problem with helpText('.') - helpText:\n" << parser4.helpText('.')
+        << ", expectedHelpString:\n" << expectedHelpString;
+    return EXIT_FAILURE;
+    }
+
+  QString expectedHelpString2;
+  QTextStream streamExpectedHelpString2(&expectedHelpString2);
+  streamExpectedHelpString2 << "  --help-string          This is an help string\n"
+                           << "  --help-string-med\n"
+                           << "  -hs2\n"
+                           << "  --help-string-long     This is an help string too !\n"
+                           << "  -hs3                   This is an help string too for sure !?\n";
+  if (expectedHelpString2 != parser4.helpText())
+    {
+    qCritical() << "Test4 - Problem with helpText() - helpText:\n" << parser4.helpText()
+        << ", expectedHelpString2:\n" << expectedHelpString2;
+    return EXIT_FAILURE;
+    }
+
+  // Test5 - check if setExactMatchRegularExpression() works as expected
+  ctkCommandLineParser parser5;
+
+  if (parser5.setExactMatchRegularExpression("--unknown",".*", "invalid"))
+    {
+    qCritical() << "Test5 - Problem with setExactMatchRegularExpression(shortOrLongArg) - "
+                   "The function should return false if an invalid argument is passed";
+    return EXIT_FAILURE;
+    }
+
+  bool test5Bool = false;
+  QString test5String;
+  int test5Int = 0;
+  QStringList test5List;
+  parser5.addStringListArgument("--list", 0, &test5List, "Test5 list");
+  if (!parser5.setExactMatchRegularExpression("--list","item[0-9]",
+                                              "Element of the form item[0-9] are expected."))
+    {
+    qCritical() << "Test5 - Problem with setExactMatchRegularExpression(StringListArg)";
+    return EXIT_FAILURE;
+    }
+  parser5.addStringArgument("--string", 0, &test5String, "Test5 string");
+  if (!parser5.setExactMatchRegularExpression("--string","ctkStop|ctkStart",
+                                              "ctkStop or ctkStart is expected."))
+    {
+    qCritical() << "Test5 - Problem with setExactMatchRegularExpression(StringArg)";
+    return EXIT_FAILURE;
+    }
+  parser5.addBooleanArgument("--bool", 0, &test5Bool, "Test5 bool");
+  if (parser5.setExactMatchRegularExpression("--bool",".*", "invalid"))
+    {
+    qCritical() << "Test5 - Problem with setExactMatchRegularExpression(BooleanArg) - "
+                   "The function should return false if a boolean argument is passed";
+    return EXIT_FAILURE;
+    }
+  parser5.addIntegerArgument("--int", 0, &test5Int, "Test5 int");
+  if (!parser5.setExactMatchRegularExpression("--int","[1-3]",
+                                              "Value 1, 2 or 3 is expected."))
+    {
+    qCritical() << "Test5 - Problem with setExactMatchRegularExpression(IntegerArg)";
+    return EXIT_FAILURE;
+    }
+
+  QStringList arguments5;
+  arguments5 << "ctkCommandLineParserTest1";
+  arguments5 << "--string"<< "ctkStop";
+  arguments5 << "--int"<< "2";
+  arguments5 << "--list"<< "item2" << "item3";
+  
+  if (!parser5.parseArguments(arguments5))
+    {
+    qCritical() << "Test5 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  arguments5.clear();
+  arguments5 << "ctkCommandLineParserTest1";
+  arguments5 << "--string"<< "ctkStopp";
+  arguments5 << "--int"<< "2";
+  arguments5 << "--list"<< "item2" << "item3";
+
+  if (parser5.parseArguments(arguments5))
+    {
+    qCritical() << "Test5 - parseArguments() should return False - 'ctkStopp' isn't a valid string";
+    return EXIT_FAILURE;
+    }
+
+  QString expectedErrorString =
+    "Value(s) associated with argument --string are incorrect."
+    " ctkStop or ctkStart is expected.";
+
+  if(expectedErrorString != parser5.errorString())
+    {
+    qCritical() << "Test5 - Failed - expectedErrorString" << expectedErrorString
+            << ", parser5.errorString()" << parser5.errorString();
+    return EXIT_FAILURE;
+    }
+
+  arguments5.clear();
+  arguments5 << "ctkCommandLineParserTest1";
+  arguments5 << "--string"<< "ctkStop";
+  arguments5 << "--int"<< "4";
+  arguments5 << "--list"<< "item2" << "item3";
+
+  if (parser5.parseArguments(arguments5))
+    {
+    qCritical() << "Test5 - parseArguments() should return False - '4' isn't a valid int";
+    return EXIT_FAILURE;
+    }
+
+  QString expectedErrorString2 =
+    "Value(s) associated with argument --int are incorrect."
+    " Value 1, 2 or 3 is expected.";
+
+  if(expectedErrorString2 != parser5.errorString())
+    {
+    qCritical() << "Test5 - Failed - expectedErrorString2" << expectedErrorString2
+            << ", parser5.errorString()" << parser5.errorString();
+    return EXIT_FAILURE;
+    }
+
+  arguments5.clear();
+  arguments5 << "ctkCommandLineParserTest1";
+  arguments5 << "--string"<< "ctkStop";
+  arguments5 << "--int"<< "2";
+  arguments5 << "--list"<< "item2" << "item10";
+
+  if (parser5.parseArguments(arguments5))
+    {
+    qCritical() << "Test5 - parseArguments() should return False "
+                   "- 'item10' isn't a valid list element";
+    return EXIT_FAILURE;
+    }
+
+  QString expectedErrorString3 =
+    "Value(s) associated with argument --list are incorrect."
+    " Element of the form item[0-9] are expected.";
+
+  if(expectedErrorString3 != parser5.errorString())
+    {
+    qCritical() << "Test5 - Failed - expectedErrorString3" << expectedErrorString3
+            << ", parser5.errorString()" << parser5.errorString();
+    return EXIT_FAILURE;
+    }
+    
+  // Test6 - Check if the parser handle the case when value of parameter is omitted
+  ctkCommandLineParser parser6;
+  QString test6String;
+  bool test6Bool = false;
+  parser6.addStringArgument("--string", 0, &test6String, "This is a string");
+  parser6.addBooleanArgument("--bool", 0, &test6Bool, "This is a bool");
+  
+  QStringList arguments6; 
+  arguments6 << "ctkCommandLineParserTest1"
+             << "--string";
+  
+  // since --string is missing a parameter, parseArgument is expected to fail
+  if (parser6.parseArguments(arguments6))
+    {
+    qCritical() << "Test6 - Problem with parseArguments()";
+    return EXIT_FAILURE;
+    }
+
+  // Expected ignore argument for Test7, 8. 9 and 10
+  QStringList expectedUnparsedArguments;
+  expectedUnparsedArguments << "--ignoreint"<< "2" << "--ignorelist"<< "item1" << "item2";
+
+  // Test7 - Check if addBooleanArgument and ignore_rest=true works as expected
+  ctkCommandLineParser parser7;
+  bool test7Bool = false;
+  parser7.addBooleanArgument("--bool", 0, &test7Bool, "This is a boolean",
+                             false /*defaultValue*/, true /* ignoreRest*/);
+  QStringList arguments7;
+  arguments7 << "ctkCommandLineParserTest1";
+  arguments7 << "--bool";
+  arguments7 << expectedUnparsedArguments;
+
+  if (!parser7.parseArguments(arguments7))
+    {
+    qCritical() << "Test7 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+  bool expectedTest7Bool = true;
+  if (test7Bool != expectedTest7Bool)
+    {
+    qCritical() << "Test7 - Failed - test7Bool" << test7Bool
+        << ", expectedTest7Bool" << expectedTest7Bool;
+    return EXIT_FAILURE;
+    }
+
+  if (parser7.unparsedArguments() != expectedUnparsedArguments)
+    {
+    qCritical() << "Test7 - Failed - expectedUnparsedArguments " << expectedUnparsedArguments
+                << ", parser7.unparsedArguments" << parser7.unparsedArguments();
+    return EXIT_FAILURE;
+    }
+
+  // Test8 - Check if addStringArgument and ignore_rest=true works as expected
+  ctkCommandLineParser parser8;
+  QString test8String;
+  parser8.addStringArgument("--string", 0, &test8String, "This is a string",
+                            QString() /*defaultValue*/, true /* ignoreRest*/);
+
+  QStringList arguments8;
+  arguments8 << "ctkCommandLineParserTest1";
+  arguments8 << "--string" << "ctk";
+  arguments8 << expectedUnparsedArguments;
+
+  if (!parser8.parseArguments(arguments8))
+    {
+    qCritical() << "Test8 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  QString expectedTest8String = "ctk";
+  if (test8String != expectedTest8String)
+    {
+    qCritical() << "Test8 - Failed - test8String" << test8String
+        << ", expectedTest8String" << expectedTest8String;
+    return EXIT_FAILURE;
+    }
+
+  if (parser8.unparsedArguments() != expectedUnparsedArguments)
+    {
+    qCritical() << "Test8 - Failed - expectedUnparsedArguments " << expectedUnparsedArguments
+                << ", parser8.unparsedArguments" << parser8.unparsedArguments();
+    return EXIT_FAILURE;
+    }
+
+  // Test9 - Check if addIntegerArgument and ignore_rest=true works as expected
+  ctkCommandLineParser parser9;
+  int test9Int = 0;
+  parser9.addIntegerArgument("--integer", 0, &test9Int, "This is an integer",
+                             0 /*defaultValue*/, true /* ignoreRest*/);
+
+  QStringList arguments9;
+  arguments9 << "ctkCommandLineParserTest1";
+  arguments9 << "--integer" << "74";
+  arguments9 << expectedUnparsedArguments;
+
+  if (!parser9.parseArguments(arguments9))
+    {
+    qCritical() << "Test9 - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+
+  int expectedTest9Int = 74;
+  if (test9Int != expectedTest9Int)
+    {
+    qCritical() << "Test9 - Failed - test9Int" << test9Int
+        << ", expectedTest9Int" << expectedTest9Int;
+    return EXIT_FAILURE;
+    }
+
+  if (parser9.unparsedArguments() != expectedUnparsedArguments)
+    {
+    qCritical() << "Test9 - Failed - expectedUnparsedArguments " << expectedUnparsedArguments
+                << ", parser9.unparsedArguments" << parser9.unparsedArguments();
+    return EXIT_FAILURE;
+    }
+
+  // Test10 - Check if argumentParsed works as expected
+  ctkCommandLineParser parser10;
+  bool test10bool = false;
+  parser10.addBooleanArgument("--bool", 0, &test10bool, "This is a boolean");
+
+  // Since parseArguments hasn't been called, argumentParsed should return False
+  if(parser10.argumentParsed("--bool"))
+    {
+    qCritical() << "Test10 - Problem with argumentParsed() - Should return False";
+    return EXIT_FAILURE;
+    }
+
+  QStringList arguments10;
+  arguments10 << "ctkCommandLineParserTest1";
+  arguments10 << "--bool";
+
+  if (!parser10.parseArguments(arguments10))
+    {
+    qCritical() << "Test10 - Failed to parse arguments.";
+    return EXIT_FAILURE;
+    }
+
+  if(parser10.argumentParsed("--bool-notadded"))
+    {
+    qCritical() << "Test10 - Problem with argumentParsed() - "
+                   "Should return False since argument '--bool-notadded' hasn't been added.";
+    return EXIT_FAILURE;
+    }
+
+  if(!parser10.argumentParsed("--bool"))
+    {
+    qCritical() << "Test10 - Problem with argumentParsed() - Should return True";
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}

+ 468 - 0
Libs/Core/ctkCommandLineParser.cpp

@@ -0,0 +1,468 @@
+
+// Qt includes 
+#include <QHash>
+#include <QStringList>
+#include <QTextStream>
+#include <QDebug>
+
+// CTK includes
+#include "ctkCommandLineParser.h"
+
+namespace
+{
+
+class CommandLineParserArgumentDescriptionBase
+{
+public:
+  CommandLineParserArgumentDescriptionBase(
+    const char* longArg, const char* shortArg, const QString& argHelp, bool ignoreRest)
+    {
+    this->LongArg = QLatin1String(longArg);
+    this->ShortArg = QLatin1String(shortArg);
+    this->ArgHelp = argHelp;
+    this->IgnoreRest = ignoreRest;
+    this->NumberOfParametersToProcess = 0;
+    }
+  virtual bool addParameter(const QString& value) = 0;
+  QString helpText(int fieldWidth, const char charPad);
+  QString LongArg;
+  QString ShortArg;
+  QString ArgHelp;
+  bool    IgnoreRest;
+  int     NumberOfParametersToProcess;
+  QString RegularExpression;
+  QString ExactMatchFailedMessage;
+  QString ArgumentType;
+};
+
+// --------------------------------------------------------------------------
+QString CommandLineParserArgumentDescriptionBase::helpText(int fieldWidth, const char charPad)
+{
+  QString text;
+  QTextStream stream(&text);
+  stream.setFieldAlignment(QTextStream::AlignLeft);
+  stream.setPadChar(charPad);
+
+  if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
+    {
+    stream.setFieldWidth(fieldWidth);
+    }
+
+  if (!this->ShortArg.isEmpty())
+    {
+    stream  << QString("  %1").arg(this->ShortArg);
+    if(!this->LongArg.isEmpty())
+      {
+      stream << "\n";
+      }
+    }
+
+  if (!this->LongArg.isEmpty())
+    {
+    if(!this->ArgHelp.isEmpty())
+      {
+      stream.setFieldWidth(fieldWidth);
+      }
+    stream  << QString("  %1").arg(this->LongArg);
+    }
+  stream.setFieldWidth(0);
+  stream << this->ArgHelp << "\n";
+  return text;
+}
+
+#define CommandLineParserArgumentDescription_class(_NAME, _TYPE,                        \
+                                                   _NUMBEROFPARAMTOPROCESS,             \
+                                                   _REGEXP, _EXACTMACTHERRORMSG)        \
+  class CommandLineParser##_NAME##ArgumentDescription:                                  \
+    public CommandLineParserArgumentDescriptionBase                                     \
+  {                                                                                     \
+  public:                                                                               \
+    CommandLineParser##_NAME##ArgumentDescription(                                      \
+      const char* longArg, const char* shortArg, _TYPE * variable,                      \
+      const QString& argHelp, const _TYPE& defaultValue,                                \
+      bool ignoreRest):                                                                 \
+      CommandLineParserArgumentDescriptionBase(longArg , shortArg, argHelp, ignoreRest) \
+      {                                                                                 \
+      this->Variable = variable;                                                        \
+      this->DefaultValue = defaultValue;                                                \
+      this->NumberOfParametersToProcess = _NUMBEROFPARAMTOPROCESS;                      \
+      this->RegularExpression = _REGEXP;                                                \
+      this->ArgumentType = #_TYPE;                                                      \
+      }                                                                                 \
+     virtual bool addParameter(const QString& value);                                   \
+    _TYPE* Variable;                                                                    \
+    _TYPE DefaultValue;                                                                 \
+  };
+
+CommandLineParserArgumentDescription_class(String, QString, 1, ".*", "");
+CommandLineParserArgumentDescription_class(Boolean, bool, 0, "", "");
+CommandLineParserArgumentDescription_class(StringList, QStringList, -1, ".*", "");
+CommandLineParserArgumentDescription_class(Integer, int, 1, "-?[0-9]+",
+                                           "A negative or positive integer is expected.");
+
+#undef CommandLineParserArgumentDescription_class
+
+// --------------------------------------------------------------------------
+bool CommandLineParserStringArgumentDescription::addParameter(const QString& value)
+{
+  // Validate value
+  QRegExp regexp(this->RegularExpression);
+  if (!regexp.exactMatch(value))
+    {
+    return false;
+    }
+  (*this->Variable).clear();
+  (*this->Variable).append(value);
+  return true;
+}
+
+// --------------------------------------------------------------------------
+bool CommandLineParserBooleanArgumentDescription::addParameter(const QString& value)
+{
+  *this->Variable = (value == "true");
+  return true;
+}
+
+// --------------------------------------------------------------------------
+bool CommandLineParserStringListArgumentDescription::addParameter(const QString& value)
+{
+  // Validate value
+  QRegExp regexp(this->RegularExpression);
+  if (!regexp.exactMatch(value))
+    {
+    return false;
+    }
+  *this->Variable << value;
+  return true;
+}
+
+// --------------------------------------------------------------------------
+bool CommandLineParserIntegerArgumentDescription::addParameter(const QString& value)
+{
+  // Validate value
+  QRegExp regexp(this->RegularExpression);
+  if (!regexp.exactMatch(value))
+    {
+    return false;
+    }
+  *this->Variable = value.toInt();
+  return true;
+}
+
+}
+
+// --------------------------------------------------------------------------
+// ctkCommandLineParser::ctkInternal class
+
+// --------------------------------------------------------------------------
+class ctkCommandLineParser::ctkInternal
+{
+public:
+  ctkInternal():Debug(false),FieldWidth(0){}
+  
+  CommandLineParserArgumentDescriptionBase* argumentDescription(const QString& argument);
+  
+  QList<CommandLineParserArgumentDescriptionBase*>          ArgumentDescriptionList;
+  QHash<QString, CommandLineParserArgumentDescriptionBase*> ArgNameToArgumentDescriptionMap;
+  
+  #define ctkCommandLineParser_ctkInternal_declare_map(_NAME) \
+    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
+      LongArgTo##_NAME##ArgumentDescriptionMap;                          \
+    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
+      ShortArgTo##_NAME##ArgumentDescriptionMap;
+  
+  ctkCommandLineParser_ctkInternal_declare_map(String);
+  ctkCommandLineParser_ctkInternal_declare_map(Boolean);
+  ctkCommandLineParser_ctkInternal_declare_map(StringList);
+  ctkCommandLineParser_ctkInternal_declare_map(Integer);
+  
+  #undef ctkCommandLineParser_ctkInternal_declare_map
+  
+  QStringList UnparsedArguments; 
+  QStringList ProcessedArguments;
+  QString     ErrorString;
+  bool        Debug;
+  int         FieldWidth;
+};
+
+// --------------------------------------------------------------------------
+// ctkCommandLineParser::ctkInternal methods
+
+// --------------------------------------------------------------------------
+CommandLineParserArgumentDescriptionBase* 
+  ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
+{
+  if (this->ArgNameToArgumentDescriptionMap.contains(argument))
+    {
+    return this->ArgNameToArgumentDescriptionMap[argument];
+    }
+  return 0;
+}
+
+// --------------------------------------------------------------------------
+// ctkCommandLineParser methods
+
+// --------------------------------------------------------------------------
+ctkCommandLineParser::ctkCommandLineParser()
+{
+  this->Internal = new ctkInternal();
+}
+
+// --------------------------------------------------------------------------
+ctkCommandLineParser::~ctkCommandLineParser()
+{
+  delete this->Internal;
+}
+
+// --------------------------------------------------------------------------
+bool ctkCommandLineParser::parseArguments(const QStringList& arguments)
+{
+  // Reset
+  this->Internal->UnparsedArguments.clear();
+  this->Internal->ProcessedArguments.clear();
+  this->Internal->ErrorString.clear();
+
+  bool ignoreRest = false;
+  CommandLineParserArgumentDescriptionBase * currentArgDesc = 0;
+  for(int i = 1; i < arguments.size(); ++i)
+    {
+
+    QString argument = arguments.at(i);
+    if (this->Internal->Debug) { qDebug() << "Processing" << argument; }
+
+    // should argument be ignored ?
+    if (ignoreRest)
+      {
+      this->Internal->UnparsedArguments << argument;
+      continue;
+      }
+
+    // Skip if argument has already been parsed ...
+    if (this->Internal->ProcessedArguments.contains(argument))
+      {
+      qDebug() << "Skipping argument" << argument << " - Already processed !";
+      continue;
+      }
+
+    // Retrieve corresponding argument description
+    currentArgDesc = this->Internal->argumentDescription(argument);
+
+    // Is there a corresponding argument description ?
+    if (currentArgDesc)
+      {
+      this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg;
+      int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
+      ignoreRest = currentArgDesc->IgnoreRest;
+
+      // Is the number of parameters associated with the argument being processed known ?
+      if (numberOfParametersToProcess == 0)
+        {
+        currentArgDesc->addParameter("true");
+        }
+      else if (numberOfParametersToProcess > 0)
+        {
+        QString missingParameterError =
+            "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
+        for(int j=1; j <= numberOfParametersToProcess; ++j)
+          {
+          if (i + j >= arguments.size())
+            {
+            this->Internal->ErrorString = 
+                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
+            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
+            return false;
+            }
+          QString parameter = arguments.at(i + j);
+          if (this->Internal->Debug)
+            {
+            qDebug() << "Processing parameter" << j << ", value:" << parameter;
+            }
+          if (this->argumentAdded(parameter))
+            {
+            this->Internal->ErrorString =
+                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
+            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
+            return false;
+            }
+          if (!currentArgDesc->addParameter(parameter))
+            {
+            this->Internal->ErrorString = QString(
+                "Value(s) associated with argument %1 are incorrect. %2").
+                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
+
+            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
+            return false;
+            }
+          }
+        // Update main loop increment
+        i = i + numberOfParametersToProcess;
+        }
+      else if (numberOfParametersToProcess == -1)
+        {
+        if (this->Internal->Debug)
+          {
+          qDebug() << "Proccessing StringList ...";
+          }
+        int j = 1;
+        while(j + i < arguments.size())
+          {
+          if (this->argumentAdded(arguments.at(j + i)))
+            {
+            if (this->Internal->Debug)
+              {
+              qDebug() << "No more parameter for" << argument;
+              }
+            break;
+            }
+          QString parameter = arguments.at(j + i);
+          if (this->Internal->Debug)
+            {
+            qDebug() << "Processing parameter" << j << ", value:" << parameter;
+            }
+          if (!currentArgDesc->addParameter(parameter))
+            {
+            this->Internal->ErrorString = QString(
+                "Value(s) associated with argument %1 are incorrect. %2").
+                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
+
+            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
+            return false;
+            }
+          j++;
+          }
+        // Update main loop increment
+        i = i + j;
+        }
+      }
+    else
+      {
+      this->Internal->UnparsedArguments << argument;
+      }
+    }
+  return true;
+}
+
+// -------------------------------------------------------------------------
+QString ctkCommandLineParser::errorString()
+{
+  return this->Internal->ErrorString;
+}
+
+// -------------------------------------------------------------------------
+const QStringList& ctkCommandLineParser::unparsedArguments()
+{
+  return this->Internal->UnparsedArguments;
+}
+
+// -------------------------------------------------------------------------
+#define ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE)                 \
+  /* Make sure it's not already added */                                                   \
+  bool added = this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap.contains(longarg); \
+  Q_ASSERT(!added);                                                                        \
+  if (added) { return; }                                                                   \
+                                                                                           \
+  added = this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap.contains(shortarg);    \
+  Q_ASSERT(!added);                                                                        \
+  if (added) { return; }                                                                   \
+                                                                                           \
+  CommandLineParser##_NAME##ArgumentDescription * argDesc =                                \
+    new CommandLineParser##_NAME##ArgumentDescription(longarg, shortarg, var,              \
+                                                      argHelp, defaultValue, ignoreRest);  \
+                                                                                           \
+  Q_ASSERT(!(longarg == 0 && shortarg == 0));                                              \
+  if (longarg == 0 && shortarg == 0) { return; }                                           \
+  if (longarg != 0)                                                                        \
+    {                                                                                      \
+    this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap[longarg] = argDesc;           \
+    int argWidth = QString(longarg).length() + 7;                                          \
+    if (argWidth > this->Internal->FieldWidth)                                             \
+      {                                                                                    \
+      this->Internal->FieldWidth = argWidth;                                               \
+      }                                                                                    \
+    }                                                                                      \
+  if (shortarg != 0)                                                                       \
+    {                                                                                      \
+    this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap[shortarg] = argDesc;         \
+    int argWidth = QString(shortarg).length() + 7;                                         \
+    if (argWidth > this->Internal->FieldWidth)                                             \
+      {                                                                                    \
+      this->Internal->FieldWidth = argWidth;                                               \
+      }                                                                                    \
+    }                                                                                      \
+  this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;                      \
+  this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;                     \
+  this->Internal->ArgumentDescriptionList << argDesc;
+
+  // -------------------------------------------------------------------------
+  #define ctkCommandLineParser_addArgument_cxx(_NAME, _TYPE)                        \
+    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
+      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue,     \
+      bool ignoreRest)                                                                         \
+    {                                                                                          \
+    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
+    }
+
+  // -------------------------------------------------------------------------
+  #define ctkCommandLineParser_addArgument_cxx_without_ignore_rest(_NAME, _TYPE)    \
+    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
+      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue)     \
+    {                                                                                          \
+    bool ignoreRest = false;                                                                   \
+    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
+    }
+
+// --------------------------------------------------------------------------
+ctkCommandLineParser_addArgument_cxx(String, QString);
+ctkCommandLineParser_addArgument_cxx(Boolean, bool);
+ctkCommandLineParser_addArgument_cxx_without_ignore_rest(StringList, QStringList);
+ctkCommandLineParser_addArgument_cxx(Integer, int);
+
+#undef ctkCommandLineParser_addArgument_cxx
+
+// --------------------------------------------------------------------------
+bool ctkCommandLineParser::setExactMatchRegularExpression(
+    const QString& argument, const QString& expression, const QString& exactMatchFailedMessage)
+{
+  CommandLineParserArgumentDescriptionBase * argDesc =
+      this->Internal->argumentDescription(argument);
+  if (!argDesc)
+    {
+    return false;
+    }
+
+  if (argDesc->ArgumentType == "bool")
+    {
+    return false;
+    }
+  argDesc->RegularExpression = expression;
+  argDesc->ExactMatchFailedMessage = exactMatchFailedMessage;
+  return true;
+}
+
+// --------------------------------------------------------------------------
+QString ctkCommandLineParser::helpText(const char charPad)
+{
+  QString text;
+  QTextStream stream(&text);
+
+  // Loop over argument descriptions
+  foreach(CommandLineParserArgumentDescriptionBase* argDesc,
+          this->Internal->ArgumentDescriptionList)
+    {
+    stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
+    }
+  return text;
+}
+
+// --------------------------------------------------------------------------
+bool ctkCommandLineParser::argumentAdded(const QString& argument)
+{
+  return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
+}
+
+// --------------------------------------------------------------------------
+bool ctkCommandLineParser::argumentParsed(const QString& argument)
+{
+  return this->Internal->ProcessedArguments.contains(argument);
+}
+

+ 56 - 0
Libs/Core/ctkCommandLineParser.h

@@ -0,0 +1,56 @@
+#ifndef __ctkCommandLineParser_h
+#define __ctkCommandLineParser_h
+
+// Qt includes
+#include <QString>
+#include <QStringList>
+
+// CTK includes
+#include "CTKCoreExport.h"
+
+// --------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkCommandLineParser
+{
+public:
+
+  ctkCommandLineParser();
+  ~ctkCommandLineParser();
+  
+  bool parseArguments(const QStringList& arguments);
+
+  QString errorString();
+  
+  const QStringList& unparsedArguments();
+  
+  bool argumentAdded(const QString& argument);
+
+  bool argumentParsed(const QString& argument);
+  
+  void addBooleanArgument(const char* longarg, const char* shortarg, bool* var, 
+                          const QString& argHelp = QString(), const bool& defaultValue = false,
+                          bool ignoreRest = false);
+
+  void addStringArgument(const char* longarg, const char* shortarg, QString* var, 
+                         const QString& argHelp = QString(), const QString& defaultValue = QString(),
+                         bool ignoreRest = false);
+
+  void addIntegerArgument(const char* longarg, const char* shortarg, int* var,
+                         const QString& argHelp = QString(), const int& defaultValue = 0,
+                         bool ignoreRest = false);
+
+  void addStringListArgument(const char* longarg, const char* shortarg, QStringList* var,
+                             const QString& argHelp = QString(),
+                             const QStringList& defaultValue = QStringList());
+
+  bool setExactMatchRegularExpression(const QString& argument, const QString& expression,
+                                      const QString& ExactMatchFailedMessage);
+
+  int fieldWidth();
+
+  QString helpText(const char charPad = ' ');
+  
+private:
+  class ctkInternal;
+  ctkInternal * Internal;
+};
+#endif

+ 446 - 0
Libs/Visualization/VTK/Widgets/ctkVTKSliceView.cpp

@@ -0,0 +1,446 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+// Qt includes
+#include <QTimer>
+#include <QVBoxLayout>
+
+// CTK includes
+#include "ctkVTKSliceView.h"
+#include "ctkVTKSliceView_p.h"
+
+// VTK includes
+#include <vtkRendererCollection.h>
+#include <vtkRenderWindowInteractor.h>
+#include <vtkTextProperty.h>
+#include <vtkProperty2D.h>
+#include <vtkCamera.h>
+#include <vtkImageData.h>
+
+// --------------------------------------------------------------------------
+// RenderWindowItem methods
+
+//-----------------------------------------------------------------------------
+RenderWindowItem::RenderWindowItem(const QColor& rendererBackgroundColor,
+                                   double colorWindow, double colorLevel)
+{
+  // Instanciate an image mapper
+  this->ImageMapper = vtkSmartPointer<vtkImageMapper>::New();
+  this->ImageMapper->SetColorWindow(colorWindow);
+  this->ImageMapper->SetColorLevel(colorLevel);
+
+  // .. and its corresponding 2D actor
+  vtkSmartPointer<vtkActor2D> actor2D =  vtkSmartPointer<vtkActor2D>::New();
+  actor2D->SetMapper(this->ImageMapper);
+  actor2D->GetProperty()->SetDisplayLocationToBackground();
+
+  // Instanciate a renderer
+  this->Renderer = vtkSmartPointer<vtkRenderer>::New();
+  this->Renderer->SetBackground(rendererBackgroundColor.redF(),
+                                rendererBackgroundColor.greenF(),
+                                rendererBackgroundColor.blueF());
+
+  // .. and add it the 2D actor
+  this->Renderer->AddActor2D(actor2D);
+}
+
+//-----------------------------------------------------------------------------
+void RenderWindowItem::setViewport(double xMin, double yMin,
+                                   double viewportWidth, double viewportHeight)
+{
+  Q_ASSERT(this->Renderer);
+  this->Renderer->SetViewport( xMin, yMin, (xMin + viewportWidth), (yMin + viewportHeight));
+}
+
+// --------------------------------------------------------------------------
+// ctkVTKSliceViewPrivate methods
+
+// --------------------------------------------------------------------------
+ctkVTKSliceViewPrivate::ctkVTKSliceViewPrivate()
+{
+  //this->Renderer = vtkSmartPointer<vtkRenderer>::New();
+  this->RenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
+  this->Axes = vtkSmartPointer<vtkAxesActor>::New();
+  this->RenderPending = false;
+  this->RenderEnabled = false;
+  this->RenderWindowRowCount = 0;
+  this->RenderWindowColumnCount = 0;
+  this->RenderWindowLayoutType = ctkVTKSliceView::LeftRightTopBottom;
+  this->ColorWindow = 255;
+  this->ColorLevel = 127.5;
+  this->RendererBackgroundColor = QColor(Qt::black);
+  //this->ActorCollection = vtkSmartPointer<vtkActor2DCollection>::New();
+  //this->PolyDataCollection = vtkSmartPointer<vtkPolyDataCollection>::New();
+}
+
+// --------------------------------------------------------------------------
+void ctkVTKSliceViewPrivate::setupCornerAnnotation()
+{
+  foreach(const QSharedPointer<RenderWindowItem>& item, this->RenderWindowItemList)
+    {
+    if (!item->Renderer->HasViewProp(this->CornerAnnotation))
+      {
+      item->Renderer->AddViewProp(this->CornerAnnotation);
+      this->CornerAnnotation->SetMaximumLineHeight(0.07);
+      vtkTextProperty *tprop = this->CornerAnnotation->GetTextProperty();
+      tprop->ShadowOn();
+      }
+    this->CornerAnnotation->ClearAllTexts();
+    }
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKSliceViewPrivate::setupRendering()
+{
+  CTK_P(ctkVTKSliceView);
+
+  Q_ASSERT(this->RenderWindow);
+  this->RenderWindow->SetAlphaBitPlanes(1);
+  this->RenderWindow->SetMultiSamples(0);
+  this->RenderWindow->StereoCapableWindowOn();
+  
+  this->RenderWindow->GetRenderers()->RemoveAllItems();
+  
+  // Compute the width and height of each RenderWindowItem
+  double viewportWidth  = 1.0 / static_cast<double>(this->RenderWindowColumnCount);
+  double viewportHeight = 1.0 / static_cast<double>(this->RenderWindowRowCount);
+
+  // Postion of the Top-Left corner of the RenderWindowItem
+  float xMin, yMin;
+
+  // Loop through RenderWindowItem and set their respective viewport
+  for ( int rowId = 0; rowId < this->RenderWindowRowCount; ++rowId )
+    {
+    yMin = (this->RenderWindowRowCount - 1 - rowId) * viewportHeight;
+    xMin = 0.0;
+
+    for ( int columnId = 0; columnId < this->RenderWindowColumnCount; ++columnId )
+      {
+      this->RenderWindowItemList.at(p->renderWindowItemId(rowId, columnId))->setViewport(
+          xMin, yMin, viewportWidth, viewportHeight);
+      }
+    }
+
+  // Get active camera
+  vtkCamera * activeCamera = p->activeCamera();
+  Q_ASSERT(activeCamera);
+  activeCamera->ParallelProjectionOn();
+
+  // Add render(s) and set their respective active camera
+  foreach(const QSharedPointer<RenderWindowItem>& item, this->RenderWindowItemList)
+    {
+    this->RenderWindow->AddRenderer(item->Renderer);
+    item->Renderer->SetActiveCamera(activeCamera);
+    }
+
+  this->VTKWidget->SetRenderWindow(this->RenderWindow);
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKSliceViewPrivate::setupDefaultInteractor()
+{
+  CTK_P(ctkVTKSliceView);
+  p->setInteractor(this->RenderWindow->GetInteractor());
+}
+
+// --------------------------------------------------------------------------
+void ctkVTKSliceViewPrivate::updateRenderWindowItemZIndex(
+    ctkVTKSliceView::RenderWindowLayoutType layoutType)
+{
+  CTK_P(ctkVTKSliceView);
+
+  for (int rowId = 0; rowId < this->RenderWindowRowCount; ++rowId)
+    {
+    for (int columnId = 0; columnId < this->RenderWindowColumnCount; ++columnId)
+      {
+      int itemId = p->renderWindowItemId(rowId, columnId);
+      Q_ASSERT(itemId <= this->RenderWindowItemList.size());
+
+      RenderWindowItem * item = this->RenderWindowItemList.at(itemId).data();
+      Q_ASSERT(item->ImageMapper->GetInput());
+
+      // Default to ctkVTKSliceView::LeftRightTopBottom
+      int zSliceIndex = rowId * this->RenderWindowColumnCount + columnId;
+
+      if (layoutType == ctkVTKSliceView::LeftRightBottomTop)
+        {
+        zSliceIndex = (this->RenderWindowRowCount - rowId - 1) *
+                      this->RenderWindowColumnCount + columnId;
+        }
+
+      item->ImageMapper->SetZSlice(zSliceIndex);
+      }
+    }
+}
+
+//---------------------------------------------------------------------------
+// ctkVTKSliceView methods
+
+// --------------------------------------------------------------------------
+ctkVTKSliceView::ctkVTKSliceView(QWidget* _parent) : Superclass(_parent)
+{
+  CTK_INIT_PRIVATE(ctkVTKSliceView);
+  CTK_D(ctkVTKSliceView);
+  
+  d->VTKWidget = new QVTKWidget(this);
+  this->setLayout(new QVBoxLayout);
+  this->layout()->setMargin(0);
+  this->layout()->setSpacing(0);
+  this->layout()->addWidget(d->VTKWidget);
+
+  d->setupRendering();
+  d->setupDefaultInteractor();
+}
+
+// --------------------------------------------------------------------------
+ctkVTKSliceView::~ctkVTKSliceView()
+{
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::scheduleRender()
+{
+  CTK_D(ctkVTKSliceView);
+  if (!d->RenderEnabled)
+    {
+    return;
+    }
+  if (!d->RenderPending)
+    {
+    d->RenderPending = true;
+    QTimer::singleShot(0, this, SLOT(forceRender()));
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::forceRender()
+{
+  CTK_D(ctkVTKSliceView);
+  if (!d->RenderEnabled)
+    {
+    return;
+    }
+  d->RenderWindow->Render();
+  d->RenderPending = false;
+}
+
+//----------------------------------------------------------------------------
+CTK_GET_CXX(ctkVTKSliceView, vtkRenderWindow*, renderWindow, RenderWindow);
+
+//----------------------------------------------------------------------------
+CTK_GET_CXX(ctkVTKSliceView, vtkRenderWindowInteractor*, interactor, CurrentInteractor);
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::setInteractor(vtkRenderWindowInteractor* newInteractor)
+{
+  CTK_D(ctkVTKSliceView);
+  d->RenderWindow->SetInteractor(newInteractor);
+  d->CurrentInteractor = newInteractor;
+}
+
+//----------------------------------------------------------------------------
+vtkInteractorObserver* ctkVTKSliceView::interactorStyle()
+{
+  CTK_D(ctkVTKSliceView);
+  if (d->CurrentInteractor)
+    {
+    return d->CurrentInteractor->GetInteractorStyle();
+    }
+  else
+    {
+    return 0;
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::setCornerAnnotationText(const QString& text)
+{
+  CTK_D(ctkVTKSliceView);
+  d->CornerAnnotation->ClearAllTexts();
+  d->CornerAnnotation->SetText(2, text.toLatin1());
+}
+
+//----------------------------------------------------------------------------
+QString ctkVTKSliceView::cornerAnnotationText()const
+{
+  CTK_D(const ctkVTKSliceView);
+  return QLatin1String(d->CornerAnnotation->GetText(2));
+}
+
+// --------------------------------------------------------------------------
+void ctkVTKSliceView::setBackgroundColor(const QColor& newBackgroundColor)
+{
+  CTK_D(ctkVTKSliceView);
+
+  foreach(const QSharedPointer<RenderWindowItem>& item, d->RenderWindowItemList)
+    {
+    item->Renderer->SetBackground(newBackgroundColor.redF(),
+                                  newBackgroundColor.greenF(),
+                                  newBackgroundColor.blueF());
+    }
+
+  d->RendererBackgroundColor = newBackgroundColor;
+}
+
+//----------------------------------------------------------------------------
+CTK_GET_CXX(ctkVTKSliceView, QColor, backgroundColor, RendererBackgroundColor);
+
+//----------------------------------------------------------------------------
+vtkCamera* ctkVTKSliceView::activeCamera()
+{
+  CTK_D(ctkVTKSliceView);
+  if (d->RenderWindowItemList.size() == 0)
+    {
+    return 0;
+    }
+
+  // Obtain reference of the first renderer
+  vtkRenderer * firstRenderer = d->RenderWindowItemList.at(0)->Renderer;
+  if (firstRenderer->IsActiveCameraCreated())
+    {
+    return firstRenderer->GetActiveCamera();
+    }
+  else
+    {
+    return 0;
+    }
+  return 0;
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::resetCamera()
+{
+  CTK_D(ctkVTKSliceView);
+  foreach(const QSharedPointer<RenderWindowItem>& item, d->RenderWindowItemList)
+    {
+    item->Renderer->ResetCamera();
+    }
+}
+
+//----------------------------------------------------------------------------
+int ctkVTKSliceView::rendererCount()
+{
+  CTK_D(ctkVTKSliceView);
+  return d->RenderWindowItemList.size();
+}
+
+//----------------------------------------------------------------------------
+vtkRenderer* ctkVTKSliceView::renderer(int id)
+{
+  CTK_D(ctkVTKSliceView);
+  if (id < 0 || id >= d->RenderWindowItemList.size())
+    {
+    return 0;
+    }
+  return d->RenderWindowItemList.at(id)->Renderer;
+}
+
+//----------------------------------------------------------------------------
+CTK_SET_CXX(ctkVTKSliceView, bool, setRenderEnabled, RenderEnabled);
+CTK_GET_CXX(ctkVTKSliceView, bool, renderEnabled, RenderEnabled);
+
+//----------------------------------------------------------------------------
+int ctkVTKSliceView::renderWindowItemId(int rowId, int columnId)
+{
+  CTK_D(ctkVTKSliceView);
+  return d->RenderWindowColumnCount * rowId + columnId;
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::setRenderWindowLayoutType(ctkVTKSliceView::RenderWindowLayoutType layoutType)
+{
+  CTK_D(ctkVTKSliceView);
+  if (d->RenderWindowLayoutType == layoutType)
+    {
+    return;
+    }
+
+  d->updateRenderWindowItemZIndex(layoutType);
+
+  d->RenderWindowLayoutType = layoutType;
+}
+
+//----------------------------------------------------------------------------
+CTK_GET_CXX(ctkVTKSliceView, ctkVTKSliceView::RenderWindowLayoutType,
+            renderWindowLayoutType, RenderWindowLayoutType);
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::setRenderWindowLayout(int rowCount, int columnCount)
+{
+  CTK_D(ctkVTKSliceView);
+
+  // Sanity checks
+  Q_ASSERT(rowCount >= 0 && columnCount >= 0);
+  if(!(rowCount >= 0 && columnCount >= 0))
+    {
+    return;
+    }
+
+  if (d->RenderWindowRowCount == rowCount && d->RenderWindowColumnCount == columnCount)
+    {
+    return;
+    }
+
+  int extraItem = (rowCount * columnCount) - d->RenderWindowItemList.size();
+  if (extraItem > 0)
+    {
+    // Create extra RenderWindowItem(s)
+    while(extraItem > 0)
+      {
+      d->RenderWindowItemList.append(QSharedPointer<RenderWindowItem>(
+          new RenderWindowItem(d->RendererBackgroundColor, d->ColorWindow, d->ColorLevel)));
+      }
+    }
+  else
+    {
+    // Remove extra RenderWindowItem(s)
+    extraItem = qAbs(extraItem);
+    while(extraItem > 0)
+      {
+      d->RenderWindowItemList.removeLast();
+      --extraItem;
+      }
+    }
+
+  d->RenderWindowRowCount = rowCount;
+  d->RenderWindowColumnCount = columnCount;
+
+  d->setupRendering();
+}
+
+//----------------------------------------------------------------------------
+CTK_GET_CXX(ctkVTKSliceView, double, colorWindow, ColorWindow);
+CTK_GET_CXX(ctkVTKSliceView, double, colorLevel, ColorLevel);
+
+//----------------------------------------------------------------------------
+void ctkVTKSliceView::setImageData(vtkImageData* imageDataToSet)
+{
+  CTK_D(ctkVTKSliceView);
+
+  foreach(const QSharedPointer<RenderWindowItem>& item, d->RenderWindowItemList)
+    {
+    item->ImageMapper->SetInput(imageDataToSet);
+    }
+
+  d->updateRenderWindowItemZIndex(d->RenderWindowLayoutType);
+
+  d->ImageData = imageDataToSet;
+}
+

+ 136 - 0
Libs/Visualization/VTK/Widgets/ctkVTKSliceView.h

@@ -0,0 +1,136 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+#ifndef __ctkVTKSliceView_h
+#define __ctkVTKSliceView_h
+
+// Qt includes
+#include <QWidget>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "CTKVisualizationVTKWidgetsExport.h"
+
+class ctkVTKSliceViewPrivate;
+class vtkInteractorObserver;
+class vtkRenderWindowInteractor;
+class vtkRenderWindow;
+class vtkRenderer;
+class vtkCamera; 
+class vtkImageData;
+
+class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKSliceView : public QWidget
+{
+  Q_OBJECT
+  Q_ENUMS(RenderWindowLayoutType)
+  Q_PROPERTY(RenderWindowLayoutType renderWindowLayoutType READ renderWindowLayoutType
+             WRITE setRenderWindowLayoutType)
+  Q_PROPERTY(QString cornerAnnotationText READ cornerAnnotationText WRITE setCornerAnnotationText)
+  Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
+  Q_PROPERTY(double colorWindow READ colorWindow)
+  Q_PROPERTY(double colorLevel READ colorLevel)
+  Q_PROPERTY(bool renderEnabled READ renderEnabled WRITE setRenderEnabled)
+
+public:
+  /// Constructors
+  typedef QWidget   Superclass;
+  explicit ctkVTKSliceView(QWidget* parent = 0);
+  virtual ~ctkVTKSliceView();
+
+  /// If a render has already been scheduled, this called is a no-op
+  void scheduleRender();
+
+  /// Force a render even if a render is already ocurring
+  void forceRender();
+
+  /// Get underlying RenderWindow
+  vtkRenderWindow* renderWindow() const;
+  
+  /// Set/Get window interactor
+  vtkRenderWindowInteractor* interactor() const;
+  void setInteractor(vtkRenderWindowInteractor* newInteractor);
+
+  /// Get current interactor style
+  vtkInteractorObserver* interactorStyle();
+  
+  /// Set/Get corner annotation \a text
+  void setCornerAnnotationText(const QString& text);
+  QString cornerAnnotationText() const;
+
+  /// Set/Get background color
+  void setBackgroundColor(const QColor& newBackgroundColor);
+  QColor backgroundColor() const;
+
+  /// Get active camera
+  /// Note that the same camera is used with all the renderWindowItem
+  vtkCamera* activeCamera();
+
+  /// Reset cameras associated with all renderWindowItem
+  void resetCamera();
+  
+  /// Return number of underlying renderer
+  int rendererCount();
+  
+  /// Get a reference to the associated vtkRenderer(s) identified by its \a id
+  vtkRenderer* renderer(int id);
+
+
+  /// The layout type determines how the image slices should be displayed
+  /// within the different render view items.
+  /// \sa setRenderWindowLayout() renderWindowLayoutType()
+  enum RenderWindowLayoutType{ LeftRightTopBottom, LeftRightBottomTop};
+
+  /// Get current layout type
+  RenderWindowLayoutType renderWindowLayoutType() const;
+
+  /// Set current \a layoutType
+  void setRenderWindowLayoutType(RenderWindowLayoutType layoutType);
+
+  /// Split the current vtkRenderWindow in \a rowCount per \a columnCount grid
+  void setRenderWindowLayout(int rowCount, int columnCount);
+
+  /// Convenient function allowing to compute the renderWindowItemId
+  /// given \a rowId and \a columnId.
+  /// The following formula is used: ColumnCount * rowId + columnId
+  /// Note that the \a rowCount and \a columnCount correspond to the one set
+  /// using setRenderWindowLayout.
+  inline int renderWindowItemId(int rowId, int columnId);
+
+  /// Return if rendering is enabled
+  bool renderEnabled() const;
+
+  /// Enable/Disable rendering
+  void setRenderEnabled(bool value);
+
+  /// Get current color window
+  double colorWindow() const;
+
+  /// Get current color level
+  double colorLevel() const;
+
+  /// Set image data
+  void setImageData(vtkImageData* newImageData);
+  
+private:
+  CTK_DECLARE_PRIVATE(ctkVTKSliceView);
+}; 
+
+#endif

+ 101 - 0
Libs/Visualization/VTK/Widgets/ctkVTKSliceView_p.h

@@ -0,0 +1,101 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+#ifndef __ctkVTKSliceView_p_h
+#define __ctkVTKSliceView_p_h
+
+// Qt includes
+#include <QObject>
+#include <QColor>
+#include <QList>
+#include <QSharedPointer>
+
+// CTK includes
+#include <ctkPimpl.h>
+#include <ctkVTKObject.h>
+#include "ctkVTKSliceView.h"
+
+// VTK includes
+#include <QVTKWidget.h>
+#include <vtkAxesActor.h>
+#include <vtkCornerAnnotation.h>
+#include <vtkRenderer.h>
+#include <vtkRenderWindow.h>
+#include <vtkSmartPointer.h>
+#include <vtkWeakPointer.h>
+#include <vtkImageMapper.h>
+
+class vtkRenderWindowInteractor;
+
+
+//-----------------------------------------------------------------------------
+/// A RenderWindow can be split in 1x1, 2x3, ... grid view, each element of that grid
+/// will be identified as RenderWindowItem
+class RenderWindowItem
+{
+public:
+  RenderWindowItem(const QColor& rendererBackgroundColor, double colorWindow, double colorLevel);
+  void setViewport(double xMin, double yMin, double viewportWidth, double viewportHeight);
+
+  vtkSmartPointer<vtkRenderer>     Renderer;
+  vtkSmartPointer<vtkImageMapper>  ImageMapper;
+};
+
+//-----------------------------------------------------------------------------
+class ctkVTKSliceViewPrivate : public QObject,
+                               public ctkPrivate<ctkVTKSliceView>
+{
+  Q_OBJECT
+  CTK_DECLARE_PUBLIC(ctkVTKSliceView);
+public:
+  ctkVTKSliceViewPrivate();
+
+  /// Convenient setup methods
+  void setupCornerAnnotation();
+  void setupRendering();
+  void setupDefaultInteractor();
+
+  /// Update render window ImageMapper Z slice according to \a layoutType
+  void updateRenderWindowItemZIndex(ctkVTKSliceView::RenderWindowLayoutType layoutType);
+
+  QVTKWidget*                                   VTKWidget;
+  vtkSmartPointer<vtkRenderWindow>              RenderWindow;
+  bool                                          RenderPending;
+  bool                                          RenderEnabled;
+
+  int                                           RenderWindowRowCount;
+  int                                           RenderWindowColumnCount;
+  ctkVTKSliceView::RenderWindowLayoutType       RenderWindowLayoutType;
+  
+  vtkSmartPointer<vtkAxesActor>                 Axes;
+  vtkSmartPointer<vtkCornerAnnotation>          CornerAnnotation;
+
+  vtkWeakPointer<vtkRenderWindowInteractor>     CurrentInteractor;
+
+  QList<QSharedPointer<RenderWindowItem> >      RenderWindowItemList;
+  vtkWeakPointer<vtkImageData>                  ImageData;
+
+  double                                        ColorWindow;
+  double                                        ColorLevel;
+  QColor                                        RendererBackgroundColor;
+
+};
+
+#endif