Difference between revisions of "Whole-Brain-Tractography-Wizard"

From NAMIC Wiki
Jump to: navigation, search
 
(10 intermediate revisions by the same user not shown)
Line 8: Line 8:
 
Image:fibers.png|[Fibers]
 
Image:fibers.png|[Fibers]
 
</gallery>
 
</gallery>
 +
In order to generate this wizard, we will use the For each step, we must:
 +
* Obtain the input from the interface
 +
* Validate the input
 +
* Set up the parameters for the next step
 +
* Provide the user with the appropriate information about errors in the processing and/or missing data
 +
 +
=API support for workflows=
 +
In order to do this, we use CTK's support for workflows:
 +
* [http://www.commontk.org/docs/html/classctkWorkflow.html ctkWorkflow]: State machine for the workflow
 +
* [http://www.commontk.org/docs/html/classctkWorkflowStackedWidget.html ctkWorkflowStackedWidget]: UI class for the workflow
 +
* [http://www.commontk.org/docs/html/classctkWorkflowWidgetStep.html ctkWorkflowWidgetStep]: Base class for each step of the workflow
 +
 +
In which the idea is to derive a new class from ctkWorkflowWidgetStep for each step and then add them all to the workflow widget ctkWorkflowStackedWidget making the transition explicit. The class ctkWorkflow is a state machine that controls the sequence of steps that are to be executed. The next figure gives an example of a non-linear workflow that can be implemented through this method:
 +
[[File:workflow_scheme.jpg | thumb | Example fo non-linear workflow]]
 +
 +
 +
===Events of each workflow widget step (ctkWorkflowWidgetStep)===
 +
* createUserInterface
 +
* onEntry(comingFrom, transitionType)
 +
* onExit(goingTo, transitionType)
 +
* validate(validationSucceded, desiredBranchId)
 +
 +
==Obtaining the input from the Interface==
 +
 +
You can find the example at
 +
  Modules/Scripted/Scripts/DICOM2FullBrainTractography/DICOM2FullBrainTractographyLib/full_tractography_workflow.py
 +
 +
In this example there is a helper class that makes things easy for linear workflows:
 +
  WorkflowConfiguration
 +
 +
This class:
 +
* Takes the widget for each step from a UI file generated by the designer
 +
* Setups the widgets into a sequential workflow
 +
* Obtain the data from the fields on the Qt window and put it into a python dictionary and passes it to the appropriate validation step
 +
And requires
 +
* Each step to be declared as the name of such file
 +
* The fields from each step to be declared
 +
*  The validation for each step to be implemented
 +
 +
====Declaring the Modules====
 +
    step_widget_files = [
 +
        'dicom2nrrd',
 +
        'dwi2dti',
 +
        'dti2fibers',
 +
        'done',
 +
    ]
 +
 +
====Declaring Each Module's Fields====
 +
    step_widget_fields = {
 +
        'dicom2nrrd':[
 +
            ('DICOMRadioButton', 'checked'),
 +
            ('NRRDDWIRadioButton', 'checked'),
 +
            ('inputDicomDirectory', 'directory'),
 +
            ('outputVolume', 'currentPath'),
 +
            ('useBMatrixGradientDirections','checked'),
 +
            ('inputNRRDVolume','currentPath'),
 +
        ],
 +
        'dwi2dti':[
 +
            ('leastSquaresEstimation', 'checked'),
 +
            ('weightedLeastSquaresEstimation', 'checked'),
 +
            ('thresholdParameter', 'value'),
 +
            ('removeIslands', 'checked'),
 +
            ('applyMask', 'checked'),
 +
        ],
 +
        'dti2fibers':[
 +
            ('seedSpacing','value'),
 +
            ('stoppingFAValue','value'),
 +
            ('minimumFAValueSeed','value'),
 +
            ('stoppingTrackCurvature','value'),
 +
        ],
 +
        'done':[],
 +
    }
 +
 +
====Validating Data for a Field====
 +
 +
  def validate_dicom2nrrd(self, step_object, data):
 +
        if data[step_object.id()]['DICOMRadioButton']:
 +
Running a CLI module from a python script
 +
            self.dicomtonrrdconverter_parameter_node = slicer.cli.run(
 +
                slicer.modules.dicomtonrrdconverter, self.dicomtonrrdconverter_parameter_node,
 +
                data[step_object.id()],
 +
                wait_for_completion = True)
 +
Validating the result of a CLI module
 +
          if self.dicomtonrrdconverter_parameter_node.GetStatusString() == 'Completed':
 +
                file_path = data[step_object.id()]['outputVolume']
 +
                result_status, node = slicer.util.loadVolume(
 +
                    file_path,
 +
                    True
 +
                )
 +
            else:
 +
                result_status = False
 +
Setting data for the next module
 +
            if result_status:
 +
                self.dwi_node = node
 +
                self.dwi_node_name = node.GetID()
 +
Output errors if needed
 +
        if not result_status:
 +
            display_error("Error in DICOM to NRRD conversion, please see log")
 +
        return result_status
 +
 +
==Details on Each Step's CallBack Implementation==
 +
Entry Callback
 +
    def onEntry(self, comingFrom, transitionType):
 +
        comingFromId = "None"
 +
        if comingFrom: comingFromId = comingFrom.id()
 +
        super(GeneralizedStep, self).onEntry(comingFrom, transitionType)
 +
        if hasattr(self, 'onEntryCallback'):
 +
            self.onEntryCallback(self, comingFrom, transitionType)
 +
 +
Exit Callback
 +
    def onExit(self, goingTo, transitionType):
 +
        goingToId = "None"
 +
        if goingTo: goingToId = goingTo.id()
 +
        super(GeneralizedStep, self).onExit(goingTo, transitionType)
 +
        if hasattr(self, 'onExitCallback'):
 +
            self.onExitCallback(self, goingTo, transitionType)
 +
 +
Validation Callabck
 +
    def validate(self, desiredBranchId):
 +
        validationSuceeded = True
 +
        if hasattr(self, 'validateCallback'):
 +
            validationSuceeded = self.validateCallback(self, desiredBranchId)
 +
        super(GeneralizedStep, self).validate(validationSuceeded, desiredBranchId)

Latest revision as of 16:59, 10 January 2012

Home < Whole-Brain-Tractography-Wizard

The Idea of a Wizard

Make a simple set of steps that will guide the user through a complex process. An example of this is the simple wizard that takes the user from a DICOM or NRRD Diffusion Weighted Image to a Full brain tractography. There are 4 steps to this process:

In order to generate this wizard, we will use the For each step, we must:

  • Obtain the input from the interface
  • Validate the input
  • Set up the parameters for the next step
  • Provide the user with the appropriate information about errors in the processing and/or missing data

API support for workflows

In order to do this, we use CTK's support for workflows:

In which the idea is to derive a new class from ctkWorkflowWidgetStep for each step and then add them all to the workflow widget ctkWorkflowStackedWidget making the transition explicit. The class ctkWorkflow is a state machine that controls the sequence of steps that are to be executed. The next figure gives an example of a non-linear workflow that can be implemented through this method:

Example fo non-linear workflow


Events of each workflow widget step (ctkWorkflowWidgetStep)

  • createUserInterface
  • onEntry(comingFrom, transitionType)
  • onExit(goingTo, transitionType)
  • validate(validationSucceded, desiredBranchId)

Obtaining the input from the Interface

You can find the example at

 Modules/Scripted/Scripts/DICOM2FullBrainTractography/DICOM2FullBrainTractographyLib/full_tractography_workflow.py

In this example there is a helper class that makes things easy for linear workflows:

 WorkflowConfiguration

This class:

  • Takes the widget for each step from a UI file generated by the designer
  • Setups the widgets into a sequential workflow
  • Obtain the data from the fields on the Qt window and put it into a python dictionary and passes it to the appropriate validation step

And requires

  • Each step to be declared as the name of such file
  • The fields from each step to be declared
  • The validation for each step to be implemented

Declaring the Modules

   step_widget_files = [
       'dicom2nrrd',
       'dwi2dti',
       'dti2fibers',
       'done',
   ]

Declaring Each Module's Fields

   step_widget_fields = {
       'dicom2nrrd':[
           ('DICOMRadioButton', 'checked'),
           ('NRRDDWIRadioButton', 'checked'),
           ('inputDicomDirectory', 'directory'),
           ('outputVolume', 'currentPath'),
           ('useBMatrixGradientDirections','checked'),
           ('inputNRRDVolume','currentPath'),
       ],
       'dwi2dti':[
           ('leastSquaresEstimation', 'checked'),
           ('weightedLeastSquaresEstimation', 'checked'),
           ('thresholdParameter', 'value'),
           ('removeIslands', 'checked'),
           ('applyMask', 'checked'),
       ],
       'dti2fibers':[
           ('seedSpacing','value'),
           ('stoppingFAValue','value'),
           ('minimumFAValueSeed','value'),
           ('stoppingTrackCurvature','value'),
       ],
       'done':[],
   }

Validating Data for a Field

 def validate_dicom2nrrd(self, step_object, data):
       if data[step_object.id()]['DICOMRadioButton']:

Running a CLI module from a python script

           self.dicomtonrrdconverter_parameter_node = slicer.cli.run(
               slicer.modules.dicomtonrrdconverter, self.dicomtonrrdconverter_parameter_node,
               data[step_object.id()],
               wait_for_completion = True)

Validating the result of a CLI module

          if self.dicomtonrrdconverter_parameter_node.GetStatusString() == 'Completed':
               file_path = data[step_object.id()]['outputVolume']
               result_status, node = slicer.util.loadVolume(
                   file_path,
                   True
               )
           else:
               result_status = False

Setting data for the next module

           if result_status:
               self.dwi_node = node
               self.dwi_node_name = node.GetID()

Output errors if needed

       if not result_status:
           display_error("Error in DICOM to NRRD conversion, please see log")
       return result_status

Details on Each Step's CallBack Implementation

Entry Callback

   def onEntry(self, comingFrom, transitionType):
       comingFromId = "None"
       if comingFrom: comingFromId = comingFrom.id()
       super(GeneralizedStep, self).onEntry(comingFrom, transitionType)
       if hasattr(self, 'onEntryCallback'):
           self.onEntryCallback(self, comingFrom, transitionType)

Exit Callback

   def onExit(self, goingTo, transitionType):
       goingToId = "None"
       if goingTo: goingToId = goingTo.id()
       super(GeneralizedStep, self).onExit(goingTo, transitionType)
       if hasattr(self, 'onExitCallback'):
           self.onExitCallback(self, goingTo, transitionType)

Validation Callabck

   def validate(self, desiredBranchId):
       validationSuceeded = True
       if hasattr(self, 'validateCallback'):
           validationSuceeded = self.validateCallback(self, desiredBranchId)
       super(GeneralizedStep, self).validate(validationSuceeded, desiredBranchId)