Difference between revisions of "Documentation/Nightly/Developers/SlicerExecutionModel"

From Slicer Wiki
Jump to: navigation, search
(Created page with '== Introduction == The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mech…')
 
Tag: 2017 source edit
 
(32 intermediate revisions by 10 users not shown)
Line 1: Line 1:
== Introduction ==
+
==Introduction==
 
+
po
 
The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mechanism for incorporating command line programs as Slicer modules. These command line modules are self-describing, emitting an XML description of its command line arguments. Slicer uses this XML description to construct a GUI for the module.
 
The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mechanism for incorporating command line programs as Slicer modules. These command line modules are self-describing, emitting an XML description of its command line arguments. Slicer uses this XML description to construct a GUI for the module.
  
=== Types of Slicer Plugins ===
+
===Types of Slicer Plugins===
  
 
There are various types of plugins that Slicer supports as command line modules. This variety allows a breadth of integration choices to balance performance and flexibility.
 
There are various types of plugins that Slicer supports as command line modules. This variety allows a breadth of integration choices to balance performance and flexibility.
  
==== Shared object plugins (dll, so) with global symbols ====
+
====Shared object plugins (dll, so) with global symbols====
  
 
Shared object plugins with global symbols integrate into Slicer tighter than the Executable plugins.  Shared object plugins with global symbols can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO).  Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk.  Slicer looks for a standard entry point to execute the module called ModuleEntryPoint defined as
 
Shared object plugins with global symbols integrate into Slicer tighter than the Executable plugins.  Shared object plugins with global symbols can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO).  Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk.  Slicer looks for a standard entry point to execute the module called ModuleEntryPoint defined as
Line 48: Line 48:
 
</pre>
 
</pre>
  
==== Shared object plugins (dll, so) with entry points ====
+
====Shared object plugins (dll, so) with entry points====
  
Shared object plugins with entry points integrate into Slicer tighter than the Executable plugins. Shared object plugins with entry points can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for standard entry points for executing the module as well as for querying the module for it's xml description and logos. The entry points are defined as
+
Shared object plugins with entry points integrate into Slicer tighter than the Executable plugins. Shared object plugins with entry points can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for standard entry points for executing the module as well as for querying the module for its xml description and logos. The entry points are defined as
  
 
           int  ModuleEntryPoint(int argc, char* argv[]);
 
           int  ModuleEntryPoint(int argc, char* argv[]);
Line 58: Line 58:
 
GetXMLModuleDescription() and GetModuleLogo() are accessed during module discovery. GetModuleLogo() is optional.
 
GetXMLModuleDescription() and GetModuleLogo() are accessed during module discovery. GetModuleLogo() is optional.
  
==== Executable plugins with global symbols ====
+
====Executable plugins with global symbols====
  
 
Executable plugins with global symbols allow for a single executable to be developed that can be integrated into Slicer 3 or run standalone on a cluster. Plugins of this type are opened but not executed at module discovery time.  Slicer 3 looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength.  The data types for these symbols are
 
Executable plugins with global symbols allow for a single executable to be developed that can be integrated into Slicer 3 or run standalone on a cluster. Plugins of this type are opened but not executed at module discovery time.  Slicer 3 looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength.  The data types for these symbols are
Line 71: Line 71:
 
These global symbols are access during module discovery. ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, ModuleLogoLength are optional.
 
These global symbols are access during module discovery. ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, ModuleLogoLength are optional.
  
==== Executable plugins (exe) with command line options ====
+
====Executable plugins (exe) with command line options====
  
 
Executable plugins with command line options are the most flexible type of plugin. This approach allows for legacy applications to be integrated into Slicer using a wrapper around the legacy application.  Plugins of this type are executed at module discovery time, passing in the command line argument "--xml". The plugin responds to the "--xml" query by emitting the xml description of the module. The plugin is also executed at module discovery time with a "--logo" command line argument. The plugin responds to the "--logo" query by emitting a logo description.  
 
Executable plugins with command line options are the most flexible type of plugin. This approach allows for legacy applications to be integrated into Slicer using a wrapper around the legacy application.  Plugins of this type are executed at module discovery time, passing in the command line argument "--xml". The plugin responds to the "--xml" query by emitting the xml description of the module. The plugin is also executed at module discovery time with a "--logo" command line argument. The plugin responds to the "--logo" query by emitting a logo description.  
Line 80: Line 80:
 
  > module.exe --logo
 
  > module.exe --logo
  
==== Script plugins with Python ====
+
====Script plugins with Python====
  
Python scripts are found using the discovery mechanisms for the other plugins. Since Python plugins are fundamentally different from the shared and executable plugins, they are documented on their own [[Slicer3:Execution Model Documentation:Python | page]] '''TODO: update this link'''.
+
Python scripts are found using the discovery mechanisms for the other plugins. Since Python plugins are fundamentally different from the shared and executable plugins, they are documented on their own [[Documentation/Nightly/Developers/SlicerExecutionModel/Python|page]].
  
=== Calling Command Line Modules from Other Code ===
+
===Calling Command Line Modules from Other Code===
  
In addition to the automated GUI that shows up in the Slicer user interface, the Execution Model can be used to encapsulate functionality that is invoked by other parts of the program.  For instance, the Editor Module can invoke the Model Maker Module to build models.  By invoking a command line module in this way, you get the advantage of background processing with progress reporting.  See [[Slicer3:Execution Model Documentation:Programmatic Invocation | this page]] '''TODO: update this link''' for more details.
+
In addition to the automated GUI that shows up in the Slicer user interface, the Execution Model can be used to encapsulate functionality that is invoked by other parts of the program.  For instance, the Editor Module can invoke the Model Maker Module to build models.  By invoking a command line module in this way, you get the advantage of background processing with progress reporting.  See [[Documentation/Nightly/Developers/SlicerExecutionModel/Programmatic Invocation|this page]] for more details.
  
=== Architecture ===  
+
===Architecture===  
 
[[Image:ExecutionModelPlugins.png|600px|Plugin architecture]]
 
[[Image:ExecutionModelPlugins.png|600px|Plugin architecture]]
 
[[Image:CommandLineModule.png|400px|Module architecture]]
 
[[Image:CommandLineModule.png|400px|Module architecture]]
Line 94: Line 94:
 
[[Image:Class_parser_state_coll_graph.png|400px|Module description]]
 
[[Image:Class_parser_state_coll_graph.png|400px|Module description]]
  
== Module Description ==
+
==Module Description==
  
 
Modules are described using XML. The XML is used to generate the C++ command line code and the GUI for the application.
 
Modules are described using XML. The XML is used to generate the C++ command line code and the GUI for the application.
  
=== XML Schema ===
+
===XML Schema===
  
 
At a minimum, each module description must contain:
 
At a minimum, each module description must contain:
Line 113: Line 113:
 
In the following descriptions of each XML tag, CLP means command line processing and GUI means graphical user interface. Unless otherwise specified, tags are optional.
 
In the following descriptions of each XML tag, CLP means command line processing and GUI means graphical user interface. Unless otherwise specified, tags are optional.
  
; <executable> (required)
+
;<executable> (required)
 +
 
 +
;;<category>
 +
::Classifies the executable (e.g. Filtering, Segmentation). Category can be a ''dot'' separated string. Multiple categories can be given and should be separated by a ''semicolon''.
 +
::''for CLP'', not used.
 +
::''for GUI'', used on the menu selector to group executables. ''Dot'' separated strings can be used to generate sub-menus. ''Semicolon'' separated strings can be used to create multiple categories.
 +
;;</category>
 +
 
 +
;;<title> (required)
 +
::A word or two describing the executable (e.g. Median Filter, Anisotropic Diffusion
 +
::''for CLP'', not used.
 +
::''for GUI'', used to label the frame containing the GUI for the executable. Also, GUI names for volumes use this label as a prefix.
 +
;;</title>
 +
 
 +
;;<description> (required)
 +
::A long description of the executable. Any double quotes will be converted to single quotes.
 +
::''for CLP'', appears at the end of the --help.
 +
::''for GUI'', appears in the help frame.
 +
;;</description>
 +
 
 +
;;<version>
 +
::The version of the command line executable. A suggested format is:
 +
:::''major''.''minor''.''patch''.''build''.''status''
 +
:::where status is
 +
::::vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).
 +
::::a: alpha
 +
::::b: beta
 +
::::rc: release candidate
 +
::::fcs: first customer ship
 +
::''for CLP'', reported in response to --version.
 +
::''for GUI'', not used.
 +
;;</version>
 +
 
 +
;;<documentation-url>
 +
::The location of extended documentation for the executable, (e.g. http://www.na-mic.org/foo.html).
 +
;;</documentation-url>
  
;; <category>
+
;;<license>
:: Classifies the executable (e.g. Filtering, Segmentation). Category can be a ''dot'' separated string.
+
::The type of license or a url containing the license, (e.g. Berkeley, Apache, http://www.slicer.org/copyright/copyright.txt).
:: ''for CLP'', not used.
+
::''for CLP'', not used.
:: ''for GUI'', used on the menu selector to group executables. ''Dot'' separated strings can be used to generate sub-menus.
+
::''for GUI'', may show up in the Help or About section.
;; </category>
+
;;</license>
  
;; <title> (required)
+
;;<contributor>
:: A word or two describing the executable (e.g. Median Filter, Anisotropic Diffusion
+
::The author(s) of the command line executable (e.g. Pieper, Jim Miller).
:: ''for CLP'', not used.
+
::for ''CLP'', appears as part of --help
:: ''for GUI'', used to label the frame containing the GUI for the executable. Also, GUI names for volumes use this label as a prefix.
+
::for ''GUI'', may show up in the Help or About section.
;; </title>
+
;;</contributor>
  
;; <description> (required)
+
;;<acknowledgements>
:: A long description of the executable. Any double quotes will be converted to single quotes.
+
::Acknowledgements for funding agency, employer, colleague, (e.g. This work is part of the National Alliance for Medical Image Computing NAMIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149).
:: ''for CLP'', appears at the end of the --help.
+
::for ''CLP'', appears as part of --help
:: ''for GUI'', appears in the help frame.
+
::for ''GUI'', may show up in the Help of About section.
;; </description>
+
;;</acknowledgements>
  
;; <version>
+
;;<parameters [advanced="true|''false''"]> (required for each group of parameters)
:: The version of the command line executable. A suggested format is:
+
::Starts a group of parameters.
::: ''major''.''minor''.''patch''.''build''.''status''
+
::for ''CLP'', not used.
::: where status is
+
::for ''GUI'', defines a widget (in tk, a frame) that contains other widgets. If ''advanced'' is true, the frame will be closed initially.
:::: vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).
 
:::: a: alpha
 
:::: b: beta
 
:::: rc: release candidate
 
:::: fcs: first customer ship
 
:: ''for CLP'', reported in response to --version.
 
:: ''for GUI'', not used.
 
;; </version>
 
  
;; <documentation-url>
+
;;;<label> (required)
:: The location of extended documentation for the executable, (e.g. http://www.na-mic.org/foo.html).
+
:::A short string that summarizes a parameter group, (e.g. I/O, Diffusion)
;; </documentation-url>
+
:::for ''CLP'', not used.
 +
:::for ''GUI'', used to label the frame.
 +
;;;</label>
  
;; <license>
+
;;;<description> (required)
:: The type of license or a url containing the license, (e.g. Berkeley, Apache, http://www.slicer.org/copyright/copyright.txt).
+
:::A short description of the parameter group, (e.g. Input/Output Parameters, Anitostropic Diffusion Parameters). Any double quotes will be converted to single quotes.
:: ''for CLP'', not used.
+
:::''for CLP'', not used.
:: ''for GUI'', may show up in the Help or About section.
+
:::''for GUI'', used in balloon help for the frame containing the parameter group.
;; </license>
+
;;;</description>
  
;; <contributor>
+
;;;<integer> | <float> | <double> | <boolean> | <string> | <integer-vector> | <float-vector> | <double-vector> | <string-vector> | <integer-enumeration> | <float-enumeration> | <double-enumeration> | <string-enumeration> | <file> | <directory> | <image [type="''scalar''|''label''|''tensor''|''diffusion-weighted''|''vector''|''model''"] [reference="..."] > | <geometry [type="''fiberbundle''|''model''"] [reference="..."]> | <point [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''"]> | <pointfile [type="''any''|''point''|''line''|''angle''|''curve''|''closedcurve''"] [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''"]> | <region [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''"]> | &lt;table [type="''color''"] [fileExtensions="''.tsv''|''.csv''|''.txt''|''.ctbl''"]> | <transform [type="''linear''|''nonlinear''|''bspline''"] [reference="..."] fileExtensions=".h5,.tfm,.hdf5,.mat,.txt,.mrml">
:: The author(s) of the command line executable (e.g. Pieper, Jim Miller).
+
:::The type of the parameter.
:: for ''CLP'', appears as part of --help
+
::::The scalar types ('''integer''', '''float''', etc.) correspond to the usual programming language types.
:: for ''GUI'', may show up in the Help or About section.
+
::::The '''-vector''' types are represented by comma separated values of the scalar type.
;; </contributor>
+
::::The '''-enumeration''' types use the '''<element>''' tag to enumerate choices of the scalar type.
 +
::::'''<image>''' is a special type that indicates that the parameter is a file name that specifies images.
 +
::::The <region> parameter can be used to specify rectangular ROIs. The command-line syntax is --region X_center,Y_center,Z_center,X_radius,Y_Radius,Z_Radius.
 +
::::If the attribute ''multiple'' is "true", multiple arguments are allowed for '''scalar''', '''file''', '''directory''', '''image''', '''geometry''', '''point''', '''pointfile''' and '''region''' parameters. Limitation: the automatically built GUI will not support selecting multiple volumes for the '''image''' and '''pointfile''' argument, but they can be passed on the command line. If the parameter has a ''flag'' or ''longflag'', then the flag may be specified multiple times on the command line. The resulting C++ variable will be a std::vector of the scalar type. If the multiple parameter does not have a flag, then multiple arguments can appear on the command line. However, a multiple parameter with no flags must be the last parameter specified.
 +
::::The attribute ''coordinateSystem'' is allowed for the parameters '''point''', '''pointfile''' and '''region'''. Default value is '''lps'''. If coordinate system is specified inside the data file then the coordinate system specified in the xml file is ignored.
 +
::::The attribute ''fileExtensions'' is allowed for '''file''', '''pointfile''', '''image''', '''transform''' and '''geometry'''. fileExtensions can contain a list of comma separated file extensions for optional use by the GUI.
 +
::::Transform types: If not specified then any transform is accepted (linear, non-linar, composite, ...). By specifying a type, accepted transforms can be limited to 'linear' = any linear transform; 'non-linear' = grid, displacement field; 'bspline' = b-spline transform.
 +
::::The attribute ''reference'' can mean different things based on the parameter type. If  the parameter is a transform and the reference is transformable, then the transform hierarchy of the reference is manipulated such that the reference is under the transform. If the parameter is an image or a model, then the parameter is placed in subject hierarchy at the same level as the reference (note: not available in Slicer3). If the parameter is an image, reference will be used to initialize lookup table of the output image (note: not available in Slicer4).
  
;; <acknowledgements>
+
;;;;<name> (required if longflag is not specified)
:: Acknowledgements for funding agency, employer, colleague, (e.g. This work is part of the National Alliance for Medical Image Computing NAMIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149).
+
::::The name of a command line argument. If name is not specified, longflag will be used (e.g. conductance, numberOfIterations). The name must be usable as a C++ variable. For example, it CANNOT have spaces or special characters and must start with a letter.
:: for ''CLP'', appears as part of --help
+
::::''for CLP'', the name of the C++ variable.
:: for ''GUI'', may show up in the Help of About section.
+
::::''for GUI'', used internally.
;; </acknowledgements>
+
;;;;</name>
  
;; <parameters [advanced="true|''false''"]> (required for each group of parameters)
+
;;;;<description> (required)
:: Starts a group of parameters.
+
::::A brief description of the parameter. Any double quotes will be converted to single quotes.
:: for ''CLP'', not used.
+
::::''for CLP'', describes the parameter for --usage and --help.
:: for ''GUI'', defines a widget (in tk, a frame) that contains other widgets. If ''advanced'' is true, the frame will be closed initially.
+
::::''for GUI'', describes the parameter when the cursor is placed over the widget for the parameter (balloon help).
 +
;;;;</description>
  
;;; <label> (required)
+
;;;;<label> (required)
::: A short string that summarizes a parameter group, (e.g. I/O, Diffusion)
+
::::A label for parameter (e.g. Dicom Directory, Conductance).
::: for ''CLP'', not used.
+
::::''for'' CLP, not used.
::: for ''GUI'', used to label the frame.
+
::::''for'' GUI, the label for the parameter widget.
;;; </label>
+
;;;;</label>
  
;;; <description> (required)
+
;;;;<default>
::: A short description of the parameter group, (e.g. Input/Output Parameters, Anitostropic Diffusion Parameters). Any double quotes will be converted to single quotes.
+
::::A default value for the parameter. The default must be a type that is compatible with the parameter type. The vector parameters are specified as comma separated values of the atomic parameter type.
::: ''for CLP'', not used.
+
::::''for CLP'', contains the default for the parameter unless the parameter is a ''boolean''. The default for ''boolean'' parameters is always set to ''false''.
::: ''for GUI'', used in balloon help for the frame containing the parameter group.
+
::::''for GUI'', contains the default for the parameter.
;;; </description>
+
;;;;</default>
  
;;; <integer> | <float> | <double> | <boolean> | <string> | <integer-vector> | <float-vector> | <double-vector> | <string-vector> | <integer-enumeration> | <float-enumeration> | <double-enumeration> | <string-enumeration> | <file> | <directory> | <image [type="''scalar''|''label''|''tensor''|''diffusion-weighted''|''vector''|''model''"]> | <geometry [type="''fiberbundle''|''model''"]> | <point [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | <region [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]>
+
;;;;<flag [alias="a,b"] [deprecatedalias="c,d"]> (not required if longflag is present)
::: The type of the parameter.
+
::::A single character command line flag (e.g. s, W). Can provide "alias"'s (comma separated) if different flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different flags can be used to set the same parameter but the user should be notified of which "updated" flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.
:::: The scalar types ('''integer''', '''float''', etc.) correspond to the usual programming language types.  
+
::::''for CLP'', used as the short flag on the command line.
:::: The '''-vector''' types are represented by comma separated values of the scalar type.
+
::::''for GUI'', used when running the module.
:::: The '''-enumeration''' types use the '''<element>''' tag to enumerate choices of the scalar type.
+
;;;;</flag>
:::: '''<image>''' is a special type that indicates that the parameter is a file name that specifies images.  
 
:::: If the attribute ''multiple'' is "true", multiple arguments are allowed for '''scalar''', '''file''', '''directory''', '''image''', '''geometry''', '''point''' and '''region''' parameters. BUG: the automatically built GUI will not support selecting multiple volumes for the '''image''' argument, but they can be passed on the command line. If the parameter has a ''flag'' or ''longflag'', then the flag may be specified multiple times on the command line. The resulting C++ variable will be a std::vector of the scalar type. If the multiple parameter does not have a flag, then multiple arguments can appear on the command line. However, a multiple parameter with no flags must be the last parameter specified.
 
:::: The attribute ''coordinateSystem'' is allowed for the parameters '''point''' and '''region'''.  
 
:::: The attribute ''fileExtensions'' is allowed for '''file''', '''image''' and '''geometry'''. fileExtensions can contain a list of comma separated file extensions for optional use by the GUI.  
 
  
;;;; <name> (required if longflag is not specified)
+
;;;;<longflag [alias="foo,bar"] [deprecatedalias="garf"]> (not required if flag is present)
:::: The name of a command line argument. If name is not specified, longflag will be used (e.g. conductance, numberOfIterations). The name must be usable as a C++ variable. For example, it CANNOT have spaces or special characters and must start with a letter.
+
::::A command line flag (e.g. spacing, Watcher). Can provide "alias"'s (comma separated) if different long flags can be used to activate the same parameter.  Can provide "deprecatedalias"'s (comma separated) if different long flags can be used to set the same parameter but the user should be notified of which "updated" long flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned.  Parameters with flags allow one to override a default behavior.
:::: ''for CLP'', the name of the C++ variable.
+
::::Note: apparently you can't use hyphens in the longflag, so things like --my-option are not allows.  -gcs
:::: ''for GUI'', used internally.
+
::::''for CLP'', used as the long flag on the command line.
;;;; </name>
+
::::''for GUI'', used when running the module.
 +
;;;;</longflag>
  
;;;; <description> (required)
+
;;;;<constraints>
:::: A brief description of the parameter. Any double quotes will be converted to single quotes.
+
::::Encloses constraints on the value of a non-vector, non-enumerated parameter.
:::: ''for CLP'', describes the parameter for --usage and --help.
+
::::''for CLP'', not used.
:::: ''for GUI'', describes the parameter when the cursor is placed over the widget for the parameter (balloon help).
+
::::''for GUI'', if present, a slider will be created using the minimum, maximum and step specified.
;;;; </description>
 
  
;;;; <label> (required)
+
;;;;;<minimum>
:::: A label for parameter (e.g. Dicom Directory, Conductance).
+
:::::The minimum allowed value for the parameter. If not specified, the minimum is the smallest possible value for the parameter type.
:::: ''for'' CLP, not used.
+
;;;;;</minimum>
:::: ''for'' GUI, the label for the parameter widget.
 
;;;; </label>
 
  
;;;; <default>
+
;;;;;<maximum>
:::: A default value for the parameter. The default must be a type that is compatible with the parameter type. The vector parameters are specified as comma separated values of the atomic parameter type.
+
:::::The maximum allowed value for the parameter. If not specified, the maximum is the largest possible value for the parameter type.
:::: ''for CLP'', contains the default for the parameter unless the parameter is a ''boolean''. The default for ''boolean'' parameters is always set to ''false''.
+
;;;;;</maximum>
:::: ''for GUI'', contains the default for the parameter.
 
;;;; </default>
 
  
;;;; <flag [alias="a,b"] [deprecatedalias="c,d"]> (not required if longflag is present)
+
;;;;;<step>
:::: A single character command line flag (e.g. s, W). Can provide "alias"'s (comma separated) if different flags can be used to activate the same parameter.  Can provide "deprecatedalias"'s (comma separated) if different flags can be used to set the same parameter but the user should be notified of which "updated" flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned.  Parameters with flags allow one to override a default behavior.
+
:::::The increment for the parameter.
:::: ''for CLP'', used as the short flag on the command line.
+
;;;;;</step>
:::: ''for GUI'', used when running the module.
 
;;;; </flag>
 
  
;;;; <longflag [alias="foo,bar"] [deprecatedalias="garf"]> (not required if flag is present)
+
;;;;</constraints>
:::: A command line flag (e.g. spacing, Watcher). Can provide "alias"'s (comma separated) if different long flags can be used to activate the same parameter.  Can provide "deprecatedalias"'s (comma separated) if different long flags can be used to set the same parameter but the user should be notified of which "updated" long flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned.  Parameters with flags allow one to override a default behavior.
 
:::: Note: apparently you can't use hyphens in the longflag, so things like --my-option are not allows.  -gcs
 
:::: ''for CLP'', used as the long flag on the command line.
 
:::: ''for GUI'', used when running the module.
 
;;;; </longflag>
 
  
;;;; <constraints>
+
;;;;<channel> (required for file, pointfile, directory and image parameters)
:::: Encloses constraints on the value of a non-vector, non-enumerated parameter.
+
::::Specifies whether the parameter is an input or output parameter.
:::: ''for CLP'', not used.
+
::::''for CLP'', not used.
:::: ''for GUI'', if present, a slider will be created using the minimum, maximum and step specified.
+
::::''for GUI'', selects the proper widget for file handling.
 +
;;;;</channel>
  
;;;;; <minimum>
+
;;;;<index> (required if there are no flags specified)
::::: The minimum allowed value for the parameter. If not specified, the minimum is the smallest possible value for the parameter type.
+
::::An integer starting at 0, that specifies a command line argument that has no flags.
;;;;; </minimum>
+
::::Note: if you use index for, say, an image, the user must enter some input value into the GUI. If the user does not fill in a value, the plugin is not run at all.  However, slicer will "seem" to run it, and no error message is given.  -gcs
 +
::::''for CLP'', specifies the order of an argument that has no flags.
 +
::::''for GUI'', used when running the module.
 +
;;;;</index>
  
;;;;; <maximum>
+
;;;;<enumeration> (required for enumeration parameters)
::::: The maximum allowed value for the parameter. If not specified, the maximum is the largest possible value for the parameter type.
+
::::Encloses elements for the parameter. The parameter is restricted one and only one element.
;;;;; </maximum>
+
::::''for CLP'', not used.
 +
::::''for GUI'', defines a radio button with choices.
  
;;;;; <step>
+
;;;;;<element>
::::: The increment for the parameter.
+
:::::Defines the choice. Must be of the proper type for a parameter.
;;;;; </step>
+
:::::''for CLP'', not used.
 +
:::::''for GUI'', used as the label for the radio button.
 +
;;;;;</element>
  
;;;; </constraints>
+
;;;;</enumeration>
  
;;;; <channel> (required for file, directory and image parameters)
+
;;;;<reference [role="foo"] [parameter="bar"]/>
:::: Specifies whether the parameter is an input or output parameter.
+
::::A forward reference from the parameter to another parameter "bar" with the reference role "foo".
:::: ''for CLP'', not used.
 
:::: ''for GUI'', selects the proper widget for file handling.
 
;;;; </channel>
 
  
;;;; <index> (required if there are no flags specified)
+
;;;</integer> | </float> | </double> | </boolean> | </string> | </integer-vector> | </float-vector> | </double-vector> | </string-vector> | </integer-enumeration> | </float-enumeration> | </double-enumeration> | </string-enumeration> | </file> | </directory> | </image> | </geometry> | </point> | </pointfile> | </region> | &lt;/table> | </transform>
:::: An integer starting at 0, that specifies a command line argument that has no flags.
+
;;</parameters>
:::: Note: if you use index for, say, an image, the user must enter some input value into the GUI.  If the user does not fill in a value, the plugin is not run at all.  However, slicer will "seem" to run it, and no error message is given.  -gcs
 
:::: ''for CLP'', specifies the order of an argument that has no flags.
 
:::: ''for GUI'', used when running the module.
 
;;;; </index>
 
  
;;;; <enumeration> (required for enumeration parameters)
+
;</executable>
:::: Encloses elements for the parameter. The parameter is restricted one and only one element.
 
:::: ''for CLP'', not used.
 
:::: ''for GUI'', defines a radio button with choices.
 
  
;;;;; <element>
+
===Parameter References===
::::: Defines the choice. Must be of the proper type for a parameter.
 
::::: ''for CLP'', not used.
 
::::: ''for GUI'', used as the label for the raido button.
 
;;;;; </element>
 
  
;;;; </enumeration>
+
There are two types of references between parameters:
  
;;; </integer> | </float> | </double> | </boolean> | </string> | </integer-vector> | </float-vector> | </double-vector> | </string-vector> | </integer-enumeration> | </float-enumeration> | </double-enumeration> | </string-enumeration> | </file> | </directory> | </image> | </geometry> | </point> | </region>
+
*'''Attribute of image/geometry/transform parameter [reference="..."]''': The specialized attribute reference can mean different things based on the parameter type. If the parameter is a transform and the reference is transformable, then the transform hierarchy of the reference is manipulated such that the reference is under the transform. If the parameter is an image or a model, then the parameter is placed in subject hierarchy at the same level as the reference.
;; </parameters>
 
  
; </executable>
+
*'''Element under a parameter <reference [role="foo"] [parameter="bar"]/>''': This a generic reference called "forward reference", point from the parameter to another parameter with name "bar" with the reference role "foo". If both parameters are represented in Slicer as a MRML node, then the node associated to the parameter containing the reference will be added a node reference to the node associated to "bar" with the role "foo". These references can be useful for the application to identify other nodes that are in some way related to the node. For example: 1) get the moving and fixed images/landmarks from a transform that were inputs of the registration step creating it, 2) identify the source image of a processed image to know its provenance, 3) identify another node from which certain properties need to be propagated (e.g. color table)
  
== Slicer GUI Generation ==
+
==Slicer GUI Generation==
  
 
Slicer generates GUI's for each executable discovered during the startup process. Slicer searches directories stored in the Slicer Module Path. This path is set from the Slicer application in View->Application Settings->Modules->Additional module paths. Slicer attempts to run every executable in the prescribed directories and look for a valid XML file in response to a "--xml" command line.
 
Slicer generates GUI's for each executable discovered during the startup process. Slicer searches directories stored in the Slicer Module Path. This path is set from the Slicer application in View->Application Settings->Modules->Additional module paths. Slicer attempts to run every executable in the prescribed directories and look for a valid XML file in response to a "--xml" command line.
Line 288: Line 302:
 
Here are a few representative examples.
 
Here are a few representative examples.
  
=== A tour of the Execution Model XML ===
+
===A tour of the Execution Model XML===
  
 
This example is a sampler of the parameters available in the Execution Model.
 
This example is a sampler of the parameters available in the Execution Model.
Line 374: Line 388:
 
  </executable>
 
  </executable>
  
=== Module with an integer-vector, one input image and one output image ===
+
===Module with an integer-vector, one input image and one output image===
  
 
Here is the XML that describes the MedianImageFilter. The image on the right shows the generated Slicer 3 GUI. The help frame has been expanded by the user.
 
Here is the XML that describes the MedianImageFilter. The image on the right shows the generated Slicer 3 GUI. The help frame has been expanded by the user.
Line 433: Line 447:
 
       <index>1</index>
 
       <index>1</index>
 
       <description>Output filtered</description>
 
       <description>Output filtered</description>
 +
      <reference role="source" parameter="inputVolume"/>
 
     </image>
 
     </image>
 
   </parameters>
 
   </parameters>
Line 439: Line 454:
 
</pre>
 
</pre>
  
=== Module with a multiple scalars, one Input image and one output image ===
+
===Module with a multiple scalars, one Input image and one output image===
  
 
A module with
 
A module with
Line 529: Line 544:
  
  
=== Module with output text presented in GUI ===
+
===Module with output text presented in GUI===
  
 
Slicer auto generates GUI to display output from the command line module after execution is complete.
 
Slicer auto generates GUI to display output from the command line module after execution is complete.
Line 604: Line 619:
  
  
 
+
==Command Line Parsing==
== Command Line Parsing ==
 
  
 
The Slicer Execution Model has support for parsing executable command lines. The C++ code to parse the command line arguments is generated automatically from the same XML description that generates the GUI. ''GenerateCLP,'' located in ''SlicerExecutionModel/GenerateCLP'' reads the XML Module Description and creates an include file ''"Executable"CLP.h'' in the build tree. The executable includes this header file and accesses the code with the macro PARSE_ARGS.
 
The Slicer Execution Model has support for parsing executable command lines. The C++ code to parse the command line arguments is generated automatically from the same XML description that generates the GUI. ''GenerateCLP,'' located in ''SlicerExecutionModel/GenerateCLP'' reads the XML Module Description and creates an include file ''"Executable"CLP.h'' in the build tree. The executable includes this header file and accesses the code with the macro PARSE_ARGS.
Line 611: Line 625:
 
GenerateCLP provides the following to the executable:
 
GenerateCLP provides the following to the executable:
  
# A brief usage command if required arguments are missing
+
#A brief usage command if required arguments are missing
# A full help command if ''-h'' or ''--help'' is specified on the command line
+
#A full help command if ''-h'' or ''--help'' is specified on the command line
# A copy of the xml description if ''--xml'' is specified on the command line
+
#A copy of the xml description if ''--xml'' is specified on the command line
# An echo of the command line parameters and their values if ''--echo'' is specified
+
#An echo of the command line parameters and their values if ''--echo'' is specified
  
 
GenerateCLP provides the following source code:
 
GenerateCLP provides the following source code:
  
# A C++ declaration of the proper type for each parameter assigning the default value if specified by the XML
+
#A C++ declaration of the proper type for each parameter assigning the default value if specified by the XML
# For ''-vector'' parameters, a ''std::vector'' containing the proper C++ type fo the parameter. The generated code parses the comma separated strings to generate the ''std::vector''
+
#For ''-vector'' parameters, a ''std::vector'' containing the proper C++ type of the parameter. The generated code parses the comma separated strings to generate the ''std::vector''
  
=== Using GenerateCLP ===
+
===Using GenerateCLP===
  
 
GenerateCLP is normally used via CMake where it is implemented as a CUSTOM_COMMAND. To use GenerateCLP from CMake, you must first include the GenerateCLP package from your CMakeLists.txt file, which will make some macros available to you:
 
GenerateCLP is normally used via CMake where it is implemented as a CUSTOM_COMMAND. To use GenerateCLP from CMake, you must first include the GenerateCLP package from your CMakeLists.txt file, which will make some macros available to you:
Line 659: Line 673:
 
Although this example linked to ITK libraries, other libraries can be specified.
 
Although this example linked to ITK libraries, other libraries can be specified.
  
=== Short Example ===
+
===Short Example===
  
This example uses the XML for the [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation#Module_with_an_integer-vector.2C_one_input_image_and_one_output_image|Median Image Filter example].
+
This example uses the XML for the [[Slicer3:Execution_Model_Documentation#Module_with_an_integer-vector.2C_one_input_image_and_one_output_image|Median|Image Filter example]].
 
<br>
 
<br>
 
'''Note:''' The program '''MUST NOT''' write anything to stdout before the ''PARSE_ARGS'' statement. If something is written, the plugin discovery mechanism will not recognize the program as a plugin.
 
'''Note:''' The program '''MUST NOT''' write anything to stdout before the ''PARSE_ARGS'' statement. If something is written, the plugin discovery mechanism will not recognize the program as a plugin.
  
 
  <nowiki>#include "MedianImageFilterCLP.h"
 
  <nowiki>#include "MedianImageFilterCLP.h"
int main (int argc, char * argv[])
+
    int main (int argc, char * argv[])
  {
+
    {
  PARSE_ARGS;
+
    PARSE_ARGS;
  std::cout << "The size of the neighborhood is: " << neighborhood.size()
+
    std::cout << "The size of the neighborhood is: " << neighborhood.size()
    << " and the first element of the neighborhood is: " << neighborhood[0]
+
      << " and the first element of the neighborhood is: " << neighborhood[0]
    << std::endl;
+
      << std::endl;
  std::cout << "The input volume is: " << inputVolume << std::endl;
+
    std::cout << "The input volume is: " << inputVolume << std::endl;
  std::cout << "The output volume is: " << outputVolume << std::endl;
+
    std::cout << "The output volume is: " << outputVolume << std::endl;
  return EXIT_SUCCESS;
+
    return EXIT_SUCCESS;
  }
+
    }
</nowiki>
+
    </nowiki>
  
 
Here is the output --help:
 
Here is the output --help:
Line 736: Line 750:
 
</pre>
 
</pre>
  
=== Parameters and C++ code ===
+
===Parameters and C++ code===
  
 
This table shows how parameters are defined in the C++ code and how they are specified on the command line.
 
This table shows how parameters are defined in the C++ code and how they are specified on the command line.
  
 
{| border="1"
 
{| border="1"
! XML
+
!XML
! C++ Declaration
+
!C++ Declaration
! Command Line
+
!Command Line
 
|-
 
|-
 
|
 
|
Line 823: Line 837:
 
|}
 
|}
  
== Accessing Module Information at Runtime ==
+
==Accessing Module Information at Runtime==
  
 
All of the information contained in the XML description of a module can be accessed at run-time by the command line program. The ''ModuleDescriptionParser'' class library can parse an XML module description and populate a ''ModuleDescription'' instance.
 
All of the information contained in the XML description of a module can be accessed at run-time by the command line program. The ''ModuleDescriptionParser'' class library can parse an XML module description and populate a ''ModuleDescription'' instance.
Line 864: Line 878:
 
</pre>
 
</pre>
  
== Error Handling ==
+
==Error Handling==
  
 
GenerateCLP attempts to do error checking so that the generated C++ code will compile. These errors will show up as custom command errors during the build process.
 
GenerateCLP attempts to do error checking so that the generated C++ code will compile. These errors will show up as custom command errors during the build process.
  
* XML Errors
+
*XML Errors
** ''mismatched tag at line xx'' : The closing tag (a tag with </ >) does not have a matching opening tag.
+
**''mismatched tag at line xx'' : The closing tag (a tag with </ >) does not have a matching opening tag.
** ''not well-formed (invalid token) at line xx'' : Probably a blank in the token name.
+
**''not well-formed (invalid token) at line xx'' : Probably a blank in the token name.
* ModuleDescriptionParser Errors
+
*ModuleDescriptionParser Errors
** ''<executable> must be the outer most tag''
+
**''<executable> must be the outer most tag''
** ''<executable> was found inside another tag''
+
**''<executable> was found inside another tag''
** ''<parameters> can only be inside <executable>''
+
**''<parameters> can only be inside <executable>''
** ''<xxx> can only be used inside <parameters>''
+
**''<xxx> can only be used inside <parameters>''
** ''<flag> can only contain one character''
+
**''<flag> can only contain one character''
** ''<longname> can only contain letters, numbers and underscores and must start with a _ or letter''
+
**''<longname> can only contain letters, numbers and underscores and must start with an _ or letter''
** ''<name> can only contain letters, numbers and underscores and must start with an _ or letter''
+
**''<name> can only contain letters, numbers and underscores and must start with an _ or letter''
  
* ModuleDescriptionParser Warnings
+
*ModuleDescriptionParser Warnings
** ''<xxx> is an unknown parameter tag'' : Probably a misspelled parameter type.
+
**''<xxx> is an unknown parameter tag'' : Probably a misspelled parameter type.
  
* Compiler Errors
+
*Compiler Errors
** The generated C++ code may have syntax errors if invalid defaults are specified. These will show up during the C++ compilation.
+
**The generated C++ code may have syntax errors if invalid defaults are specified. These will show up during the C++ compilation.
  
== Interfacing Legacy Executables ==
+
==Interfacing Legacy Executables==
  
 
GenerateCLP is only provided as a convenience. Users can use the same XML Module Description to interface C++, shell scripts, tcl programs and even Matlab.
 
GenerateCLP is only provided as a convenience. Users can use the same XML Module Description to interface C++, shell scripts, tcl programs and even Matlab.
  
Example: [http://www.slicer.org/slicerWiki/index.php/Slicer3:FiberTrackingIntegration FiberTracking Integration]
+
Example: [[Slicer3:FiberTrackingIntegration|FiberTracking Integration]]
  
== Showing Progress in an Application ==
+
==Showing Progress in an Application==
  
 
Programs can communicate progress to the user in two ways. If the program is running an a stand-alone executable, it communicates with a simple XML syntax. If the program is loaded at run-time as a plugin library, it communicates through a C structure.
 
Programs can communicate progress to the user in two ways. If the program is running an a stand-alone executable, it communicates with a simple XML syntax. If the program is loaded at run-time as a plugin library, it communicates through a C structure.
Line 903: Line 917:
 
   </filter-name>
 
   </filter-name>
 
   <filter-comment>
 
   <filter-comment>
   ''description of program section or algrotihm''
+
   ''description of program section or algorithm''
 
   </filter-comment>
 
   </filter-comment>
 
  </filter-start>
 
  </filter-start>
Line 936: Line 950:
 
  }
 
  }
  
Details on how to use this mechanism are illustrated in [http://www.na-mic.org:8000/websvn/filedetails.php?repname=Slicer3&path=%2Ftrunk%2FApplications%2FCLI%2FitkPluginFilterWatcher.h&rev=0&sc=0 itkPluginFilterWatcher.h].
+
Details on how to use this mechanism are illustrated in [https://github.com/Slicer/Slicer/blob/master/Base/CLI/itkPluginFilterWatcher.h itkPluginFilterWatcher.h].
  
 
For vtk and itk execution model programs, two classes are available that make it simple to add progress. The classes, ''vtkPluginFilterWatcher'' and ''itk::PluginFilterWatcher'' use the vtk and itk command/observer mechanism to report progress.
 
For vtk and itk execution model programs, two classes are available that make it simple to add progress. The classes, ''vtkPluginFilterWatcher'' and ''itk::PluginFilterWatcher'' use the vtk and itk command/observer mechanism to report progress.
Line 942: Line 956:
 
vtkPluginFilterWatcher (vtkAlgorithm *'''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''') <br /> itk::PluginFilterWatcher (itk::ProcessObject '''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''')
 
vtkPluginFilterWatcher (vtkAlgorithm *'''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''') <br /> itk::PluginFilterWatcher (itk::ProcessObject '''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''')
  
: where:
+
:where:
;; filter  
+
;;filter
:: is the vtkAlgorithm or itk::ProcessObject to be watched.
+
::is the vtkAlgorithm or itk::ProcessObject to be watched.
;; comment  
+
;;comment
:: is a string that describes the algorithm.
+
::is a string that describes the algorithm.
;; inf  
+
;;inf
:: is an optional pointer to a structure that is used to communicate with the invoking program when the called program is used as a library plugin. If the pointer is 0, this prgram will not report progress if it is run as a library plugin. Default is 0.
+
::is an optional pointer to a structure that is used to communicate with the invoking program when the called program is used as a library plugin. If the pointer is 0, this prgram will not report progress if it is run as a library plugin. Default is 0.
;; fraction  
+
;;fraction
:: is the fraction (0-1) of progress that will be reported by this watcher. This is used when multiple filters are run and each filter represents a proportion of the total progress. Default is 1.
+
::is the fraction (0-1) of progress that will be reported by this watcher. This is used when multiple filters are run and each filter represents a proportion of the total progress. Default is 1.
;; start  
+
;;start
:: is the offset (0-1) of the progress for this filter. This is added to the progress of the filter. The reported progress of the watched filter is ''start + fraction * filter_progress''.
+
::is the offset (0-1) of the progress for this filter. This is added to the progress of the filter. The reported progress of the watched filter is ''start + fraction * filter_progress''.
  
 
<br /> The following example produces progress for a simple vtk program. The variable CLPProcessInformation is automatically declared and set in the program's ''program''CLP.h file.
 
<br /> The following example produces progress for a simple vtk program. The variable CLPProcessInformation is automatically declared and set in the program's ''program''CLP.h file.
  
 
  <nowiki>#include "vtkPluginFilterWatcher.h"
 
  <nowiki>#include "vtkPluginFilterWatcher.h"
...
+
    ...
  vtkMarchingCubes *cubes = vtkMarchingCubes::New();
+
    vtkMarchingCubes *cubes = vtkMarchingCubes::New();
    cubes->SetInput(reader->GetOutput());
+
      cubes->SetInput(reader->GetOutput());
  vtkPluginFilterWatcher watchCubes(cubes, "Generate Isosurface", CLPProcessInformation, .5, 0.0);
+
    vtkPluginFilterWatcher watchCubes(cubes, "Generate Isosurface", CLPProcessInformation, .5, 0.0);
  vtkDecimatePro *decimate = vtkDecimatePro::New();
+
    vtkDecimatePro *decimate = vtkDecimatePro::New();
    decimate->SetInput(cubes->GetOutput());
+
      decimate->SetInput(cubes->GetOutput());
  vtkPluginFilterWatcher watchDecimate(decimate, "Reduce Triangle Count", CLPProcessInformation, .5, 0.5);
+
    vtkPluginFilterWatcher watchDecimate(decimate, "Reduce Triangle Count", CLPProcessInformation, .5, 0.5);
  decimate->Update();
+
    decimate->Update();
</nowiki>
+
    </nowiki>
  
 
The following example produces progress for a simple itk program:
 
The following example produces progress for a simple itk program:
  
 
  <nowiki>#include "itkPluginFilterWatcher.h
 
  <nowiki>#include "itkPluginFilterWatcher.h
...
+
    ...
typedef itk::MedianImageFilter<ImageType,ImageType> FilterType;
+
    typedef itk::MedianImageFilter<ImageType,ImageType> FilterType;
FilterType::Pointer median  = FilterType::New();
+
    FilterType::Pointer median  = FilterType::New();
itk::PluginFilterWatcher watchMedian(median, "Denoise Image", CLPProcessInformation);
+
    itk::PluginFilterWatcher watchMedian(median, "Denoise Image", CLPProcessInformation);
</nowiki>
+
    </nowiki>
  
== Adding Module Logos to Slicer ==
+
==Adding Module Logos to Slicer==
Slicer plugins, both libraries and executables, can specify plugin-specific logos. These appear in Slicer when a module is selected. The logos are specified in a .h header file. The KWWidget utility, KWConvertImageToHeader, converts a .png file into a .h header file containing the encoded image and additional information such as width, height and pixel size. [[Documentation-3.5#Requirements_for_Modules|See here for additonal information.]]
+
Slicer plugins, both libraries and executables, can specify plugin-specific logos. These appear in Slicer when a module is selected. The logos are specified in a .h header file. The KWWidget utility, KWConvertImageToHeader, converts a .png file into a .h header file containing the encoded image and additional information such as width, height and pixel size. [[Documentation-3.5#Requirements_for_Modules|See here for additional information.]]
  
 
For Slicer, execution model plugin logos are stored in Modules/CLI/Resources. The corresponding image in .png format should be stored in Modules/CLI/ImageData. Othere plugins, created outside the Slicer tree, should store the logo and image in a similar location.
 
For Slicer, execution model plugin logos are stored in Modules/CLI/Resources. The corresponding image in .png format should be stored in Modules/CLI/ImageData. Othere plugins, created outside the Slicer tree, should store the logo and image in a similar location.
  
 
To add a logo to a plugin:
 
To add a logo to a plugin:
* Create a png image of the logo. The height of the logo should not exceed 40 pixels.
+
 
* Convert the logo to the KWWidget icon format as follows. '''NOTE:''' the prefix of the image and header file must be the same for a plugin logo.
+
*Create a png image of the logo. The height of the logo should not exceed 40 pixels.
 +
*Convert the logo to the KWWidget icon format as follows. '''NOTE:''' the prefix of the image and header file must be the same for a plugin logo.
 
<pre>
 
<pre>
 
cd Modules/CLI
 
cd Modules/CLI
 
KWConvertImageToHeader --base64 --zlib Resources/ITKLogo.h ImageData/ITKLogo.png
 
KWConvertImageToHeader --base64 --zlib Resources/ITKLogo.h ImageData/ITKLogo.png
 
</pre>
 
</pre>
* Add the logo to the SEMMacroBuildCLImacro in the CMakeLists.txt file for the plugin using LOGO_HEADER parameter.
 
  
=== Runtime specification of filter types ===
+
*Add the logo to the SEMMacroBuildCLImacro in the CMakeLists.txt file for the plugin using LOGO_HEADER parameter.
 +
 
 +
===Runtime specification of filter types===
  
 
ITK filters are templated over the images they process. The following code snippet shows how an execution model program can select the image types for filters based on the input images.
 
ITK filters are templated over the images they process. The following code snippet shows how an execution model program can select the image types for filters based on the input images.
Line 1,077: Line 1,093:
 
</pre>
 
</pre>
  
== Behind the Scenes ==
+
==Behind the Scenes==
  
 
A primary goal of the execution model is to relieve developers from developing GUI code and command line parsing code. This section describes the major components of the execution model implementation.
 
A primary goal of the execution model is to relieve developers from developing GUI code and command line parsing code. This section describes the major components of the execution model implementation.
  
=== Command Line Processing ===
+
===Command Line Processing===
  
 
Command line processing parses command line arguments and populates internal program variables. Every Unix (and windows) program can receive an argument list through its main entry point. All C and C++ programmers are familiar with the ''int main (int argc, char *[] argv)'' entry point in their programs. Most computer languages including scripting languages provide a similar mechanism to retrieve command line arguments. Simple command line processing directly accesses the strings defined in argv.
 
Command line processing parses command line arguments and populates internal program variables. Every Unix (and windows) program can receive an argument list through its main entry point. All C and C++ programmers are familiar with the ''int main (int argc, char *[] argv)'' entry point in their programs. Most computer languages including scripting languages provide a similar mechanism to retrieve command line arguments. Simple command line processing directly accesses the strings defined in argv.
Line 1,151: Line 1,167:
 
But even these libraries require some work to use.
 
But even these libraries require some work to use.
  
==== TCLAP ====
+
====TCLAP====
  
 
This example uses '''TCLAP''' to process a command line with 10 possible entries:
 
This example uses '''TCLAP''' to process a command line with 10 possible entries:
Line 1,212: Line 1,228:
 
You do get a lot for your investment here. Good error handling and help.
 
You do get a lot for your investment here. Good error handling and help.
  
=== Module Description Parser ===
+
===Module Description Parser===
The XML parsing is done by the ''ModuleDescriptionParser'' class library located in ''Slicer3/Libs/ModuleDescriptionParser''. ''GenerateCLP'' and Slicer3 use this class library to parse the module XML descriptions. The class ''ModuleDescrptionParser'' has one method, '''Parse''', that converts the XML description into an object model. The resulting object model has one ''ModuleDescription'', one or more ''ModuleParameterGroup'' each of which has one or more ''ModuleParameter''. Each instance has access methods to retrieve information from the XML.
+
The XML parsing is done by the [https://github.com/Slicer/SlicerExecutionModel/tree/master/ModuleDescriptionParser ModuleDescriptionParser] class library. ''GenerateCLP'' and Slicer3 use this class library to parse the module XML descriptions. The class ''ModuleDescriptionParser'' has one method, '''Parse''', that converts the XML description into an object model. The resulting object model has one ''ModuleDescription'', one or more ''ModuleParameterGroup'' each of which has one or more ''ModuleParameter''. Each instance has access methods to retrieve information from the XML.
* '''ModuleDescriptionParser''' - parser for command line module XML description.
+
 
*: ''Parse(std::string xml, ModuleDescription module)'' - parse an xml string and populate a ModuleDescription.
+
*'''ModuleDescriptionParser''' - parser for command line module XML description.
* '''ModuleDescription''' - contains information about a module  
+
*:''Parse(std::string xml, ModuleDescription module)'' - parse an xml string and populate a ModuleDescription.
*: const std::string ''GetCategory()'' : returns the contents of '''<category>'''.
+
*'''ModuleDescription''' - contains information about a module  
*: const std::string ''GetTitle()'' : returns the contents of '''<title>'''.
+
*:const std::string ''GetCategory()'' : returns the contents of '''<category>'''.
*: const std::string ''GetDescription()'' : returns the contents of '''<description>'''.
+
*:const std::string ''GetTitle()'' : returns the contents of '''<title>'''.
*: const std::string ''GetVersion()'' : returns the contents of '''<version>'''.
+
*:const std::string ''GetDescription()'' : returns the contents of '''<description>'''.
*: const std::string ''GetDocumentationURL()'' : returns the contents of '''<documentationURL>'''.
+
*:const std::string ''GetVersion()'' : returns the contents of '''<version>'''.
*: const std::string ''GetLicense()'' : returns the contents of '''<license>'''.
+
*:const std::string ''GetDocumentationURL()'' : returns the contents of '''<documentationURL>'''.
*: const std::string ''GetContributor()'' : returns the contents of '''<contributor>'''.
+
*:const std::string ''GetLicense()'' : returns the contents of '''<license>'''.
*: const std::vector<ModuleParameterGroup>& ''GetParameterGroups()'' : returns a vector of parameter groups.
+
*:const std::string ''GetContributor()'' : returns the contents of '''<contributor>'''.
* '''ModuleParameterGroup''' - contains ModuleParameters for each parameter group.
+
*:const std::vector<ModuleParameterGroup>& ''GetParameterGroups()'' : returns a vector of parameter groups.
*: const std::string ''GetLabel'' - returns the contents of '''<label>'''.
+
*'''ModuleParameterGroup''' - contains ModuleParameters for each parameter group.
*: const std::string ''GetDescription()'' - returns the contents of the parameter group's '''<description>'''.
+
*:const std::string ''GetLabel'' - returns the contents of '''<label>'''.
*: const std::string ''GetAdvanced()'' - returns advanced attribute. Either "true" of "false".
+
*:const std::string ''GetDescription()'' - returns the contents of the parameter group's '''<description>'''.
* '''ModuleParameter''' - contains information for a parameter.
+
*:const std::string ''GetAdvanced()'' - returns advanced attribute. Either "true" of "false".
 +
*'''ModuleParameter''' - contains information for a parameter.
 
*:GetTag() - returns the parameter's tag, e.g. '''<integer>, <image>''', etc.
 
*:GetTag() - returns the parameter's tag, e.g. '''<integer>, <image>''', etc.
 
*:GetName() - returns the parameter's '''<name>'''.
 
*:GetName() - returns the parameter's '''<name>'''.
Line 1,243: Line 1,260:
 
*:GetFlag() -  returns the parameter's '''<flag>'''.
 
*:GetFlag() -  returns the parameter's '''<flag>'''.
 
*:GetMultiple() -  returns the parameter's multiple attribute, either "true" or "false".
 
*:GetMultiple() -  returns the parameter's multiple attribute, either "true" or "false".
*:GetCoordinateSystem() -  returns the parameter's coordinate system attribute, one of "lps", "ras", or "ijk".
+
*:GetCoordinateSystem() -  returns the parameter's coordinate system attribute, one of "lps", "ras".
  
= Adding a new parameter type =
+
=Adding a new parameter type=
  
 
Adding a ''new parameter type'' to the execution model involves several modifications:
 
Adding a ''new parameter type'' to the execution model involves several modifications:
  
# A new XML tag needs to be defined to represent the new parameter type.
+
#A new XML tag needs to be defined to represent the new parameter type.
# ModuleDescriptionParser needs to be modified to parse the new parameter tag type and specify how the command line processing code generation is going to represent the parameter type
+
#ModuleDescriptionParser needs to be modified to parse the new parameter tag type and specify how the command line processing code generation is going to represent the parameter type
# CommandLineModuleGUI needs to be modified to construct the appropriate GUI element for the parameter type
+
#CommandLineModuleGUI needs to be modified to construct the appropriate GUI element for the parameter type
# CommandLineModuleLogic needs to be modified to put the parameter type onto the command line and request outputs parameter types be loaded back into Slicer and the MRML tree.
+
#CommandLineModuleLogic needs to be modified to put the parameter type onto the command line and request outputs parameter types be loaded back into Slicer and the MRML tree.
# SlicerApplicationLogic needs to be modified to ingest any output parameter types back into Slicer and the MRML tree.
+
#SlicerApplicationLogic needs to be modified to ingest any output parameter types back into Slicer and the MRML tree.
# Additional modification are needed if the parameter is to be passed directly from the MRML tree without using the filesystem.
+
#Additional modification are needed if the parameter is to be passed directly from the MRML tree without using the filesystem.
# Updating the documentation.
+
#Updating the documentation.
  
 
Simple parameter types can be passed directly on the command line.  For instance, scalars, small lists, positions, etc. Complicated parameter types are passed to the module via '''abstract files'''. In some cases, these parameters are actually passed as files, where Slicer reads/write the data to the filesystem.  In other cases, the parameters are passed as '''abstract files''' which are really references to within the Slicer memory model but appear to the Command Line Module as being a file.  The Command Line Module is written using standard ITK ImageFileReader/ImageFileWriter classes but the ITK ImageIO factory mechanism is used to direct the ImageFileReader/ImageFileWriter to talk directly to the Slicer MRML tree instead of to the filesystem.
 
Simple parameter types can be passed directly on the command line.  For instance, scalars, small lists, positions, etc. Complicated parameter types are passed to the module via '''abstract files'''. In some cases, these parameters are actually passed as files, where Slicer reads/write the data to the filesystem.  In other cases, the parameters are passed as '''abstract files''' which are really references to within the Slicer memory model but appear to the Command Line Module as being a file.  The Command Line Module is written using standard ITK ImageFileReader/ImageFileWriter classes but the ITK ImageIO factory mechanism is used to direct the ImageFileReader/ImageFileWriter to talk directly to the Slicer MRML tree instead of to the filesystem.
  
== XML tag ==
+
==XML tag==
  
 
This is by far the easiest of the tasks involved in adding a new parameter type but it should not approached hastily. The XML description of a module is designed to be application agnostic. As such, parameter types should be described abstractly or generically.  For instance, '''<geometry>''' tag corresponds to the Slicer model node, the '''<point>''' tag corresponds to the Slicer fiducial node, etc.
 
This is by far the easiest of the tasks involved in adding a new parameter type but it should not approached hastily. The XML description of a module is designed to be application agnostic. As such, parameter types should be described abstractly or generically.  For instance, '''<geometry>''' tag corresponds to the Slicer model node, the '''<point>''' tag corresponds to the Slicer fiducial node, etc.
Line 1,265: Line 1,282:
 
Once the tag name is defined, you need to decide whether the parameter type could or should support the attributes '''multiple''', '''coordinateSystem''', or '''fileExtensions''' or perhaps a new attribute type.
 
Once the tag name is defined, you need to decide whether the parameter type could or should support the attributes '''multiple''', '''coordinateSystem''', or '''fileExtensions''' or perhaps a new attribute type.
  
== Modifying ModuleDescriptionParser ==
+
==Modifying ModuleDescriptionParser==
  
 
SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx contains the code to parse the XML description of a module and represent that module description in C++ data structures.  To add a new parameter type, this code needs to be modified.
 
SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx contains the code to parse the XML description of a module and represent that module description in C++ data structures.  To add a new parameter type, this code needs to be modified.
  
Two routines need to be modified in ModuleDescriptionParser, ''startElement()'' and ''endElement()''. For ''startElement()'', a new case block needs to be added for the parameter type.  You can start by copying the case block for a similar parameter type. This case block is responsible for processing all the attributes for the tag and managing and reporting any errors. The case block may define the '''CPPType''', the ''ArgType''', and the '''StringToType''' needed for the code generation of the command line parsing. The '''CPPType''' is used by in the generated command line processing code to represent the parameter.  This may be a simple C++ type or an STL container. The '''ArgType''' is the canonical type of each component of the parameter. The '''StringToType''' is the name of the method to use to convert the ASCII command line parameter to the final '''ArgType'''. The ''endElement()'' method merely needs a new case block to add the parameter to the description.
+
Two routines need to be modified in ModuleDescriptionParser, ''startElement()'' and ''endElement()''. For ''startElement()'', a new case block needs to be added for the parameter type.  You can start by copying the case block for a similar parameter type. This case block is responsible for processing all the attributes for the tag and managing and reporting any errors. The case block may define the '''CPPType'<nowiki/>'', the ''ArgType''', and the '''StringToType''' needed for the code generation of the command line parsing. The '''CPPType''' is used by in the generated command line processing code to represent the parameter.  This may be a simple C++ type or an STL container. The '''ArgType''' is the canonical type of each component of the parameter. The '''StringToType''' is the name of the method to use to convert the ASCII command line parameter to the final '''ArgType'''. The ''endElement()'' method merely needs a new case block to add the parameter to the description.
  
 
ModuleDescriptionParser is fairly general, handling scalars, lists of scalars, and file types as parameter types. A parameter which does not fit these models will require considerable alterations to the ModuleDescriptionParser as well as the GenerateCLP.
 
ModuleDescriptionParser is fairly general, handling scalars, lists of scalars, and file types as parameter types. A parameter which does not fit these models will require considerable alterations to the ModuleDescriptionParser as well as the GenerateCLP.
  
== Constructing a GUI for a new parameter type ==
+
==Constructing a GUI for a new parameter type==
  
To have a GUI element appear in the module GUI for a new parameter type, the '''BuildGUI()''' method of Slicer3/Modules/CommandLineModule/vtkCommandLineModuleGUI.cxx needs to be modified. A new case block needs to be added to the '''BuildGUI()''' method for the new parameter type.  This case block is triggered off the XML tag for the parameter type. The case block is responsible for the constructing the appropriate GUI element for the parameter, complete with specifying the label and help text. The design is very simple.  A single widget is used for each parameter.  If a more complicated GUI is needed with multiple widgets, then perhaps a new widget is needed to encapsulated a set of widgets or a naming convention can be added to manage all the widgets associated with a parameter. The widgets for the parameters are stored in a map, indexed by the '''name''' of the parameter.
+
To have a GUI element appear in the module GUI for a new parameter type, Base\QTCLI\qSlicerCLIModuleUIHelper.cxx and Base\QTCLI\vtkSlicerCLIModuleLogic.cxx have to be modified. A new case block needs to be added to the '''BuildGUI()''' method for the new parameter type.  This case block is triggered off the XML tag for the parameter type. The case block is responsible for the constructing the appropriate GUI element for the parameter, complete with specifying the label and help text. The design is very simple.  A single widget is used for each parameter.  If a more complicated GUI is needed with multiple widgets, then perhaps a new widget is needed to encapsulated a set of widgets or a naming convention can be added to manage all the widgets associated with a parameter. The widgets for the parameters are stored in a map, indexed by the '''name''' of the parameter.
  
 
The vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) are designed to operate very generically with sets of parameters. The aforementioned map of widgets indexed by parameter '''name''' is one such example.  The implementation of several of the methods in the vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) generically iterate over the widget map or over the parameter list. It is important to keep this in mind as new parameters are added.  The design goal is to minimize the number of ''special cases'' in the code.
 
The vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) are designed to operate very generically with sets of parameters. The aforementioned map of widgets indexed by parameter '''name''' is one such example.  The implementation of several of the methods in the vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) generically iterate over the widget map or over the parameter list. It is important to keep this in mind as new parameters are added.  The design goal is to minimize the number of ''special cases'' in the code.
Line 1,281: Line 1,298:
 
Note that there may be separate case blocks for '''input''' and '''output''' parameter types.
 
Note that there may be separate case blocks for '''input''' and '''output''' parameter types.
  
== Communicating the new parameter to the Command Line Module ==
+
==Communicating the new parameter to the Command Line Module==
  
 
To communicate the new parameter type to and from a Command Line Module, the '''ApplyTask()''' method Slicer3/Modules/CommandLineModule/vtkCommandLineModuleLogic.cxx needs to be modified.  
 
To communicate the new parameter type to and from a Command Line Module, the '''ApplyTask()''' method Slicer3/Modules/CommandLineModule/vtkCommandLineModuleLogic.cxx needs to be modified.  
Line 1,289: Line 1,306:
 
The command line is constructed in two passes.  First, a pass is made over the parameter list, building the portion of the command line for any parameters that have flags.  Note that whether a parameter has a flag or not is up to the discretion of the module author and is not defined by the parameter type.  Second, a pass is made to construction the portion of the command  line for the parameters that do not have flags. These parameters are ordered appropriately, then placed on the command line. For parameters with flags, this code emits the flag and the parameter value.  For the parameters without flags, this code emits just the parameter value.  You will need to edit both of these passes to emit your parameter type.  In most cases, this is simply a matter of grabbing the parameter value from the parameter and pushing it onto the command line.  But some parameter types do require translation to a string appropriate for the command line.
 
The command line is constructed in two passes.  First, a pass is made over the parameter list, building the portion of the command line for any parameters that have flags.  Note that whether a parameter has a flag or not is up to the discretion of the module author and is not defined by the parameter type.  Second, a pass is made to construction the portion of the command  line for the parameters that do not have flags. These parameters are ordered appropriately, then placed on the command line. For parameters with flags, this code emits the flag and the parameter value.  For the parameters without flags, this code emits just the parameter value.  You will need to edit both of these passes to emit your parameter type.  In most cases, this is simply a matter of grabbing the parameter value from the parameter and pushing it onto the command line.  But some parameter types do require translation to a string appropriate for the command line.
  
== Output parameters from the Command Line Modules ==
+
==Output parameters from the Command Line Modules==
  
 
Any outputs from a Command Line Module that are communicated via files are queued to be read by the main application thread. Command Line Modules run in a separate execution thread from the main GUI.  This thread is not allowed to modify the Slicer GUI, so any results from the module that need to be read back into Slicer and displayed are queued for the main thread.
 
Any outputs from a Command Line Module that are communicated via files are queued to be read by the main application thread. Command Line Modules run in a separate execution thread from the main GUI.  This thread is not allowed to modify the Slicer GUI, so any results from the module that need to be read back into Slicer and displayed are queued for the main thread.
Line 1,295: Line 1,312:
 
The '''ProcessReadData()''' method of Slicer3/Base/Logic/vtkSlicerApplicationLogic.cxx pulls data from the queue to load back into Slicer and display. You may need to a case block for your new parameter type to construct the appropriate storage node and display node.
 
The '''ProcessReadData()''' method of Slicer3/Base/Logic/vtkSlicerApplicationLogic.cxx pulls data from the queue to load back into Slicer and display. You may need to a case block for your new parameter type to construct the appropriate storage node and display node.
  
== Communicating directly with the MRML tree ==
+
==Communicating directly with the MRML tree==
  
 
Currently scalar image types can sent as parameters to shared object command line modules without going through the filesystem.  Slicer provides a new ITK ImageIO factory that can communicated directly with the Slicer MRML tree. This ImageIO factory is in Slicer/Libs/MRMLIDImageIO. This approach can be extended for other image types such as vector or tensor volumes.
 
Currently scalar image types can sent as parameters to shared object command line modules without going through the filesystem.  Slicer provides a new ITK ImageIO factory that can communicated directly with the Slicer MRML tree. This ImageIO factory is in Slicer/Libs/MRMLIDImageIO. This approach can be extended for other image types such as vector or tensor volumes.
Line 1,301: Line 1,318:
 
For other constructs such as models and transforms, we'll need to see if an existing factory mechanism can be leverage to communicate directly with the Slicer MRML tree. An alternative may be to construct bridges specific to interfacing from a command line module to the Slicer MRML tree.
 
For other constructs such as models and transforms, we'll need to see if an existing factory mechanism can be leverage to communicate directly with the Slicer MRML tree. An alternative may be to construct bridges specific to interfacing from a command line module to the Slicer MRML tree.
  
== Adding new image types ==
+
==Adding new image types==
  
 
The Command Line Module support scalar, vector, tensor, and diffusion weighted images. Adding a new image type to the Command Line Module requires modify the sections of the code outlined above to manage GUI for the module, to construct temporary file names, to write image to disk, and load them back into the MRML tree.  These blocks are easy to identify as case blocks on vtkMRMLScalarVolumeNode, vtkMRMLDiffusionTensorVolumeNode, etc. Note that the '''<image>''' tag supports a '''type''' attribute that can scalar, label, vector, tensor, or diffusion-weighted.  The case block for '''image''' in the '''startElement()''' method of ModuleDescriptionParser would need to be extended to recognize a new type of image.
 
The Command Line Module support scalar, vector, tensor, and diffusion weighted images. Adding a new image type to the Command Line Module requires modify the sections of the code outlined above to manage GUI for the module, to construct temporary file names, to write image to disk, and load them back into the MRML tree.  These blocks are easy to identify as case blocks on vtkMRMLScalarVolumeNode, vtkMRMLDiffusionTensorVolumeNode, etc. Note that the '''<image>''' tag supports a '''type''' attribute that can scalar, label, vector, tensor, or diffusion-weighted.  The case block for '''image''' in the '''startElement()''' method of ModuleDescriptionParser would need to be extended to recognize a new type of image.
  
=FAQ=
+
{{:Documentation/{{documentation/version}}/Developers/FAQ/SlicerExecutionModel|SlicerExecutionModel}}
 
 
==What facilities are available for third party software to interface with the NA-MIC Kit?==
 
 
 
NA-MIC has committed to developing and supporting a generic batch processing interface.  This is the Slicer3 Command Line Interface (CLI). The application programming interface (API) for CLI was worked out based on interactions with the community including input from those familiar with LONI, ITK, Python and Matlab software development practices.
 
 
 
We believe this API already supports all the features required by to implement batch processing with BatchMake, the LONI Pipeline, and any other systems that we may want to work with in the future.  The [[Slicer3:Execution_Model_Documentation|Slicer3 Execution Model]] provides a simple XML-based descriptor for command line arguments for input/output volumes and related parameters.
 
 
 
==Which Third Party software has currently been interfaced to the NA-MIC Kit using the CLI?==
 
 
 
*Batchmake (Kitware)
 
*Python
 
*Matlab
 
 
 
== How do I debug my command line module? ==
 
 
 
See [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation:Debugging Debugging]
 
 
 
== How do I specify a Logo? ==
 
 
 
You need to create a logo.h file (embedding the zipped pixel data as base64 strings in a C source code).  There is a utility bundled with kwwidgets that accomplishes this.  From a Slicer3 build directory you can do something like this:
 
  
./Slicer3 --launch ../Slicer3-lib/KWWidgets-build/bin/KWConvertImageToHeader --base64 --zlib ~/Desktop/bioimagesuite_logo_www.h ~/Desktop/bioimagesuite_logo_www.png
+
=Laundry List=
 
 
The resulting .h header file can then be used as an argument to the 'generateclp' cmake macro like this:
 
 
 
generateclp(${CLP}_SOURCE ${CLP}.xml ${Slicer3_SOURCE_DIR}/Resources/bioimagesuite_logo_www.h)
 
 
 
''Note: logos are limited in size: if your .h file contains a symbol like 'image_bioimagesuite_logo_www' it will work.  But if it has several symbols like 'image_bioimagesuite_logo_www_1' 'image_bioimagesuite_logo_www_2' etc then the logo was too large and needs to be downsampled for use.''
 
 
 
== What do I do if I want to use the CLI outside of slicer? ==
 
 
 
In general CLI executables will depend on shared libraries provided by Slicer (such as ITKCommon, etc).  To be sure your run-time environment resolves to the correct versions, you should always use a form like:
 
./Slicer3 --launch <CLI>
 
or, you can start a shell from which you can run multiple CLI modules.  Something like:
 
./Slicer3 --launch xterm &
 
 
 
We do not suggest hard-coding the runtime paths into, for example, your .bashrc since this may interfere with other programs and can make it hard to use multiple versions of slicer on the same account.
 
 
 
= Laundry List =
 
  
 
Use this section to describe any features that are missing, not documented, or require additional work.
 
Use this section to describe any features that are missing, not documented, or require additional work.
  
* Comments from Hans Johnson, June 4, 2010:
+
*Comments from Hans Johnson, June 4, 2010:
** Enumerated types can only have about 3 characters in them, otherwise they don’t render in the slicer wizard at all.
+
**Enumerated types can only have about 3 characters in them, otherwise they don’t render in the slicer wizard at all.
** There is no way to unset an enumerated type, and the values are cached even after leaving the module.  If the default enumerated type is to be blank, and you select it, then you must restart slicer in order to make the blank option available again.
+
**There is no way to unset an enumerated type, and the values are cached even after leaving the module.  If the default enumerated type is to be blank, and you select it, then you must restart slicer in order to make the blank option available again.
** There is no way to have conditional options.  For example if one flag is used, then other flag can not be used, so it should be disabled.
+
**There is no way to have conditional options.  For example if one flag is used, then other flag can not be used, so it should be disabled.
 
+
*Multiple images, transforms, etc. cannot be selected in the module user interface shown in Slicer (https://issues.slicer.org/view.php?id=4299)
* Documenting your module:
+
*Documenting your module:
** a convenience script has been developed by Hans Johnson, which creates a wiki page describing the command line module by extracting the information from the xml description [[SEMToMediaWiki.py]]. TODO: extract the default values for the parameters to be included in the documentation
+
**a convenience script has been developed by Hans Johnson, which creates a wiki page describing the command line module by extracting the information from the xml description [[SEMToMediaWiki.py]]. TODO: extract the default values for the parameters to be included in the documentation

Latest revision as of 14:33, 7 May 2021

Home < Documentation < Nightly < Developers < SlicerExecutionModel

Contents

Introduction

po The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mechanism for incorporating command line programs as Slicer modules. These command line modules are self-describing, emitting an XML description of its command line arguments. Slicer uses this XML description to construct a GUI for the module.

Types of Slicer Plugins

There are various types of plugins that Slicer supports as command line modules. This variety allows a breadth of integration choices to balance performance and flexibility.

Shared object plugins (dll, so) with global symbols

Shared object plugins with global symbols integrate into Slicer tighter than the Executable plugins. Shared object plugins with global symbols can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for a standard entry point to execute the module called ModuleEntryPoint defined as

int ModuleEntryPoint(int argc, char* argv[]);

Slicer also looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. These global symbols provide the xml module description and data for the module logo. The data types for these symbols are

         char *XMLModuleDescription;
unsigned char *ModuleLogoImage;
          int  ModuleLogoWidth;
          int  ModuleLogoHeight;
          int  ModuleLogoPixelSize;
unsigned long  ModuleLogoLength;

These global symbols are accessed during module discovery. The ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength are optional.

Controlling what symbols are exported from a shared library module

Shared Object Modules are loaded into Slicer using standard "dlopen" style calls. The ModuleEntryPoint is located within the shared library and cached for later use. The ModuleEntryPoint, however, may not be the only function accessible to Slicer within the shared library. To protect against symbol clash at runtime, all functions and variables accessible within file scope of the shared library should be put in an anonymous namespace.

// Use an anonymous namespace to keep class types and function names
// from colliding when module is used as shared object module.  Every
// thing should be in an anonymous namespace except for the module
// entry point, e.g. main() or ModuleEntryPoint()
//
namespace {
    // functions like DoIt() should be put in the anonymous namespace
    template<class T> int DoIt( int argc, char * argv[], const T& targ)
    {
    }
} // end of anonymous namespace


int main(int argc, char* argv[] )
{
}

Shared object plugins (dll, so) with entry points

Shared object plugins with entry points integrate into Slicer tighter than the Executable plugins. Shared object plugins with entry points can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for standard entry points for executing the module as well as for querying the module for its xml description and logos. The entry points are defined as

          int  ModuleEntryPoint(int argc, char* argv[]);
         char *GetXMLModuleDescription();
unsigned char *GetModuleLogo()(int *width, int *height, int *pixel_size, unsigned long *bufferLength);

GetXMLModuleDescription() and GetModuleLogo() are accessed during module discovery. GetModuleLogo() is optional.

Executable plugins with global symbols

Executable plugins with global symbols allow for a single executable to be developed that can be integrated into Slicer 3 or run standalone on a cluster. Plugins of this type are opened but not executed at module discovery time. Slicer 3 looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. The data types for these symbols are

         char *XMLModuleDescription;
unsigned char *ModuleLogoImage;
          int  ModuleLogoWidth;
          int  ModuleLogoHeight;
          int  ModuleLogoPixelSize;
unsigned long  ModuleLogoLength;

These global symbols are access during module discovery. ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, ModuleLogoLength are optional.

Executable plugins (exe) with command line options

Executable plugins with command line options are the most flexible type of plugin. This approach allows for legacy applications to be integrated into Slicer using a wrapper around the legacy application. Plugins of this type are executed at module discovery time, passing in the command line argument "--xml". The plugin responds to the "--xml" query by emitting the xml description of the module. The plugin is also executed at module discovery time with a "--logo" command line argument. The plugin responds to the "--logo" query by emitting a logo description.

This type of plugin allows for legacy applications to be integrated into Slicer. A developer can provide Slicer with a small executable or shell script that responds to the "--xml" and "--logo" command line arguments needed for Slicer 3 integration and otherwise spawns the legacy executable passing down any command line arguments.

> module.exe --xml
> module.exe --logo

Script plugins with Python

Python scripts are found using the discovery mechanisms for the other plugins. Since Python plugins are fundamentally different from the shared and executable plugins, they are documented on their own page.

Calling Command Line Modules from Other Code

In addition to the automated GUI that shows up in the Slicer user interface, the Execution Model can be used to encapsulate functionality that is invoked by other parts of the program. For instance, the Editor Module can invoke the Model Maker Module to build models. By invoking a command line module in this way, you get the advantage of background processing with progress reporting. See this page for more details.

Architecture

Plugin architecture Module architecture Module factory Module description

Module Description

Modules are described using XML. The XML is used to generate the C++ command line code and the GUI for the application.

XML Schema

At a minimum, each module description must contain:

<?xml version="1.0" encoding="utf-8"?>
<executable>
<title>A title</title>
<description>A description</description>
  <parameters>
  At least one parameter
  </parameters>
</executable>

In the following descriptions of each XML tag, CLP means command line processing and GUI means graphical user interface. Unless otherwise specified, tags are optional.

<executable> (required)
<category>
Classifies the executable (e.g. Filtering, Segmentation). Category can be a dot separated string. Multiple categories can be given and should be separated by a semicolon.
for CLP, not used.
for GUI, used on the menu selector to group executables. Dot separated strings can be used to generate sub-menus. Semicolon separated strings can be used to create multiple categories.
</category>
<title> (required)
A word or two describing the executable (e.g. Median Filter, Anisotropic Diffusion
for CLP, not used.
for GUI, used to label the frame containing the GUI for the executable. Also, GUI names for volumes use this label as a prefix.
</title>
<description> (required)
A long description of the executable. Any double quotes will be converted to single quotes.
for CLP, appears at the end of the --help.
for GUI, appears in the help frame.
</description>
<version>
The version of the command line executable. A suggested format is:
major.minor.patch.build.status
where status is
vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).
a: alpha
b: beta
rc: release candidate
fcs: first customer ship
for CLP, reported in response to --version.
for GUI, not used.
</version>
<documentation-url>
The location of extended documentation for the executable, (e.g. http://www.na-mic.org/foo.html).
</documentation-url>
<license>
The type of license or a url containing the license, (e.g. Berkeley, Apache, http://www.slicer.org/copyright/copyright.txt).
for CLP, not used.
for GUI, may show up in the Help or About section.
</license>
<contributor>
The author(s) of the command line executable (e.g. Pieper, Jim Miller).
for CLP, appears as part of --help
for GUI, may show up in the Help or About section.
</contributor>
<acknowledgements>
Acknowledgements for funding agency, employer, colleague, (e.g. This work is part of the National Alliance for Medical Image Computing NAMIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149).
for CLP, appears as part of --help
for GUI, may show up in the Help of About section.
</acknowledgements>
<parameters [advanced="true|false"]> (required for each group of parameters)
Starts a group of parameters.
for CLP, not used.
for GUI, defines a widget (in tk, a frame) that contains other widgets. If advanced is true, the frame will be closed initially.
<label> (required)
A short string that summarizes a parameter group, (e.g. I/O, Diffusion)
for CLP, not used.
for GUI, used to label the frame.
</label>
<description> (required)
A short description of the parameter group, (e.g. Input/Output Parameters, Anitostropic Diffusion Parameters). Any double quotes will be converted to single quotes.
for CLP, not used.
for GUI, used in balloon help for the frame containing the parameter group.
</description>
<integer> | <float> | <double> | <boolean> | <string> | <integer-vector> | <float-vector> | <double-vector> | <string-vector> | <integer-enumeration> | <float-enumeration> | <double-enumeration> | <string-enumeration> | <file> | <directory> | <image [type="scalar|label|tensor|diffusion-weighted|vector|model"] [reference="..."] > | <geometry [type="fiberbundle|model"] [reference="..."]> | <point [multiple="true|false"] [coordinateSystem="lps|ras"]> | <pointfile [type="any|point|line|angle|curve|closedcurve"] [multiple="true|false"] [coordinateSystem="lps|ras"]> | <region [multiple="true|false"] [coordinateSystem="lps|ras"]> | <table [type="color"] [fileExtensions=".tsv|.csv|.txt|.ctbl"]> | <transform [type="linear|nonlinear|bspline"] [reference="..."] fileExtensions=".h5,.tfm,.hdf5,.mat,.txt,.mrml">
The type of the parameter.
The scalar types (integer, float, etc.) correspond to the usual programming language types.
The -vector types are represented by comma separated values of the scalar type.
The -enumeration types use the <element> tag to enumerate choices of the scalar type.
<image> is a special type that indicates that the parameter is a file name that specifies images.
The <region> parameter can be used to specify rectangular ROIs. The command-line syntax is --region X_center,Y_center,Z_center,X_radius,Y_Radius,Z_Radius.
If the attribute multiple is "true", multiple arguments are allowed for scalar, file, directory, image, geometry, point, pointfile and region parameters. Limitation: the automatically built GUI will not support selecting multiple volumes for the image and pointfile argument, but they can be passed on the command line. If the parameter has a flag or longflag, then the flag may be specified multiple times on the command line. The resulting C++ variable will be a std::vector of the scalar type. If the multiple parameter does not have a flag, then multiple arguments can appear on the command line. However, a multiple parameter with no flags must be the last parameter specified.
The attribute coordinateSystem is allowed for the parameters point, pointfile and region. Default value is lps. If coordinate system is specified inside the data file then the coordinate system specified in the xml file is ignored.
The attribute fileExtensions is allowed for file, pointfile, image, transform and geometry. fileExtensions can contain a list of comma separated file extensions for optional use by the GUI.
Transform types: If not specified then any transform is accepted (linear, non-linar, composite, ...). By specifying a type, accepted transforms can be limited to 'linear' = any linear transform; 'non-linear' = grid, displacement field; 'bspline' = b-spline transform.
The attribute reference can mean different things based on the parameter type. If the parameter is a transform and the reference is transformable, then the transform hierarchy of the reference is manipulated such that the reference is under the transform. If the parameter is an image or a model, then the parameter is placed in subject hierarchy at the same level as the reference (note: not available in Slicer3). If the parameter is an image, reference will be used to initialize lookup table of the output image (note: not available in Slicer4).
<name> (required if longflag is not specified)
The name of a command line argument. If name is not specified, longflag will be used (e.g. conductance, numberOfIterations). The name must be usable as a C++ variable. For example, it CANNOT have spaces or special characters and must start with a letter.
for CLP, the name of the C++ variable.
for GUI, used internally.
</name>
<description> (required)
A brief description of the parameter. Any double quotes will be converted to single quotes.
for CLP, describes the parameter for --usage and --help.
for GUI, describes the parameter when the cursor is placed over the widget for the parameter (balloon help).
</description>
<label> (required)
A label for parameter (e.g. Dicom Directory, Conductance).
for CLP, not used.
for GUI, the label for the parameter widget.
</label>
<default>
A default value for the parameter. The default must be a type that is compatible with the parameter type. The vector parameters are specified as comma separated values of the atomic parameter type.
for CLP, contains the default for the parameter unless the parameter is a boolean. The default for boolean parameters is always set to false.
for GUI, contains the default for the parameter.
</default>
<flag [alias="a,b"] [deprecatedalias="c,d"]> (not required if longflag is present)
A single character command line flag (e.g. s, W). Can provide "alias"'s (comma separated) if different flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different flags can be used to set the same parameter but the user should be notified of which "updated" flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.
for CLP, used as the short flag on the command line.
for GUI, used when running the module.
</flag>
<longflag [alias="foo,bar"] [deprecatedalias="garf"]> (not required if flag is present)
A command line flag (e.g. spacing, Watcher). Can provide "alias"'s (comma separated) if different long flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different long flags can be used to set the same parameter but the user should be notified of which "updated" long flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.
Note: apparently you can't use hyphens in the longflag, so things like --my-option are not allows. -gcs
for CLP, used as the long flag on the command line.
for GUI, used when running the module.
</longflag>
<constraints>
Encloses constraints on the value of a non-vector, non-enumerated parameter.
for CLP, not used.
for GUI, if present, a slider will be created using the minimum, maximum and step specified.
<minimum>
The minimum allowed value for the parameter. If not specified, the minimum is the smallest possible value for the parameter type.
</minimum>
<maximum>
The maximum allowed value for the parameter. If not specified, the maximum is the largest possible value for the parameter type.
</maximum>
<step>
The increment for the parameter.
</step>
</constraints>
<channel> (required for file, pointfile, directory and image parameters)
Specifies whether the parameter is an input or output parameter.
for CLP, not used.
for GUI, selects the proper widget for file handling.
</channel>
<index> (required if there are no flags specified)
An integer starting at 0, that specifies a command line argument that has no flags.
Note: if you use index for, say, an image, the user must enter some input value into the GUI. If the user does not fill in a value, the plugin is not run at all. However, slicer will "seem" to run it, and no error message is given. -gcs
for CLP, specifies the order of an argument that has no flags.
for GUI, used when running the module.
</index>
<enumeration> (required for enumeration parameters)
Encloses elements for the parameter. The parameter is restricted one and only one element.
for CLP, not used.
for GUI, defines a radio button with choices.
<element>
Defines the choice. Must be of the proper type for a parameter.
for CLP, not used.
for GUI, used as the label for the radio button.
</element>
</enumeration>
<reference [role="foo"] [parameter="bar"]/>
A forward reference from the parameter to another parameter "bar" with the reference role "foo".
</integer> | </float> | </double> | </boolean> | </string> | </integer-vector> | </float-vector> | </double-vector> | </string-vector> | </integer-enumeration> | </float-enumeration> | </double-enumeration> | </string-enumeration> | </file> | </directory> | </image> | </geometry> | </point> | </pointfile> | </region> | </table> | </transform>
</parameters>
</executable>

Parameter References

There are two types of references between parameters:

  • Attribute of image/geometry/transform parameter [reference="..."]: The specialized attribute reference can mean different things based on the parameter type. If the parameter is a transform and the reference is transformable, then the transform hierarchy of the reference is manipulated such that the reference is under the transform. If the parameter is an image or a model, then the parameter is placed in subject hierarchy at the same level as the reference.
  • Element under a parameter <reference [role="foo"] [parameter="bar"]/>: This a generic reference called "forward reference", point from the parameter to another parameter with name "bar" with the reference role "foo". If both parameters are represented in Slicer as a MRML node, then the node associated to the parameter containing the reference will be added a node reference to the node associated to "bar" with the role "foo". These references can be useful for the application to identify other nodes that are in some way related to the node. For example: 1) get the moving and fixed images/landmarks from a transform that were inputs of the registration step creating it, 2) identify the source image of a processed image to know its provenance, 3) identify another node from which certain properties need to be propagated (e.g. color table)

Slicer GUI Generation

Slicer generates GUI's for each executable discovered during the startup process. Slicer searches directories stored in the Slicer Module Path. This path is set from the Slicer application in View->Application Settings->Modules->Additional module paths. Slicer attempts to run every executable in the prescribed directories and look for a valid XML file in response to a "--xml" command line.

Here are a few representative examples.

A tour of the Execution Model XML

This example is a sampler of the parameters available in the Execution Model.

<?xml version="1.0" encoding="utf-8"?>
<executable>
  <category>Tours</category>
  <title>Execution Model Tour</title>
  <description>
  Shows one of each type of parameter.
  </description>
  <version>1.0</version>
  <documentation-url></documentation-url>
  <license></license>
  <contributor>Daniel Blezek</contributor>

  <parameters>
    <label>Scalar Parameters</label>
    <description>
    Variations on scalar parameters
    </description>
    <integer>
      <name>integerVariable</name>
      <flag>i</flag>
      <longflag>integer</longflag>
      <description>
      An integer without constraints
      </description>
      <label>Integer Parameter</label>
      <default>30</default>
    </integer>
    <label>Scalar Parameters With Constraints</label>
    <description>Variations on scalar parameters</description>
    <double>
      <name>doubleVariable</name>
      <flag>d</flag>
      <longflag>double</longflag>
      <description>An double with constraints</description>
      <label>Double Parameter</label>
      <default>30</default>
      <constraints>
        <minimum>0</minimum>
        <maximum>1.e3</maximum>
        <step>0</step>
      </constraints>
    </double>
  </parameters>

  <parameters>
    <label>Vector Parameters</label>
    <description>Variations on vector parameters</description>
    <float-vector>
      <name>floatVector</name>
      <flag>f</flag>
      <description>A vector of floats</description>
      <label>Float Vector Parameter</label>
      <default>1.3,2,-14</default>
    </float-vector>
    <string-vector>
      <name>stringVector</name>
      <longflag>string_vector</longflag>
      <description>A vector of strings</description>
      <label>String Vector Parameter</label>
      <default>"foo",bar,"foobar"</default>
    </string-vector>
  </parameters>

  <parameters>
    <label>Enumeration Parameters</label>
    <description>Variations on enumeration parameters</description>
    <string-enumeration>
      <name>stringChoice</name>
      <flag>e</flag>
      <longflag>enumeration</longflag>
      <description>An enumeration of strings</description>
      <label>String Enumeration Parameter</label>
      <default>foo</default>
      <element>foo</element>
      <element>"foobar"</element>
      <element>foofoo</element>
    </string-enumeration>
  </parameters>
</executable>

Module with an integer-vector, one input image and one output image

Here is the XML that describes the MedianImageFilter. The image on the right shows the generated Slicer 3 GUI. The help frame has been expanded by the user.

<?xml version="1.0" encoding="utf-8"?>
<executable>
  <category>
  Filtering.Denoising
  </category>
  <title>
  Median Filter
  </title>
  <description>
The MedianImageFilter is commonly used as a robust approach for
noise reduction. This filter is particularly efficient against
"salt-and-pepper" noise. In other words, it is robust to the presence
of gray-level outliers. MedianImageFilter computes the value of each output
pixel as the statistical median of the neighborhood of values around the
corresponding input pixel.
  </description>
  <version>0.1.0.$Revision: 2085 $(alpha)</version>
  <documentation-url></documentation-url>
  <license></license>
  <contributor>Bill Lorensen</contributor>
  <acknowledgements>This command module was derived from
Insight/Examples/Filtering/MedianImageFilter (copyright) Insight Software Consortium
  </acknowledgements>
  <parameters>
    <label>Median Filter Parameters</label>
    <description>Parameters for the median filter</description>

    <integer-vector>
      <name>neighborhood</name>
      <longflag>--neighborhood</longflag>
      <description>The size of the neighborhood in each dimension</description>
      <label>Neighborhood Size</label>
      <default>1,1,1</default>
    </integer-vector>

  </parameters>

  <parameters>
    <label>IO</label>
    <description>Input/output parameters</description>
    <image>
      <name>inputVolume</name>
      <label>Input Volume</label>
      <channel>input</channel>
      <index>0</index>
      <description>Input volume to be filtered</description>
    </image>
    <image>
      <name>outputVolume</name>
      <label>Output Volume</label>
      <channel>output</channel>
      <index>1</index>
      <description>Output filtered</description>
      <reference role="source" parameter="inputVolume"/>
    </image>
  </parameters>

</executable>

Module with a multiple scalars, one Input image and one output image

A module with

<?xml version="1.0" encoding="utf-8"?>
<executable>
  <category>filtering</category>
  <title>Anisotropic Diffusion</title>
  <description>
  Runs anisotropic diffusion on a volume
  </description>
  <version>1.0</version>
  <documentation-url></documentation-url>
  <license></license>
  <contributor>Bill Lorensen</contributor>

  <parameters>
    <label>
    Anisotropic Diffusion Parameters
    </label>
    <description>
    Parameters for the anisotropic
    diffusion algorithm
    </description>

    <double>
      <name>conductance</name>
      <longflag>conductance</longflag>
      <description>Conductance</description>
      <label>Conductance</label>
      <default>1</default>
      <constraints>
        <minimum>0</minimum>
        <maximum>10</maximum>
        <step>.01</step>
      </constraints>
    </double>

    <double>
      <name>timeStep</name>
      <longflag>timeStep</longflag>
      <description>Time Step</description>
      <label>Time Step</label>
      <default>0.0625</default>
      <constraints>
        <minimum>.001</minimum>
        <maximum>1</maximum>
        <step>.001</step>
      </constraints>
    </double>

    <integer>
      <name>numberOfIterations</name>
      <longflag>iterations</longflag>
      <description>Number of iterations</description>
      <label>Iterations</label>
      <default>1</default>
      <constraints>
        <minimum>1</minimum>
        <maximum>30</maximum>
        <step>1</step>
      </constraints>
    </integer>

  </parameters>

  <parameters>
    <label>IO</label>
    <description>Input/output parameters</description>
    <image>
      <name>inputVolume</name>
      <label>Input Volume</label>
      <channel>input</channel>
      <index>0</index>
      <description>Input volume to be filtered</description>
    </image>
    <image>
      <name>outputVolume</name>
      <label>Output Volume</label>
      <channel>output</channel>
      <index>1</index>
      <description>Output filtered</description>
    </image>
  </parameters>

</executable>


Module with output text presented in GUI

Slicer auto generates GUI to display output from the command line module after execution is complete. The flag [--returnparameterfile <file name>] is used to provide a name of a file that the module uses to store key=value pairs that are displayed in the Slicer GUI. The keys are described in the command line module xml schema using the output channel.

This example demonstrates a portion of an XML schema containing 1 string-enumeration input and 7 string outputs.

<parameters>
  <label>Radnostics Beta Osteoporosis Analysis</label>
  <string-enumeration>
    <name>Osteoporosis</name>
    <longflag>--osteoporosis</longflag>
    <label>After Segmentation Perform Osteoporosis Analysis</label>
    <description>Osteoporosis Analysis will provide a Radnostics Osteoporosis score for the patient.</description>
    <default>yes</default>
    <element>yes</element>
    <element>no</element>
  </string-enumeration>
  <string>
    <name>PatientGender</name>
    <label>Gender</label>
    <channel>output</channel>
    <description>Patient Gender</description>		  
    <default>na</default>
  </string>		
  <string>
    <name>PatientAge</name>
    <label>Age</label>
    <channel>output</channel>
    <description>Patient Age</description>		  
    <default>na</default>
  </string>		
  <string>
    <name>RadnosticsOsteoporosisScore</name>
    <label>Radnostics Osteoporosis Score</label>
    <channel>output</channel>
    <description>Radnostics Osteoporosis Score</description>		  
    <default>na</default>
  </string>		
  <string>
    <name>OsteoporosisComment1</name>
    <channel>output</channel>
    <label>*</label>
    <description>Comment 1</description>
  </string>
  <string>
    <name>OsteoporosisComment2</name>
    <channel>output</channel>
    <label>*</label>
    <description>Comment 2</description>
  </string>
  <string>
    <name>OsteoporosisComment3</name>
    <channel>output</channel>
    <label>*</label>
    <description>Comment 3</description>
  </string>
  <string>
    <name>OsteoporosisComment4</name>
    <channel>output</channel>
    <label>*</label>
    <description>Comment 4</description>
  </string>
</parameters>
The command line module outputs key=value pairs to the file supplied by –-returnparameterfile as shown below:
   PatientGender = F 
   PatientAge = 059Y
   RadnosticsOsteoporosisScore = 8 (high)
   OsteoporosisComment1 = vertebra label 58 has biconcave fracture
   OsteoporosisComment2 = vertebra label 56 has mean density of 120 HU


Command Line Parsing

The Slicer Execution Model has support for parsing executable command lines. The C++ code to parse the command line arguments is generated automatically from the same XML description that generates the GUI. GenerateCLP, located in SlicerExecutionModel/GenerateCLP reads the XML Module Description and creates an include file "Executable"CLP.h in the build tree. The executable includes this header file and accesses the code with the macro PARSE_ARGS.

GenerateCLP provides the following to the executable:

  1. A brief usage command if required arguments are missing
  2. A full help command if -h or --help is specified on the command line
  3. A copy of the xml description if --xml is specified on the command line
  4. An echo of the command line parameters and their values if --echo is specified

GenerateCLP provides the following source code:

  1. A C++ declaration of the proper type for each parameter assigning the default value if specified by the XML
  2. For -vector parameters, a std::vector containing the proper C++ type of the parameter. The generated code parses the comma separated strings to generate the std::vector

Using GenerateCLP

GenerateCLP is normally used via CMake where it is implemented as a CUSTOM_COMMAND. To use GenerateCLP from CMake, you must first include the GenerateCLP package from your CMakeLists.txt file, which will make some macros available to you:

 find_package(SlicerExecutionModel REQUIRED)
 include(${SlicerExecutionModel_USE_FILE})

Note that the HelloSlicer command line module example provides a good starting point and can be used as a skeleton to build your own. It can be found in the Modules/CommandLineModule/Testing/HelloSlicer subdirectory.

For each executable, include the following, replacing MyFilter with the name of your C++ source:

 set(MyFilter_SOURCE MyFilter.cxx)
 GENERATECLP(MyFilter_SOURCE MyFilter.xml)

To generate a stand-alone executable add the lines:

 add_executable(MyFilter ${MyFilter_SOURCE})
 target_link_libraries(MyFilter ${ITK_LIBRARIES})

Slicer expects modules and plugins to be stored in a specific subdirectory, so that they can be discovered and loaded at run-time. To make sure your CLP module is built in said subdirectory, add the line:

 SEMMacroBuildCLI(
   NAME MyFilter
   LOGO_HEADER ${Slicer_SOURCE_DIR}/Resources/NAMICLogo.h
   ADDITIONAL_SRCS
     MyFilter.cxx
   TARGET_LIBRARIES ModuleDescriptionParser ${ITK_LIBRARIES} vtkTeem MRMLCore SlicerBaseCLI ${VTK_LIBRARIES}
   INCLUDE_DIRECTORIES
     ${vtkTeem_INCLUDE_DIRS}
     ${MRMLCore_INCLUDE_DIRS}
     ${vtkITK_INCLUDE_DIRS}
     ${SlicerBaseCLI_SOURCE_DIR} ${SlicerBaseCLI_BINARY_DIR}
   )

The add_executable target creates a stand-alone executable that can be run from a command line. The add_library target creates a shared library that is discovered at Slicer 3 startup.

Although this example linked to ITK libraries, other libraries can be specified.

Short Example

This example uses the XML for the Median|Image Filter example.
Note: The program MUST NOT write anything to stdout before the PARSE_ARGS statement. If something is written, the plugin discovery mechanism will not recognize the program as a plugin.

#include "MedianImageFilterCLP.h"
    int main (int argc, char * argv[])
     {
     PARSE_ARGS;
     std::cout << "The size of the neighborhood is: " << neighborhood.size()
       << " and the first element of the neighborhood is: " << neighborhood[0]
       << std::endl;
     std::cout << "The input volume is: " << inputVolume << std::endl;
     std::cout << "The output volume is: " << outputVolume << std::endl;
     return EXIT_SUCCESS;
     }
    

Here is the output --help:


USAGE: 

   ./MedianImageFilter  [--processinformationaddress <std::string>] [--xml]
                        [--echo] [--neighborhood <std::vector<int>>] [--]
                        [--version] [-h] <std::string> <std::string>


Where: 

   --processinformationaddress <std::string>
     Address of a structure to store process information (progress, abort,
     etc.). (default: 0)

   --xml
     Produce xml description of command line arguments (default: 0)

   --echo
     Echo the command line arguments (default: 0)

   --neighborhood <std::vector<int>>
     The size of the neighborhood in each dimension (default: 1,1,1)

   --,  --ignore_rest
     Ignores the rest of the labeled arguments following this flag.

   --version
     Displays version information and exits.

   -h,  --help
     Displays usage information and exits.

   <std::string>
     (required)  Input volume to be filtered

   <std::string>
     (required)  Output filtered


   The MedianImageFilter is commonly used as a robust approach for noise
   reduction. This filter is particularly efficient against
   'salt-and-pepper' noise. In other words, it is robust to the presence of
   gray-level outliers. MedianImageFilter computes the value of each output
   pixel as the statistical median of the neighborhood of values around the
   corresponding input pixel.

   Author(s): Bill Lorensen

   Acknowledgements: This command module was derived from
   Insight/Examples/Filtering/MedianImageFilter (copyright) Insight
   Software Consortium


Parameters and C++ code

This table shows how parameters are defined in the C++ code and how they are specified on the command line.

XML C++ Declaration Command Line

<integer>
<name>count</name>
<flag>c</flag> </integer>

int count;

prog -c 10

<float>
<name>stepSize</name>
<default>.0625</default>
<longflag>stepSize</longflag>
</float>

float stepSize=.0625;

prog --stepSize .003

<integer multiple="true">
<name>iterations</name>
<flag>i</flag>
<default>100</default>
</integer>

std::vector<int> iterations;
iterations.push_back(100);

prog -i 20 -i 30 -i 100

<float-vector>
<name>variation</name>
<flag>v</flag>
<default>1,2,3</default>
</float-vector>

std::vector<float> variation; iterations.push_back(1);
iterations.push_back(2);
iterations.push_back(3);

prog -v 10,20,3

<string-vector>
<name>sites</name>
<longflag>sites</longflag> </string-vector>

std::vector<std::string> sites;

prog --sites BWH,GE,Kitware,UNC,MIT,UTAH,GT

<string-enumeration>
<name>leaders</name>
<default>Bill</default>
<element>Ron</element>
<element>Bill</element>
<element>Steve</element>
</string-enumeration>

std::string leaders = "Bill";

prog --leaders Ron

<boolean>
<name>debugSwitch</name>
<flag>d</flag> <default>true</default>
</boolean>

bool debugSwitch = true;

prog -d

<file>
<longflag>file1</longflag>
<file>

std::string file1;

prog --file1 mytext.txt

<image>
<name>image</name>
<index>0</index>
</image>

std::string image;

prog c:/lorensen/Data/ct.nrrd

<file multiple="true">
<name>args</name>
<index>1</index>
</file>

std::vector<std::string> args;

prog --otherFlags file1 file2 ... filen

<point multiple="true" coordinateSystem="ras">
<name>seed</name>
<longflag>--seed</longflag>

std::vector<std::vector<float> > seed;

prog --seed 10,100,23 --seed 5,240,17

Accessing Module Information at Runtime

All of the information contained in the XML description of a module can be accessed at run-time by the command line program. The ModuleDescriptionParser class library can parse an XML module description and populate a ModuleDescription instance.

// Module Description Parser Class Library
#include "ModuleDescriptionParser.h"
#include "ModuleDescription.h"
#include "ModuleParameterGroup.h"
#include "ModuleParameter.h"
.
.
.
// Create a module and a parser
    ModuleDescription module;
    ModuleDescriptionParser parser;
// Parse the XML
    if (parser.Parse(GetXMLModuleDescription(), module))
      {
      std::cerr << argv[0] << ": One or more XML errors detected." << std::endl;
      return EXIT_FAILURE;
      }
// Access the module description information
    std::cout << "Module Description Information" << std::endl;
    std::cout << "\tCategory is: " << module.GetCategory() << std::endl;
    std::cout << "\tTitle is: " << module.GetTitle() << std::endl;
    std::cout << "\tDescription is: " << module.GetDescription() << std::endl;
    std::cout << "\tVersion is: " << module.GetVersion() << std::endl;
    std::cout << "\tDocumentationURL is: " << module.GetDocumentationURL() << std::endl;
    std::cout << "\tLicense is: " << module.GetLicense() << std::endl;
    std::cout << "\tContributor is: " << module.GetContributor() << std::endl;

GetXMLModuleDescription is automatically generated by GenerateCLP. Information about parameter groups and parameters is also available here.

The CMakeLists.txt file that creates the command line module should point to the ModuleDescriptionParser library.

target_link_libraries(${CLP}
    ModuleDescriptionParser
)

Error Handling

GenerateCLP attempts to do error checking so that the generated C++ code will compile. These errors will show up as custom command errors during the build process.

  • XML Errors
    • mismatched tag at line xx : The closing tag (a tag with </ >) does not have a matching opening tag.
    • not well-formed (invalid token) at line xx : Probably a blank in the token name.
  • ModuleDescriptionParser Errors
    • <executable> must be the outer most tag
    • <executable> was found inside another tag
    • <parameters> can only be inside <executable>
    • <xxx> can only be used inside <parameters>
    • <flag> can only contain one character
    • <longname> can only contain letters, numbers and underscores and must start with an _ or letter
    • <name> can only contain letters, numbers and underscores and must start with an _ or letter
  • ModuleDescriptionParser Warnings
    • <xxx> is an unknown parameter tag : Probably a misspelled parameter type.
  • Compiler Errors
    • The generated C++ code may have syntax errors if invalid defaults are specified. These will show up during the C++ compilation.

Interfacing Legacy Executables

GenerateCLP is only provided as a convenience. Users can use the same XML Module Description to interface C++, shell scripts, tcl programs and even Matlab.

Example: FiberTracking Integration

Showing Progress in an Application

Programs can communicate progress to the user in two ways. If the program is running an a stand-alone executable, it communicates with a simple XML syntax. If the program is loaded at run-time as a plugin library, it communicates through a C structure.

The XML syntax is:

<filter-start>
 <filter-name>
 name of program section or algorithm
 </filter-name>
 <filter-comment>
 description of program section or algorithm
 </filter-comment>
</filter-start>
<filter-progress>
floating number from 0 to 1
</filter-progress>
<filter-end>
 <filter-name>
 name of program section or algorithm
 </filter-name>
 <filter-time>
 execution time
 </filter-time>
</filter-end>

The C structure that library plugins use is:

extern "C" {
 struct ModuleProcessInformation
 {
   /** Inputs from calling application to the module **/
   unsigned char Abort;
   /** Outputs from the module to the calling application **/
   float Progress;
   char  ProgressMessage[1024];
   void (*ProgressCallbackFunction)(void *);
   void *ProgressCallbackClientData;
   double ElapsedTime;
 }
}

Details on how to use this mechanism are illustrated in itkPluginFilterWatcher.h.

For vtk and itk execution model programs, two classes are available that make it simple to add progress. The classes, vtkPluginFilterWatcher and itk::PluginFilterWatcher use the vtk and itk command/observer mechanism to report progress.

vtkPluginFilterWatcher (vtkAlgorithm *filter, const char* comment, ModuleProcessInformation *inf, double fraction, double start)
itk::PluginFilterWatcher (itk::ProcessObject filter, const char* comment, ModuleProcessInformation *inf, double fraction, double start)

where:
filter
is the vtkAlgorithm or itk::ProcessObject to be watched.
comment
is a string that describes the algorithm.
inf
is an optional pointer to a structure that is used to communicate with the invoking program when the called program is used as a library plugin. If the pointer is 0, this prgram will not report progress if it is run as a library plugin. Default is 0.
fraction
is the fraction (0-1) of progress that will be reported by this watcher. This is used when multiple filters are run and each filter represents a proportion of the total progress. Default is 1.
start
is the offset (0-1) of the progress for this filter. This is added to the progress of the filter. The reported progress of the watched filter is start + fraction * filter_progress.


The following example produces progress for a simple vtk program. The variable CLPProcessInformation is automatically declared and set in the program's programCLP.h file.

#include "vtkPluginFilterWatcher.h"
    ...
     vtkMarchingCubes *cubes = vtkMarchingCubes::New();
       cubes->SetInput(reader->GetOutput());
     vtkPluginFilterWatcher watchCubes(cubes, "Generate Isosurface", CLPProcessInformation, .5, 0.0);
     vtkDecimatePro *decimate = vtkDecimatePro::New();
       decimate->SetInput(cubes->GetOutput());
     vtkPluginFilterWatcher watchDecimate(decimate, "Reduce Triangle Count", CLPProcessInformation, .5, 0.5);
     decimate->Update();
    

The following example produces progress for a simple itk program:

#include "itkPluginFilterWatcher.h
    ...
    typedef itk::MedianImageFilter<ImageType,ImageType> FilterType;
    FilterType::Pointer median  = FilterType::New();
    itk::PluginFilterWatcher watchMedian(median, "Denoise Image", CLPProcessInformation);
    

Adding Module Logos to Slicer

Slicer plugins, both libraries and executables, can specify plugin-specific logos. These appear in Slicer when a module is selected. The logos are specified in a .h header file. The KWWidget utility, KWConvertImageToHeader, converts a .png file into a .h header file containing the encoded image and additional information such as width, height and pixel size. See here for additional information.

For Slicer, execution model plugin logos are stored in Modules/CLI/Resources. The corresponding image in .png format should be stored in Modules/CLI/ImageData. Othere plugins, created outside the Slicer tree, should store the logo and image in a similar location.

To add a logo to a plugin:

  • Create a png image of the logo. The height of the logo should not exceed 40 pixels.
  • Convert the logo to the KWWidget icon format as follows. NOTE: the prefix of the image and header file must be the same for a plugin logo.
cd Modules/CLI
KWConvertImageToHeader --base64 --zlib Resources/ITKLogo.h ImageData/ITKLogo.png
  • Add the logo to the SEMMacroBuildCLImacro in the CMakeLists.txt file for the plugin using LOGO_HEADER parameter.

Runtime specification of filter types

ITK filters are templated over the images they process. The following code snippet shows how an execution model program can select the image types for filters based on the input images.

First, include the utilites for plugin's:

#include "itkPluginUtilities.h"

Then, turn your main program into a templated procedure called DoIt:

template<class T> int DoIt( int argc, char * argv[], T )
{
  PARSE_ARGS;

  typedef itk::Image< T, 3 >   InputImageType;
  typedef itk::Image< T, 3 >   OutputImageType;
.
.
.
}

Then, create a main program that gets the native component type from one of the input file. Here that input file is inputVolume:

int main( int argc, char * argv[] )
{
  
  PARSE_ARGS;

  itk::ImageIOBase::IOPixelType pixelType;
  itk::ImageIOBase::IOComponentType componentType;

  try
    {
    itk::GetImageType (inputVolume, pixelType, componentType);

    // This filter handles all types
    
    switch (componentType)
      {
      case itk::ImageIOBase::UCHAR:
        return DoIt( argc, argv, static_cast<unsigned char>(0));
        break;
      case itk::ImageIOBase::CHAR:
        return DoIt( argc, argv, static_cast<char>(0));
        break;
      case itk::ImageIOBase::USHORT:
        return DoIt( argc, argv, static_cast<unsigned short>(0));
        break;
      case itk::ImageIOBase::SHORT:
        return DoIt( argc, argv, static_cast<short>(0));
        break;
      case itk::ImageIOBase::UINT:
        return DoIt( argc, argv, static_cast<unsigned int>(0));
        break;
      case itk::ImageIOBase::INT:
        return DoIt( argc, argv, static_cast<int>(0));
        break;
      case itk::ImageIOBase::ULONG:
        return DoIt( argc, argv, static_cast<unsigned long>(0));
        break;
      case itk::ImageIOBase::LONG:
        return DoIt( argc, argv, static_cast<long>(0));
        break;
      case itk::ImageIOBase::FLOAT:
        return DoIt( argc, argv, static_cast<float>(0));
        break;
      case itk::ImageIOBase::DOUBLE:
        return DoIt( argc, argv, static_cast<double>(0));
        break;
      case itk::ImageIOBase::UNKNOWNCOMPONENTTYPE:
      default:
        std::cout << "unknown component type" << std::endl;
        break;
      }
    }
  catch( itk::ExceptionObject &excep)
    {
    std::cerr << argv[0] << ": exception caught !" << std::endl;
    std::cerr << excep << std::endl;
    return EXIT_FAILURE;
    }
  return EXIT_SUCCESS;
}

Behind the Scenes

A primary goal of the execution model is to relieve developers from developing GUI code and command line parsing code. This section describes the major components of the execution model implementation.

Command Line Processing

Command line processing parses command line arguments and populates internal program variables. Every Unix (and windows) program can receive an argument list through its main entry point. All C and C++ programmers are familiar with the int main (int argc, char *[] argv) entry point in their programs. Most computer languages including scripting languages provide a similar mechanism to retrieve command line arguments. Simple command line processing directly accesses the strings defined in argv.

This snippet shows simple commmand line processing:

int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    std::cout << "Usage: " << argv[0] << " filename" << std::endl;
    return -1;
    }
  std::cout << "The File is " << argv[1] << std::endl;
  return 0;
}

The simple approach works great for a small number of arguments. But larger numbers of arguments of varying types quickly make the processing code more complex and subject to error, both in coding and usage.

int main (int argc, char *argv[])
{
  if (argc < 5)
    {
    std::cout << "Usage: " << argv[0] << " iterations epsilon inputfile outputfile " << std::endl;
    return -1;
    }
  std::string inputfile(argv[3]);
  std::string outputfile(argv[4]);
  unsigned int iterations = atoi(argv[1]);
  float epsilon = atof(argv[2]);
...
  return 0;
}

Adding flags (or options) to the command line makes the program easier to use but places a larger burden on the program developer. Each developer must invent a command line argument syntax and implement code to parse the command line. Even a simple example of this is too long to include in this description. This code snippet looks for just two command line arguments.

int main (int argc, char *argv[])
{
  if (argc < 3)
    {
    std::cout << "Usage: " << argv[0] << " [-i iterations] [-e epsilon] inputfile outputfile " << std::endl;
    return -1;
    }
  std::string inputfile;
  std::string outputfile;
  unsigned int iterations = 10; /* a default */
  float epsilon = .001; /* a defualt */
  ++argc; /* skip program name */
  while (argc > 0)
    {
    if (strcmp(argv[argc], "-i")
     {
     iterations = atoi(argv[argc+1]);
     argc+=2;
     continue;

   else if (strcmp(argv[argc], "-e")
     {
     epsilon = atof(argv[argc+1]);
     argc+=2;
     continue;
   ...
    }

The code gets longer and longer as more options are added and must be rewritten every time a new programs is open.

To solve this complexity issue, people have developed command line argument libraries. There are dozens, if not hundreds, of command line processing tools. For Slicer3 we looked at argument processors in vxl, nrrd, meta, kwsys and tclap. Each has its strengths and weaknesses. We chose The Templatized C++ Command Line Parser Library, TCLAP. TCLAP is implemented in include files and does not require a separate library build. As you will see later, the particular command line processing tool is, for the most part, transparent to the Slicer3 developer or user.

But even these libraries require some work to use.

TCLAP

This example uses TCLAP to process a command line with 10 possible entries:

int main ( int argc, char* argv[] ) {
 //
 // Define default values
 int HistogramBins      = 30;
 int RandomSeed         = 1234567;
 int SpatialSamples     = 10000;
 float TranslationScale = 100.0;
 int Iterations         = 200;
 int SplineOrder        = 3;
 double MinimumStepSize = 0.00001;
 double MaximumStepSize = 0.005;
 bool PrintTransform    = false;
 string fixedImageFileName;
 string movingImageFileName;
 string resampledImageFileName;
 //
 // Setup command line parsing
 try
   {
   TCLAP::CmdLine cl ( "Register2d", ' ', "$Revision: 1.1 $" );
   TCLAP::ValueArg<int>    HistogramBinsArg    ( "b", "histogrambins",    "Number of histogram bins", false, 30, "integer", cl );
   TCLAP::ValueArg<int>    IterationsArg       ( "i", "iterations",       "Number of Iterations", false, Iterations, "int", cl );
   TCLAP::ValueArg<double> MinimumStepSizeArg  ( "m", "minstepsize",      "Minimum Step Size", false, MinimumStepSize, "double", cl );
   TCLAP::ValueArg<double> MaximumStepSizeArg  ( "x", "maxstepsize",      "Maximum Step Size", false, MaximumStepSize, "double", cl );
   TCLAP::ValueArg<int>    RandomSeedArg       ( "r", "randomseed",       "Random Seed", false, RandomSeed, "int", cl );
   TCLAP::ValueArg<int>    SpatialSamplesArg   ( "s", "spatialsamples",   "Number of spatial samples", false, SpatialSamples, "int", cl );
   TCLAP::ValueArg<int>    SplineOrderArg      ( "o", "splineorder",      "Order of spline for registration", false, SplineOrder, "int", cl );
   TCLAP::SwitchArg        PrintTransformArg   ( "p", "printtransform",   "Print the final transform", PrintTransform, cl );
   TCLAP::ValueArg<float>  TranslationScaleArg ( "t", "translationscale", "Translation scale", false, TranslationScale, "float", cl );
   TCLAP::UnlabeledValueArg<string> FixedImageArg ( "fixed", "Fixed image filename", "", "string", cl );
   TCLAP::UnlabeledValueArg<string> MovingImageArg ( "moving", "Moving image filename", "", "string", cl );
   TCLAP::UnlabeledValueArg<string> ResampledImageArg ( "resampled", "Resampled image filename", "", "string", cl );
   //
   // Parse the command line
   cl.parse ( argc, argv );
   //
   // Access the variables
   HistogramBins          = HistogramBinsArg.getValue();
   Iterations             = IterationsArg.getValue();
   MinimumStepSize        = MinimumStepSizeArg.getValue();
   MaximumStepSize        = MaximumStepSizeArg.getValue();
   RandomSeed             = RandomSeedArg.getValue();
   SpatialSamples         = SpatialSamplesArg.getValue();
   TranslationScale       = TranslationScaleArg.getValue();
   PrintTransform         = PrintTransformArg.getValue();
   fixedImageFileName     = FixedImageArg.getValue();
   movingImageFileName    = MovingImageArg.getValue();
   resampledImageFileName = ResampledImageArg.getValue();
   }
 catch ( ArgException e )
   {
   cerr << "error: " << e.error() << " for arg " << e.argId() << endl;
   exit ( EXIT_FAILURE );
   }

You do get a lot for your investment here. Good error handling and help.

Module Description Parser

The XML parsing is done by the ModuleDescriptionParser class library. GenerateCLP and Slicer3 use this class library to parse the module XML descriptions. The class ModuleDescriptionParser has one method, Parse, that converts the XML description into an object model. The resulting object model has one ModuleDescription, one or more ModuleParameterGroup each of which has one or more ModuleParameter. Each instance has access methods to retrieve information from the XML.

  • ModuleDescriptionParser - parser for command line module XML description.
    Parse(std::string xml, ModuleDescription module) - parse an xml string and populate a ModuleDescription.
  • ModuleDescription - contains information about a module
    const std::string GetCategory() : returns the contents of <category>.
    const std::string GetTitle() : returns the contents of <title>.
    const std::string GetDescription() : returns the contents of <description>.
    const std::string GetVersion() : returns the contents of <version>.
    const std::string GetDocumentationURL() : returns the contents of <documentationURL>.
    const std::string GetLicense() : returns the contents of <license>.
    const std::string GetContributor() : returns the contents of <contributor>.
    const std::vector<ModuleParameterGroup>& GetParameterGroups() : returns a vector of parameter groups.
  • ModuleParameterGroup - contains ModuleParameters for each parameter group.
    const std::string GetLabel - returns the contents of <label>.
    const std::string GetDescription() - returns the contents of the parameter group's <description>.
    const std::string GetAdvanced() - returns advanced attribute. Either "true" of "false".
  • ModuleParameter - contains information for a parameter.
    GetTag() - returns the parameter's tag, e.g. <integer>, <image>, etc.
    GetName() - returns the parameter's <name>.
    GetLongFlag() - returns the parameter's <longflag>.
    GetLabel() - returns the parameter's <label>.
    GetMaximum() - returns the parameter's <maximum> constraint.
    GetMinimum() - returns the parameter's <minimum> constraint.
    GetStep() - returns the parameter's <step>.
    GetDescription() - returns the parameter's <description>.
    GetChannel() - returns the parameter's <channel>.
    GetIndex() - returns the parameter's <index>.
    GetDefault() - returns the parameter's <default>.
    GetFlag() - returns the parameter's <flag>.
    GetMultiple() - returns the parameter's multiple attribute, either "true" or "false".
    GetCoordinateSystem() - returns the parameter's coordinate system attribute, one of "lps", "ras".

Adding a new parameter type

Adding a new parameter type to the execution model involves several modifications:

  1. A new XML tag needs to be defined to represent the new parameter type.
  2. ModuleDescriptionParser needs to be modified to parse the new parameter tag type and specify how the command line processing code generation is going to represent the parameter type
  3. CommandLineModuleGUI needs to be modified to construct the appropriate GUI element for the parameter type
  4. CommandLineModuleLogic needs to be modified to put the parameter type onto the command line and request outputs parameter types be loaded back into Slicer and the MRML tree.
  5. SlicerApplicationLogic needs to be modified to ingest any output parameter types back into Slicer and the MRML tree.
  6. Additional modification are needed if the parameter is to be passed directly from the MRML tree without using the filesystem.
  7. Updating the documentation.

Simple parameter types can be passed directly on the command line. For instance, scalars, small lists, positions, etc. Complicated parameter types are passed to the module via abstract files. In some cases, these parameters are actually passed as files, where Slicer reads/write the data to the filesystem. In other cases, the parameters are passed as abstract files which are really references to within the Slicer memory model but appear to the Command Line Module as being a file. The Command Line Module is written using standard ITK ImageFileReader/ImageFileWriter classes but the ITK ImageIO factory mechanism is used to direct the ImageFileReader/ImageFileWriter to talk directly to the Slicer MRML tree instead of to the filesystem.

XML tag

This is by far the easiest of the tasks involved in adding a new parameter type but it should not approached hastily. The XML description of a module is designed to be application agnostic. As such, parameter types should be described abstractly or generically. For instance, <geometry> tag corresponds to the Slicer model node, the <point> tag corresponds to the Slicer fiducial node, etc.

Once the tag name is defined, you need to decide whether the parameter type could or should support the attributes multiple, coordinateSystem, or fileExtensions or perhaps a new attribute type.

Modifying ModuleDescriptionParser

SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx contains the code to parse the XML description of a module and represent that module description in C++ data structures. To add a new parameter type, this code needs to be modified.

Two routines need to be modified in ModuleDescriptionParser, startElement() and endElement(). For startElement(), a new case block needs to be added for the parameter type. You can start by copying the case block for a similar parameter type. This case block is responsible for processing all the attributes for the tag and managing and reporting any errors. The case block may define the CPPType', the ArgType, and the StringToType needed for the code generation of the command line parsing. The CPPType is used by in the generated command line processing code to represent the parameter. This may be a simple C++ type or an STL container. The ArgType is the canonical type of each component of the parameter. The StringToType is the name of the method to use to convert the ASCII command line parameter to the final ArgType. The endElement() method merely needs a new case block to add the parameter to the description.

ModuleDescriptionParser is fairly general, handling scalars, lists of scalars, and file types as parameter types. A parameter which does not fit these models will require considerable alterations to the ModuleDescriptionParser as well as the GenerateCLP.

Constructing a GUI for a new parameter type

To have a GUI element appear in the module GUI for a new parameter type, Base\QTCLI\qSlicerCLIModuleUIHelper.cxx and Base\QTCLI\vtkSlicerCLIModuleLogic.cxx have to be modified. A new case block needs to be added to the BuildGUI() method for the new parameter type. This case block is triggered off the XML tag for the parameter type. The case block is responsible for the constructing the appropriate GUI element for the parameter, complete with specifying the label and help text. The design is very simple. A single widget is used for each parameter. If a more complicated GUI is needed with multiple widgets, then perhaps a new widget is needed to encapsulated a set of widgets or a naming convention can be added to manage all the widgets associated with a parameter. The widgets for the parameters are stored in a map, indexed by the name of the parameter.

The vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) are designed to operate very generically with sets of parameters. The aforementioned map of widgets indexed by parameter name is one such example. The implementation of several of the methods in the vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) generically iterate over the widget map or over the parameter list. It is important to keep this in mind as new parameters are added. The design goal is to minimize the number of special cases in the code.

Note that there may be separate case blocks for input and output parameter types.

Communicating the new parameter to the Command Line Module

To communicate the new parameter type to and from a Command Line Module, the ApplyTask() method Slicer3/Modules/CommandLineModule/vtkCommandLineModuleLogic.cxx needs to be modified.

If the parameter type is communicated to the command line module as a file (as opposed to directly on the command line as a number or srting), then there are several blocks of code to construct a temporary file name, keep track of whether that node needs to be written to the filesystem before execution, read from the filesystem after the execution, and deleted after execution completes. These blocks may need to be modified based on your new parameter type.

The command line is constructed in two passes. First, a pass is made over the parameter list, building the portion of the command line for any parameters that have flags. Note that whether a parameter has a flag or not is up to the discretion of the module author and is not defined by the parameter type. Second, a pass is made to construction the portion of the command line for the parameters that do not have flags. These parameters are ordered appropriately, then placed on the command line. For parameters with flags, this code emits the flag and the parameter value. For the parameters without flags, this code emits just the parameter value. You will need to edit both of these passes to emit your parameter type. In most cases, this is simply a matter of grabbing the parameter value from the parameter and pushing it onto the command line. But some parameter types do require translation to a string appropriate for the command line.

Output parameters from the Command Line Modules

Any outputs from a Command Line Module that are communicated via files are queued to be read by the main application thread. Command Line Modules run in a separate execution thread from the main GUI. This thread is not allowed to modify the Slicer GUI, so any results from the module that need to be read back into Slicer and displayed are queued for the main thread.

The ProcessReadData() method of Slicer3/Base/Logic/vtkSlicerApplicationLogic.cxx pulls data from the queue to load back into Slicer and display. You may need to a case block for your new parameter type to construct the appropriate storage node and display node.

Communicating directly with the MRML tree

Currently scalar image types can sent as parameters to shared object command line modules without going through the filesystem. Slicer provides a new ITK ImageIO factory that can communicated directly with the Slicer MRML tree. This ImageIO factory is in Slicer/Libs/MRMLIDImageIO. This approach can be extended for other image types such as vector or tensor volumes.

For other constructs such as models and transforms, we'll need to see if an existing factory mechanism can be leverage to communicate directly with the Slicer MRML tree. An alternative may be to construct bridges specific to interfacing from a command line module to the Slicer MRML tree.

Adding new image types

The Command Line Module support scalar, vector, tensor, and diffusion weighted images. Adding a new image type to the Command Line Module requires modify the sections of the code outlined above to manage GUI for the module, to construct temporary file names, to write image to disk, and load them back into the MRML tree. These blocks are easy to identify as case blocks on vtkMRMLScalarVolumeNode, vtkMRMLDiffusionTensorVolumeNode, etc. Note that the <image> tag supports a type attribute that can scalar, label, vector, tensor, or diffusion-weighted. The case block for image in the startElement() method of ModuleDescriptionParser would need to be extended to recognize a new type of image.


Developer FAQ: SlicerExecutionModel

What facilities are available for third party software to interface with the NA-MIC Kit?

NA-MIC has committed to developing and supporting a generic batch processing interface. This is the Slicer3 Command Line Interface (CLI). The application programming interface (API) for CLI was worked out based on interactions with the community including input from those familiar with LONI, ITK, Python and Matlab software development practices.

We believe this API already supports all the features required by to implement batch processing with BatchMake, the LONI Pipeline, and any other systems that we may want to work with in the future. The Slicer3 Execution Model provides a simple XML-based descriptor for command line arguments for input/output volumes and related parameters.

Which Third Party software has currently been interfaced to the NA-MIC Kit using the CLI?

  • Batchmake (Kitware)
  • Python
  • Matlab

How do I debug my command line module?

See Debugging

How do I specify a Logo?

You need to create a logo.h file (embedding the zipped pixel data as base64 strings in a C source code). There is a utility bundled with kwwidgets that accomplishes this. From a Slicer3 build directory you can do something like this:

./Slicer3 --launch ../Slicer3-lib/KWWidgets-build/bin/KWConvertImageToHeader --base64 --zlib ~/Desktop/bioimagesuite_logo_www.h ~/Desktop/bioimagesuite_logo_www.png

The resulting .h header file can then be used as an argument to the 'generateclp' cmake macro like this:

generateclp(${CLP}_SOURCE ${CLP}.xml ${Slicer3_SOURCE_DIR}/Resources/bioimagesuite_logo_www.h)

Note: logos are limited in size: if your .h file contains a symbol like 'image_bioimagesuite_logo_www' it will work. But if it has several symbols like 'image_bioimagesuite_logo_www_1' 'image_bioimagesuite_logo_www_2' etc then the logo was too large and needs to be downsampled for use.

What do I do if I want to use the CLI outside of slicer?

In general CLI executables will depend on shared libraries provided by Slicer (such as ITKCommon, etc). To be sure your run-time environment resolves to the correct versions, you should always use a form like:

./Slicer --launch <CLI>

or, you can start a shell from which you can run multiple CLI modules. Something like:

./Slicer --launch xterm &

We do not suggest hard-coding the runtime paths into, for example, your .bashrc since this may interfere with other programs and can make it hard to use multiple versions of slicer on the same account.

Laundry List

Use this section to describe any features that are missing, not documented, or require additional work.

  • Comments from Hans Johnson, June 4, 2010:
    • Enumerated types can only have about 3 characters in them, otherwise they don’t render in the slicer wizard at all.
    • There is no way to unset an enumerated type, and the values are cached even after leaving the module. If the default enumerated type is to be blank, and you select it, then you must restart slicer in order to make the blank option available again.
    • There is no way to have conditional options. For example if one flag is used, then other flag can not be used, so it should be disabled.
  • Multiple images, transforms, etc. cannot be selected in the module user interface shown in Slicer (https://issues.slicer.org/view.php?id=4299)
  • Documenting your module:
    • a convenience script has been developed by Hans Johnson, which creates a wiki page describing the command line module by extracting the information from the xml description SEMToMediaWiki.py. TODO: extract the default values for the parameters to be included in the documentation