Difference between revisions of "Whole-Brain-Tractography-Wizard"
(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-WizardThe 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:
- ctkWorkflow: State machine for the workflow
- ctkWorkflowStackedWidget: UI class for the workflow
- 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:
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)