Quellcode durchsuchen

Add customization of ctkCmdLineModuleXslTransform

It is now possible to change the class name of each widget
For more advanced customization, the xsl can be overloaded
using setXslExtraTransformation.
Julien Finet vor 13 Jahren
Ursprung
Commit
481f5055ae

+ 19 - 16
Libs/CommandLineModules/Core/Resources/ctkCmdLineModuleXmlToQtUi.xsl

@@ -166,13 +166,12 @@
     Match elements from the XML description
   ===================================================================
   -->
-
   <!-- start matching at 'executable' element -->
   <xsl:template match="/executable">
     <xsl:variable name="moduleTitle"><xsl:value-of select="title"/></xsl:variable>
     <ui version="4.0" >
       <class><xsl:value-of select="translate(normalize-space($moduleTitle), ' ', '')"/></class>
-      <widget class="QWidget" name="executable:{normalize-space($moduleTitle)}">
+      <widget class="{$executableWidget}" name="executable:{normalize-space($moduleTitle)}">
         <layout class="QVBoxLayout">
 
           <!-- This will generate QGroupBox items with the specific widgets -->
@@ -186,22 +185,24 @@
               </property>
             </spacer>
           </item>
-
         </layout>
       </widget>
-
       <connections>
         <xsl:apply-templates mode="connections" select="parameters/*"/>
       </connections>
     </ui>
   </xsl:template>
 
-
+  <!--
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    Parameters (default: ctkCollapsibleGroupBox)
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  -->
   <!-- Match the 'parameters' element and create the parameter groups (QGroupBox) -->
   <xsl:template match="parameters">
     <xsl:variable name="groupLabel"><xsl:value-of select="label"/></xsl:variable>
     <item>
-      <widget class="ctkCollapsibleGroupBox" name="paramGroup:{$groupLabel}">
+      <widget class="{$parametersWidget}" name="paramGroup:{$groupLabel}">
         <xsl:apply-templates select="./label"/>
         <xsl:apply-templates select="./description"/>
         <property name="checked">
@@ -213,7 +214,7 @@
       </widget>
     </item>
   </xsl:template>
-  
+
   <!--
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     BOOLEAN parameter (default: QCheckbox)
@@ -223,7 +224,7 @@
   <xsl:template match="parameters/boolean">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QCheckBox"  name="parameter:{name}">
+      <widget class="{$booleanWidget}"  name="parameter:{name}">
         <xsl:call-template name="commonWidgetProperties"/>
         <property name="text">
          <string/>
@@ -241,7 +242,7 @@
   <xsl:template match="parameters/integer">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QSpinBox"  name="parameter:{name}">
+      <widget class="{$integerWidget}"  name="parameter:{name}">
         <xsl:if test="not(constraints)">
           <property name="minimum">
             <number>-999999999</number>
@@ -264,7 +265,7 @@
   <xsl:template match="parameters/*[name()=('double','float')]">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QDoubleSpinBox"  name="parameter:{name}">
+      <widget class="{$floatingWidget}"  name="parameter:{name}">
         <property name="decimals">
           <number>6</number>
         </property>
@@ -290,7 +291,7 @@
   <xsl:template match="parameters/*[name()=('string', 'integer-vector', 'float-vector', 'double-vector', 'string-vector')]">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QLineEdit"  name="parameter:{name}">
+      <widget class="{$vectorWidget}"  name="parameter:{name}">
         <xsl:call-template name="commonWidgetProperties"/>
       </widget>
     </item>
@@ -305,7 +306,7 @@
   <xsl:template match="parameters/*[name()=('integer-enumeration', 'float-enumeration', 'double-enumeration', 'string-enumeration')]">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QComboBox"  name="parameter:{name}">
+      <widget class="{$enumWidget}"  name="parameter:{name}">
         <xsl:call-template name="commonWidgetProperties"/>
         <xsl:for-each select="element">
           <item>
@@ -329,7 +330,7 @@
     <item  row="{position()-1}" column="1">
       <layout class="QHBoxLayout">
         <item>
-          <widget class="ctkPathLineEdit"  name="parameter:{name}">
+          <widget class="{$imageWidget}"  name="parameter:{name}">
             <xsl:call-template name="commonWidgetProperties"/>
             <xsl:call-template name="createQtDesignerStringListProperty"/>
             <property name="filters">
@@ -359,7 +360,7 @@
     <item  row="{position()-1}" column="1">
       <layout class="QHBoxLayout">
         <item>
-          <widget class="ctkPathLineEdit"  name="parameter:{name}">
+          <widget class="{$directoryWidget}"  name="parameter:{name}">
             <xsl:call-template name="commonWidgetProperties"/>
             <property name="filters">
               <set>ctkPathLineEdit::Dirs</set>
@@ -386,7 +387,7 @@
   <xsl:template match="parameters/*[name()=('point', 'region')]">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="ctkCoordinatesWidget"  name="parameter:{name}">
+      <widget class="{$pointWidget}"  name="parameter:{name}">
         <xsl:call-template name="commonWidgetProperties"/>
       </widget>
     </item>
@@ -401,7 +402,7 @@
   <xsl:template match="parameters/*" priority="-1">
     <xsl:call-template name="gridItemWithLabel"/>
     <item  row="{position()-1}" column="1">
-      <widget class="QLabel"  name="{name}">
+      <widget class="unsupportedWidget"  name="{name}">
         <property name="text">
           <string>&lt;html&gt;&lt;head&gt;&lt;meta name="qrichtext" content="1" /&gt;&lt;style type="text/css"&gt;p, li { white-space: pre-wrap; }&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;p style="margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"&gt;&lt;span style=" color:#ff0000;">Element '<xsl:value-of select="name()"/>' not supported yet.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
         </property>
@@ -412,4 +413,6 @@
     </item>
   </xsl:template>
 
+  <!-- EXTRA TRANSFORMATIONS -->
+
 </xsl:stylesheet>

+ 26 - 8
Libs/CommandLineModules/Core/Testing/Cpp/CMakeLists.txt

@@ -1,22 +1,40 @@
 set(KIT ${PROJECT_NAME})
+set(LIBRARY_NAME ${PROJECT_NAME})
 
 create_test_sourcelist(Tests ${KIT}CppTests.cpp
   ctkCmdLineModuleFutureTest.cpp
   ctkCmdLineModuleXmlProgressWatcherTest.cpp
+  ctkCmdLineModuleXslTransformTest.cpp
   )
 
 set(TestsToRun ${Tests})
 remove(TestsToRun ${KIT}CppTests.cpp)
 
-set(SRC_FILES ${Tests}
+set(Tests_SRCS ${Tests_SRCS}
   ctkCmdLineModuleSignalTester.cpp
 )
+set(Tests_MOC_SRCS ${Tests_MOC_SRCS}
+  ctkCmdLineModuleSignalTester.h
+)
 
-qt4_wrap_cpp(SRC_FILES ctkCmdLineModuleSignalTester.h)
-
-set(LIBRARY_NAME ${PROJECT_NAME})
+include_directories(
+  ${CMAKE_SOURCE_DIR}/Libs/Testing
+  ${CMAKE_CURRENT_BINARY_DIR}
+  )
 
-add_executable(${KIT}CppTests ${SRC_FILES})
+set(Tests_MOC_CPP)
+QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS})
+QT4_GENERATE_MOCS(
+  ctkCmdLineModuleXslTransformTest.cpp
+  )
+set(Tests_UI_CPP)
+if(TEST_UI_FORMS)
+  QT4_WRAP_UI(Tests_UI_CPP ${Tests_UI_FORMS})
+endif()
+set(Tests_RESOURCES_SRCS)
+QT4_ADD_RESOURCES(Tests_RESOURCES_SRCS ${Tests_RESOURCES})
+
+add_executable(${KIT}CppTests ${Tests} ${Tests_SRCS} ${Tests_MOC_CPP} ${Tests_UI_CPP} ${Tests_RESOURCES_SRCS})
 target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES})
 add_dependencies(${KIT}CppTests ctkCmdLineTestModules)
 
@@ -24,7 +42,7 @@ add_dependencies(${KIT}CppTests ctkCmdLineTestModules)
 # Add Tests
 #
 
-foreach(_test ${TestsToRun})
-  SIMPLE_TEST(${_test})
-endforeach()
+SIMPLE_TEST(ctkCmdLineModuleFutureTest)
+SIMPLE_TEST(ctkCmdLineModuleXmlProgressWatcherTest)
+SIMPLE_TEST(ctkCmdLineModuleXslTransformTest)
 

+ 494 - 0
Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleXslTransformTest.cpp

@@ -0,0 +1,494 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QBuffer>
+#include <QCoreApplication>
+#include <QList>
+#include <QString>
+
+// CTK includes
+#include "ctkCmdLineModuleXslTransform.h"
+#include "ctkTest.h"
+
+// ----------------------------------------------------------------------------
+class ctkCmdLineModuleXslTransformTester: public QObject
+{
+  Q_OBJECT
+private slots:
+  void testTransform();
+  void testTransform_data();
+
+  void testBindVariable();
+  void testBindVariable_data();
+
+  void testXslExtraTransformation();
+  void testXslExtraTransformation_data();
+};
+
+QString invalidXml =
+  "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+  "<executable>";
+
+QString header =
+  "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+  "<executable>"
+  " <category/>"
+  " <title>A Test</title>"
+  " <description><![CDATA[No parameter.]]></description>"
+  " <version>0.1.0</version>"
+  " <documentation-url/>"
+  " <license/>"
+  " <contributor></contributor>"
+  " <acknowledgements/>";
+
+QString footer =
+  "</executable>\n";
+
+QString mainWidgetHeader =
+  "<ui version=\"4.0\">\n"
+  "    <class>ATest</class>\n"
+  "    <widget class=\"QWidget\" name=\"executable:A Test\">\n"
+  "        <layout class=\"QVBoxLayout\">\n";
+
+QString mainWidgetFooter =
+  "            <item>\n"
+  "                <spacer name=\"verticalSpacer\">\n"
+  "                    <property name=\"orientation\">\n"
+  "                        <enum>Qt::Vertical</enum>\n"
+  "                    </property>\n"
+  "                </spacer>\n"
+  "            </item>\n"
+  "        </layout>\n"
+  "    </widget>\n"
+  "    <connections/>\n"
+  "</ui>\n";
+
+QString parametersHeader =
+  " <parameters>"
+  "  <label>Parameters</label>"
+  "  <description><![CDATA[Parameters]]></description>";
+
+QString parametersFooter =
+  " </parameters>\n";
+
+QString parametersWidgetHeader =
+  "            <item>\n"
+  "                <widget class=\"ctkCollapsibleGroupBox\" name=\"paramGroup:Parameters\">\n"
+  "                    <property name=\"title\">\n"
+  "                        <string>Parameters</string>\n"
+  "                    </property>\n"
+  "                    <property name=\"toolTip\">\n"
+  "                        <string>Parameters</string>\n"
+  "                    </property>\n"
+  "                    <property name=\"checked\">\n"
+  "                        <bool>true</bool>\n"
+  "                    </property>\n"
+  ;
+
+QString parametersWidgetEmptyLayout =
+  "                    <layout class=\"QGridLayout\"/>\n";
+
+QString parametersLayoutHeader =
+  "                    <layout class=\"QGridLayout\">\n";
+
+QString parametersLayoutFooter =
+  "                    </layout>\n";
+
+QString parametersWidgetFooter =
+  "                </widget>\n"
+  "            </item>\n"
+  ;
+QString integer =
+  "<integer>"
+  " <name>integer</name>"
+  " <flag>-i</flag>"
+  " <longflag>--integer</longflag>"
+  " <description><![CDATA[Integer description]]></description>"
+  " <label>Integer</label>"
+  " <default>1</default>"
+  "</integer>"
+  ;
+
+QString integerWidgetLabel =
+  "                        <item row=\"0\" column=\"0\">\n"
+  "                            <widget class=\"QLabel\">\n"
+  "                                <property name=\"sizePolicy\">\n"
+  "                                    <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n"
+  "                                        <horstretch>0</horstretch>\n"
+  "                                        <verstretch>0</verstretch>\n"
+  "                                    </sizepolicy>\n"
+  "                                </property>\n"
+  "                                <property name=\"text\">\n"
+  "                                    <string>Integer</string>\n"
+  "                                </property>\n"
+  "                            </widget>\n"
+  "                        </item>\n"
+  ;
+QString integerWidgetSpinBoxHeader =
+  "                        <item row=\"0\" column=\"1\">\n"
+  ;
+QString integerWidgetSpinBox =
+  "                            <widget class=\"QSpinBox\" name=\"parameter:integer\">\n"
+  "                                <property name=\"minimum\">\n"
+  "                                    <number>-999999999</number>\n"
+  "                                </property>\n"
+  "                                <property name=\"maximum\">\n"
+  "                                    <number>999999999</number>\n"
+  "                                </property>\n"
+  "                                <property name=\"toolTip\">\n"
+  "                                    <string>Integer description</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:cppType\">\n"
+  "                                    <string>int</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:valueProperty\">\n"
+  "                                    <string>value</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"value\">\n"
+  "                                    <number>1</number>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:flag\">\n"
+  "                                    <string>-i</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:longflag\">\n"
+  "                                    <string>--integer</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:description\">\n"
+  "                                    <string>Integer description</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:label\">\n"
+  "                                    <string>Integer</string>\n"
+  "                                </property>\n"
+  "                                <property name=\"parameter:default\">\n"
+  "                                    <string>1</string>\n"
+  "                                </property>\n"
+  "                            </widget>\n"
+  ;
+QString integerWidgetSpinBoxFooter =
+  "                        </item>\n"
+  ;
+
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testTransform()
+{
+  ctkCmdLineModuleXslTransform transformer;
+
+  QFETCH(QString, input);
+  QByteArray inputByteArray = input.toUtf8();
+  QBuffer inputBuffer(&inputByteArray);
+  transformer.setInput(&inputBuffer);
+
+  QBuffer output;
+  output.open(QBuffer::ReadWrite);
+  transformer.setOutput(&output);
+
+  QFETCH(bool, expectedSuccess);
+  bool success = transformer.transform();
+  if (!success)
+    {
+    qDebug() << transformer.errorString();
+    QCOMPARE(transformer.error(), true);
+    QVERIFY(!transformer.errorString().isEmpty());
+    }
+  QCOMPARE(success, expectedSuccess);
+
+  QFETCH(QString, expectedOutput);
+  QCOMPARE(QString(output.readAll()), expectedOutput);
+}
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testTransform_data()
+{
+  QTest::addColumn<QString>("input");
+  QTest::addColumn<bool>("expectedSuccess");
+  QTest::addColumn<QString>("expectedOutput");
+
+  QTest::newRow("null") << QString() << false << QString();
+  QTest::newRow("empty") << QString("") << false << QString();
+  QTest::newRow("invalidXml") << invalidXml << false << QString();
+  QString noParameter = header + footer;
+  QString noParameterUi = mainWidgetHeader + mainWidgetFooter;
+  QTest::newRow("no parameter") << noParameter << true << noParameterUi;
+
+  QString justParameters =
+    header
+    + parametersHeader
+    + parametersFooter
+    + footer;
+  QString justParametersUi =
+    mainWidgetHeader
+    + parametersWidgetHeader
+    + parametersWidgetEmptyLayout
+    + parametersWidgetFooter
+    + mainWidgetFooter;
+  QTest::newRow("just parameters") << justParameters << true << justParametersUi;
+
+  QString integerParameter =
+    header
+    + parametersHeader
+    + integer
+    + parametersFooter
+    + footer;
+  QString integerParameterUi =
+    mainWidgetHeader
+    + parametersWidgetHeader
+    + parametersLayoutHeader
+    + integerWidgetLabel
+    + integerWidgetSpinBoxHeader
+    + integerWidgetSpinBox
+    + integerWidgetSpinBoxFooter
+    + parametersLayoutFooter
+    + parametersWidgetFooter
+    + mainWidgetFooter;
+  QTest::newRow("integer") << integerParameter << true << integerParameterUi;
+
+}
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testBindVariable()
+{
+  ctkCmdLineModuleXslTransform transformer;
+
+  QFETCH(QString, input);
+  QByteArray inputArray(input.toUtf8());
+  QBuffer inputBuffer(&inputArray);
+  transformer.setInput(&inputBuffer);
+
+  QBuffer output;
+  output.open(QBuffer::ReadWrite);
+  transformer.setOutput(&output);
+
+  QFETCH(QString, variableName);
+  QFETCH(QString, variableValue);
+  transformer.bindVariable(variableName, variableValue);
+
+  transformer.transform();
+
+  QFETCH(QString, expectedOutput);
+  QCOMPARE(QString(transformer.output()->readAll()), expectedOutput);
+}
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testBindVariable_data()
+{
+  QTest::addColumn<QString>("input");
+  QTest::addColumn<QString>("variableName");
+  QTest::addColumn<QString>("variableValue");
+  QTest::addColumn<QString>("expectedOutput");
+
+  QString integerParameter =
+    header
+    + parametersHeader
+    + integer
+    + parametersFooter
+    + footer;
+  QString integerParameterUi =
+    mainWidgetHeader
+    + parametersWidgetHeader
+    + parametersLayoutHeader
+    + integerWidgetLabel
+    + integerWidgetSpinBoxHeader
+    + integerWidgetSpinBox
+    + integerWidgetSpinBoxFooter
+    + parametersLayoutFooter
+    + parametersWidgetFooter
+    + mainWidgetFooter;
+  integerParameterUi.replace("QSpinBox", "ctkSliderWidget");
+  QTest::newRow("QSpinBox -> ctkSpinBox") << integerParameter
+                                          << QString("integerWidget")
+                                          << QString("ctkSliderWidget")
+                                          << integerParameterUi;
+}
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testXslExtraTransformation()
+{
+  ctkCmdLineModuleXslTransform transformer;
+
+  QFETCH(QString, input);
+  QByteArray inputArray(input.toUtf8());
+  QBuffer inputBuffer(&inputArray);
+  transformer.setInput(&inputBuffer);
+
+  QBuffer output;
+  output.open(QBuffer::ReadWrite);
+  transformer.setOutput(&output);
+
+  QFETCH(QString, extra);
+  QByteArray extraTransformationArray(extra.toUtf8());
+  QBuffer extraTransformationBuffer(&extraTransformationArray);
+  transformer.setXslExtraTransformation(&extraTransformationBuffer);
+
+  transformer.transform();
+
+  QFETCH(QString, expectedOutput);
+  //qDebug() << transformer.output();
+  //qDebug() << expectedOutput;
+  QCOMPARE(QString(transformer.output()->readAll()), expectedOutput);
+}
+
+// ----------------------------------------------------------------------------
+void ctkCmdLineModuleXslTransformTester::testXslExtraTransformation_data()
+{
+  QString extra =
+    "<xsl:template match=\"parameters/integer\" priority=\"1\">\n"
+    "<xsl:call-template name=\"gridItemWithLabel\"/>\n"
+    "<item  row=\"{position()-1}\" column=\"1\">\n"
+    "  <layout class=\"QHBoxLayout\">\n"
+    "    <item>\n"
+    "      <widget class=\"QSlider\"  name=\"parameter:{name}Slider\">\n"
+    "        <xsl:if test=\"not(constraints)\">\n"
+    "          <property name=\"minimum\">\n"
+    "            <number>-999999999</number>\n"
+    "          </property>\n"
+    "          <property name=\"maximum\">\n"
+    "            <number>999999999</number>\n"
+    "          </property>\n"
+    "        </xsl:if>\n"
+    "        <xsl:call-template name=\"commonWidgetProperties\"/>\n"
+    "      </widget>\n"
+    "    </item>"
+    "    <item>\n"
+    "      <widget class=\"QSpinBox\"  name=\"parameter:{name}\">\n"
+    "        <xsl:if test=\"not(constraints)\">\n"
+    "          <property name=\"minimum\">\n"
+    "            <number>-999999999</number>\n"
+    "          </property>\n"
+    "          <property name=\"maximum\">\n"
+    "            <number>999999999</number>\n"
+    "          </property>\n"
+    "        </xsl:if>\n"
+    "        <xsl:call-template name=\"commonWidgetProperties\"/>\n"
+    "      </widget>\n"
+    "    </item>\n"
+    "  </layout>\n"
+    "</item>\n"
+    "</xsl:template>\n"
+    ;
+  QString integerWidgetSliderSpinBox =
+    "                            <layout class=\"QHBoxLayout\">\n"
+    "                                <item>\n"
+    "                                    <widget class=\"QSlider\" name=\"parameter:integerSlider\">\n"
+    "                                        <property name=\"minimum\">\n"
+    "                                            <number>-999999999</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"maximum\">\n"
+    "                                            <number>999999999</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"toolTip\">\n"
+    "                                            <string>Integer description</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:cppType\">\n"
+    "                                            <string>int</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:valueProperty\">\n"
+    "                                            <string>value</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"value\">\n"
+    "                                            <number>1</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:flag\">\n"
+    "                                            <string>-i</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:longflag\">\n"
+    "                                            <string>--integer</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:description\">\n"
+    "                                            <string>Integer description</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:label\">\n"
+    "                                            <string>Integer</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:default\">\n"
+    "                                            <string>1</string>\n"
+    "                                        </property>\n"
+    "                                    </widget>\n"
+    "                                </item>\n"
+    "                                <item>\n"
+    "                                    <widget class=\"QSpinBox\" name=\"parameter:integer\">\n"
+    "                                        <property name=\"minimum\">\n"
+    "                                            <number>-999999999</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"maximum\">\n"
+    "                                            <number>999999999</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"toolTip\">\n"
+    "                                            <string>Integer description</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:cppType\">\n"
+    "                                            <string>int</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:valueProperty\">\n"
+    "                                            <string>value</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"value\">\n"
+    "                                            <number>1</number>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:flag\">\n"
+    "                                            <string>-i</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:longflag\">\n"
+    "                                            <string>--integer</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:description\">\n"
+    "                                            <string>Integer description</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:label\">\n"
+    "                                            <string>Integer</string>\n"
+    "                                        </property>\n"
+    "                                        <property name=\"parameter:default\">\n"
+    "                                            <string>1</string>\n"
+    "                                        </property>\n"
+    "                                    </widget>\n"
+    "                                </item>\n"
+    "                            </layout>\n"
+    ;
+  QTest::addColumn<QString>("input");
+  QTest::addColumn<QString>("extra");
+  QTest::addColumn<QString>("expectedOutput");
+
+  QString integerParameter =
+    header
+    + parametersHeader
+    + integer
+    + parametersFooter
+    + footer;
+  QString integerParameterUi =
+    mainWidgetHeader
+    + parametersWidgetHeader
+    + parametersLayoutHeader
+    + integerWidgetLabel
+    + integerWidgetSpinBoxHeader
+    + integerWidgetSliderSpinBox
+    + integerWidgetSpinBoxFooter
+    + parametersLayoutFooter
+    + parametersWidgetFooter
+    + mainWidgetFooter;
+  QTest::newRow("QSpinBox -> QSlider+QSpinBox") << integerParameter
+                                          << extra
+                                          << integerParameterUi;
+
+}
+
+// ----------------------------------------------------------------------------
+CTK_TEST_MAIN(ctkCmdLineModuleXslTransformTest)
+#include "moc_ctkCmdLineModuleXslTransformTest.cpp"

+ 2 - 2
Libs/CommandLineModules/Core/ctkCmdLineModuleXmlValidator.cpp

@@ -95,10 +95,10 @@ bool ctkCmdLineModuleXmlValidator::validateInput()
 
 bool ctkCmdLineModuleXmlValidator::error() const
 {
-  return !ErrorStr.isEmpty();
+  return !this->ErrorStr.isEmpty();
 }
 
 QString ctkCmdLineModuleXmlValidator::errorString() const
 {
-  return ErrorStr;
+  return this->ErrorStr;
 }

+ 91 - 46
Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.cpp

@@ -1,70 +1,93 @@
 /*=============================================================================
-  
+
   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 "ctkCmdLineModuleXslTransform.h"
 
-#include "ctkCmdLineModuleXmlMsgHandler_p.h"
+=============================================================================*/
 
-#include <QFile>
+// Qt includes
 #include <QBuffer>
+#include <QDebug>
+#include <QFile>
 #include <QXmlSchema>
 #include <QXmlSchemaValidator>
-#include <QXmlQuery>
-
-#include <QDebug>
 
+// CTK includes
+#include "ctkCmdLineModuleXslTransform.h"
+#include "ctkCmdLineModuleXmlMsgHandler_p.h"
 
 ctkCmdLineModuleXslTransform::ctkCmdLineModuleXslTransform(QIODevice *input, QIODevice *output)
-  : ctkCmdLineModuleXmlValidator(input),
-    Validate(false), OutputSchema(0), Transformation(0), Output(output)
+  : ctkCmdLineModuleXmlValidator(input)
+  , Validate(false)
+  , OutputSchema(0)
+  , Transformation(0)
+  , Output(output)
+  , XslTransform(QXmlQuery::XSLT20)
+  , MsgHandler(0)
+{
+  this->MsgHandler = new ctkCmdLineModuleXmlMsgHandler();
+  this->XslTransform.setMessageHandler(this->MsgHandler);
+
+  this->bindVariable("executableWidget", QVariant(QString("QWidget")));
+  this->bindVariable("parametersWidget", QVariant(QString("ctkCollapsibleGroupBox")));
+  this->bindVariable("booleanWidget", QVariant(QString("QCheckBox")));
+  this->bindVariable("integerWidget", QVariant(QString("QSpinBox")));
+  this->bindVariable("floatingWidget", QVariant(QString("QDoubleSpinBox")));
+  this->bindVariable("vectorWidget", QVariant(QString("QLineEdit")));
+  this->bindVariable("enumWidget", QVariant(QString("QComboBox")));
+  this->bindVariable("imageWidget", QVariant(QString("ctkPathLineEdit")));
+  this->bindVariable("directoryWidget", QVariant(QString("ctkPathLineEdit")));
+  this->bindVariable("pointWidget", QVariant(QString("ctkCoordinatesWidget")));
+  this->bindVariable("unsupportedWidget", QVariant(QString("QLabel")));
+}
+
+ctkCmdLineModuleXslTransform::~ctkCmdLineModuleXslTransform()
 {
+  delete this->MsgHandler;
 }
 
 void ctkCmdLineModuleXslTransform::setOutput(QIODevice* output)
 {
-  Output = output;
+  this->Output = output;
 }
 
 QIODevice* ctkCmdLineModuleXslTransform::output() const
 {
-  return Output;
+  return this->Output;
 }
 
 void ctkCmdLineModuleXslTransform::setOutputSchema(QIODevice *output)
 {
-  OutputSchema = output;
+  this->OutputSchema = output;
 }
 
 bool ctkCmdLineModuleXslTransform::transform()
 {
+  this->ErrorStr.clear();
+
   if (!Output)
   {
-    ErrorStr = "No output device set";
+    this->ErrorStr = "No output device set";
     return false;
   }
-
-  QIODevice* inputDevice = input();
+  QIODevice* inputDevice = this->input();
   if (!inputDevice)
   {
-    ErrorStr = "No input set for deriving an output.";
+    this->ErrorStr = "No input set for deriving an output.";
     return false;
   }
   else if (!(inputDevice->openMode() & QIODevice::ReadOnly))
@@ -73,72 +96,94 @@ bool ctkCmdLineModuleXslTransform::transform()
   }
   inputDevice->reset();
 
-  ctkCmdLineModuleXmlMsgHandler msgHandler;
 
-  QXmlQuery xslTransform(QXmlQuery::XSLT20);
-  xslTransform.setMessageHandler(&msgHandler);
-  if (!xslTransform.setFocus(inputDevice))
+  if (!this->XslTransform.setFocus(inputDevice))
   {
     QString msg("Error transforming XML input: %1");
-    ErrorStr = msg.arg(msgHandler.statusMessage());
+    this->ErrorStr = msg.arg(this->MsgHandler->statusMessage());
     return false;
   }
 
-  QIODevice* transformation = Transformation;
+  QIODevice* transformation = this->Transformation;
   QScopedPointer<QIODevice> defaultTransform(new QFile(":/ctkCmdLineModuleXmlToQtUi.xsl"));
   if (!transformation)
   {
     transformation = defaultTransform.data();
     transformation->open(QIODevice::ReadOnly);
   }
-  xslTransform.setQuery(transformation);
+  QString query(transformation->readAll());
+  QString extra;
+  foreach(QIODevice* extraIODevice, this->ExtraTransformations)
+    {
+    extraIODevice->open(QIODevice::ReadOnly);
+    extra += extraIODevice->readAll();
+    }
+  query.replace("<!-- EXTRA TRANSFORMATIONS -->", extra);
+#if 0
+  qDebug() << query;
+#endif
+  this->XslTransform.setQuery(query);
 
   bool closeOutput = false;
-  if (!(Output->openMode() & QIODevice::WriteOnly))
+  if (!(this->Output->openMode() & QIODevice::WriteOnly))
   {
-    Output->open(QIODevice::WriteOnly);
+    this->Output->open(QIODevice::WriteOnly);
     closeOutput = true;
   }
 
-  if (!xslTransform.evaluateTo(Output))
+  if (!this->XslTransform.evaluateTo(this->Output))
   {
     QString msg("Error transforming XML input, at line %1, column %2: %3");
-    ErrorStr = msg.arg(msgHandler.line()).arg(msgHandler.column())
-        .arg(msgHandler.statusMessage());
+    this->ErrorStr = msg.arg(this->MsgHandler->line()).arg(this->MsgHandler->column())
+      .arg(this->MsgHandler->statusMessage());
     return false;
   }
 
+#if 0
+  qDebug() << this->Output;
+#endif
+
   if (closeOutput)
   {
-    Output->close();
+    this->Output->close();
   }
   else
   {
-    Output->reset();
+    this->Output->reset();
   }
 
-  if (Validate)
+  if (this->Validate)
   {
-    return validateOutput();
+    return this->validateOutput();
   }
   return true;
 }
 
 void ctkCmdLineModuleXslTransform::setXslTransformation(QIODevice *transformation)
 {
-  Transformation = transformation;
+  this->Transformation = transformation;
+}
+
+void ctkCmdLineModuleXslTransform::bindVariable(const QString& name, const QVariant& value)
+{
+  this->XslTransform.bindVariable(name, value);
+}
+
+void ctkCmdLineModuleXslTransform::setXslExtraTransformations(QList<QIODevice*> transformations)
+{
+  this->ExtraTransformations = transformations;
 }
 
 void ctkCmdLineModuleXslTransform::setValidateOutput(bool validate)
 {
-  Validate = validate;
+  this->Validate = validate;
 }
 
 bool ctkCmdLineModuleXslTransform::validateOutput()
 {
-  ErrorStr.clear();
+  this->ErrorStr.clear();
 
-  QIODevice* outputSchema = OutputSchema;
+  QIODevice* outputSchema = this->OutputSchema;
 
   // If there is no custom schema for validating the output, we just return.
   // The QtDesigner.xsd schema below (which should be the default) exhausts
@@ -181,15 +226,15 @@ bool ctkCmdLineModuleXslTransform::validateOutput()
 
 bool ctkCmdLineModuleXslTransform::error() const
 {
-  return ctkCmdLineModuleXmlValidator::error() || !ErrorStr.isEmpty();
+  return ctkCmdLineModuleXmlValidator::error() || !this->ErrorStr.isEmpty();
 }
 
 QString ctkCmdLineModuleXslTransform::errorString() const
 {
-  QString errStr = ctkCmdLineModuleXmlValidator::errorString();
+  QString errStr = this->ctkCmdLineModuleXmlValidator::errorString();
   if (errStr.isEmpty())
   {
-    return ErrorStr;
+    return this->ErrorStr;
   }
   return errStr;
 }

+ 61 - 14
Libs/CommandLineModules/Core/ctkCmdLineModuleXslTransform.h

@@ -1,44 +1,49 @@
 /*=============================================================================
-  
+
   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 CTKCMDLINEMODULEXSLTRANSFORM_H
-#define CTKCMDLINEMODULEXSLTRANSFORM_H
+#ifndef __ctkCmdLineModuleXslTransform_h
+#define __ctkCmdLineModuleXslTransform_h
 
+// CTK includes
 #include "ctkCommandLineModulesCoreExport.h"
 #include "ctkCmdLineModuleXmlValidator.h"
+class ctkCmdLineModuleXmlMsgHandler;
 
+// Qt includes
+#include <QList>
 #include <QString>
-
+#include <QXmlQuery>
 class QIODevice;
 
 /**
  * \ingroup CommandLineModulesCore
  */
 class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleXslTransform
-    : public ctkCmdLineModuleXmlValidator
+  : public ctkCmdLineModuleXmlValidator
 {
 
 public:
 
   ctkCmdLineModuleXslTransform(QIODevice* input = 0, QIODevice* output = 0);
+  virtual ~ctkCmdLineModuleXslTransform();
 
   void setOutput(QIODevice* output);
   QIODevice* output() const;
@@ -49,7 +54,7 @@ public:
    * @brief Transforms an XML input via a XSL transformation.
    *
    * This method assumes that the input set via setInput() or supplied
-   * in the constructor is a valid XML fragment.
+   * in the constructor is a valid, non empty XML fragment.
    *
    * @return
    */
@@ -57,10 +62,41 @@ public:
 
   void setXslTransformation(QIODevice* transformation);
 
+  /**
+   * @brief XSL to be injected in the main XSL.
+   *
+   * This can be used to potentially overwrite templates.
+   * To avoid ambiguity, specify a priority > 1 in your overwriting templates
+   *
+   * @return
+   */
+  inline void setXslExtraTransformation(QIODevice* transformation);
+  void setXslExtraTransformations(QList<QIODevice*> transformations);
+
+  /**
+   *  @brief Binds the variable name to the value so that $name can be used
+   *  from within the query to refer to the value.
+   *  In the default XslTransformation, the widget classes are variable and can
+   *  be replaced with a new binding.
+   *  @sa QXmlQuery::bindVariable()
+   */
+  void bindVariable(const QString& name, const QVariant& value);
+
   void setValidateOutput(bool validate);
 
-  bool error() const;
-  QString errorString() const;
+  /** @brief returns true if an error occured
+   *  transform() sets the error flag if an error occured when transforming the
+   *  XML file into XSL.
+   *  \sa errorString
+   */
+  virtual bool error() const;
+
+  /** @brief Error message if any
+   *  transform() sets the error message if an error occured when transforming
+   * the XML file into XSL.
+   *  \sa error
+   */
+  virtual QString errorString() const;
 
 private:
 
@@ -70,9 +106,20 @@ private:
 
   QIODevice* OutputSchema;
   QIODevice* Transformation;
-
   QIODevice* Output;
+
+  QXmlQuery XslTransform;
+  QList<QIODevice*> ExtraTransformations;
+  ctkCmdLineModuleXmlMsgHandler* MsgHandler;
+
   QString ErrorStr;
 };
 
+void ctkCmdLineModuleXslTransform::setXslExtraTransformation(QIODevice* transformation)
+{
+  QList<QIODevice*> transformations;
+  transformations<<transformation;
+  this->setXslExtraTransformations(transformations);
+}
+
 #endif // CTKCMDLINEMODULEXSLTRANSFORM_H