Modules:Python

From Slicer Wiki
Jump to: navigation, search
Home < Modules:Python

Basics of building a Python GUI module for Slicer3

Python GUI modules all subclass ScriptedModuleGUI and override selected methods. Below is a full example of a gradient aniostropic diffusion module.

from SlicerScriptedModule import ScriptedModuleGUI
from Slicer import slicer

vtkKWScale_ScaleValueChangedEvent = 10001
vtkKWScale_ScaleValueStartChangingEvent = 10002
vtkSlicerNodeSelectorWidget_NodeSelectedEvent = 11000
vtkKWPushButton_InvokedEvent = 10000

class PythonGADScriptedModuleGUI(ScriptedModuleGUI):

    def __init__(self):
        ScriptedModuleGUI.__init__(self)
        self.GadNodeSelector = slicer.vtkSlicerNodeSelectorWidget()
        self.ConductanceScale = slicer.vtkKWScaleWithEntry()
        self.TimeStepScale = slicer.vtkKWScaleWithEntry()
        self.NumberOfIterationsScale = slicer.vtkKWScaleWithEntry()
        self.VolumeSelector = slicer.vtkSlicerNodeSelectorWidget()
        self.OutVolumeSelector = slicer.vtkSlicerNodeSelectorWidget()
        self.ApplyButton = slicer.vtkKWPushButton()
    
    def Destructor(self):
        pass
    
    def RemoveMRMLNodeObservers(self):
        pass
    
    def RemoveLogicObservers(self):
        pass

    def AddGUIObservers(self):
        self.ConductanceScaleChangingTag = self.AddObserverByNumber(self.ConductanceScale,vtkKWScale_ScaleValueStartChangingEvent)
        self.ConductanceScaleChangedTag = self.AddObserverByNumber(self.ConductanceScale,vtkKWScale_ScaleValueChangedEvent)
      
        self.TimeStepScaleChangingTag = self.AddObserverByNumber(self.TimeStepScale,vtkKWScale_ScaleValueStartChangingEvent)
        self.TimeStepScaleChangedTag = self.AddObserverByNumber(self.TimeStepScale,vtkKWScale_ScaleValueChangedEvent)
      
        self.NumberOfIterationsScaleChangingTag = self.AddObserverByNumber(self.NumberOfIterationsScale,vtkKWScale_ScaleValueStartChangingEvent)
        self.NumberOfIterationsScaleChangedTag = self.AddObserverByNumber(self.NumberOfIterationsScale,vtkKWScale_ScaleValueChangedEvent)
        
        self.VolumeSelectorSelectedTag = self.AddObserverByNumber(self.VolumeSelector,vtkSlicerNodeSelectorWidget_NodeSelectedEvent)
        
        self.OutVolumeSelectorSelectedTag = self.AddObserverByNumber(self.OutVolumeSelector,vtkSlicerNodeSelectorWidget_NodeSelectedEvent)
        
        self.GadNodeSelectorSelectedTag = self.AddObserverByNumber(self.GadNodeSelector,vtkSlicerNodeSelectorWidget_NodeSelectedEvent)
        
        self.ApplyButtonTag = self.AddObserverByNumber(self.ApplyButton,vtkKWPushButton_InvokedEvent)

    def RemoveGUIObservers(self):
        self.RemoveObserver(self.ConductanceScaleChangingTag)
        self.RemoveObserver(self.ConductanceScaleChangedTag)
      
        self.RemoveObserver(self.TimeStepScaleChangingTag)
        self.RemoveObserver(self.TimeStepScaleChangedTag)
      
        self.RemoveObserver(self.NumberOfIterationsScaleChangingTag)
        self.RemoveObserver(self.NumberOfIterationsScaleChangedTag)
        
        self.RemoveObserver(self.VolumeSelectorSelectedTag)
        
        self.RemoveObserver(self.OutVolumeSelectorSelectedTag)
        
        self.RemoveObserver(self.GadNodeSelectorSelectedTag)
        
        self.RemoveObserver(self.ApplyButtonTag)

    def ProcessGUIEvents(self,caller,event):
        if caller == self.ConductanceScale and event == vtkKWScale_ScaleValueChangedEvent:
            self.UpdateMRML()
        elif caller == self.TimeStepScale and event == vtkKWScale_ScaleValueChangedEvent: 
            self.UpdateMRML()
        elif caller == self.NumberOfIterationsScale and event == vtkKWScale_ScaleValueChangedEvent:
            self.UpdateMRML()
        elif caller == self.VolumeSelector and event == vtkSlicerNodeSelectorWidget_NodeSelectedEvent and self.VolumeSelector.GetSelected():
            self.UpdateMRML()
        elif caller == self.OutVolumeSelector and event == vtkSlicerNodeSelectorWidget_NodeSelectedEvent and self.OutVolumeSelector.GetSelected(): 
            self.UpdateMRML()
        elif caller == self.GadNodeSelector and event == vtkSlicerNodeSelectorWidget_NodeSelectedEvent and self.GadNodeSelector.GetSelected():
            node = self.GadNodeSelector.GetSelected()
            self.GetLogic().SetAndObserveScriptedModuleNode(node)
            self.SetAndObserveScriptedModuleNode(node)
            self.UpdateGUI()
        elif caller == self.ApplyButton and event == vtkKWPushButton_InvokedEvent:
            self.UpdateMRML()
            self.Apply()

    def Apply(self):
        if not self.GetScriptedModuleNode():
          slicer.Application.ErrorMessage("No input ScriptedModuleNode found")
          return
    
        scriptedModuleNode = self.GetScriptedModuleNode()
    
        inVolume = scriptedModuleNode.GetParameter('InputVolumeRef')
        if not inVolume:
          slicer.Application.ErrorMessage("No input volume found")
          return
      
        outVolume = scriptedModuleNode.GetParameter('OutputVolumeRef')
        if not outVolume:
          slicer.Application.ErrorMessage("No output volume found")
          return
    
        outVolume.CopyOrientation(inVolume)
        outVolume.SetAndObserveTransformNodeID(inVolume.GetTransformNodeID())
    
        gradientAnisotropicDiffusionImageFilter = slicer.vtkITKGradientAnisotropicDiffusionImageFilter()
        gradientAnisotropicDiffusionImageFilter.SetInput(inVolume.GetImageData())
        gradientAnisotropicDiffusionImageFilter.SetConductanceParameter(scriptedModuleNode.GetParameter('Conductance'))
        gradientAnisotropicDiffusionImageFilter.SetNumberOfIterations(scriptedModuleNode.GetParameter('NumberOfIterations'))
        gradientAnisotropicDiffusionImageFilter.SetTimeStep(scriptedModuleNode.GetParameter('TimeStep'))
        gradientAnisotropicDiffusionImageFilter.Update()
    
        image = slicer.vtkImageData()
        image.DeepCopy(gradientAnisotropicDiffusionImageFilter.GetOutput())
        outVolume.SetAndObserveImageData(image)
        outVolume.ModifiedSinceReadOn()
       
        slicer.Application.InformationMessage("Done applying GradientAnisotropicDiffusion.")

    def UpdateMRML(self):
        node = self.GetScriptedModuleNode()
        if not node:
            self.GadNodeSelector.SetSelectedNew("vtkMRMLScriptedModuleNode")
            self.GadNodeSelector.ProcessNewNodeCommand("vtkMRMLScriptedModuleNode", "GADParameters")
            node = self.GadNodeSelector.GetSelected()
            self.GetLogic().SetAndObserveScriptedModuleNode(node)
            self.SetScriptedModuleNode(node)
      
        self.GetLogic().GetMRMLScene().SaveStateForUndo(node)
     
        node.SetParameter('Conductance',self.ConductanceScale.GetValue())
        node.SetParameter('TimeStep',self.TimeStepScale.GetValue())
        node.SetParameter('NumberOfIterations',self.NumberOfIterationsScale.GetValue())  
        
        if self.VolumeSelector.GetSelected():
            node.SetParameter('InputVolumeRef',self.VolumeSelector.GetSelected())
              
        if self.OutVolumeSelector.GetSelected():
            node.SetParameter('OutputVolumeRef',self.OutVolumeSelector.GetSelected())

    def UpdateGUI(self):
        node = self.GetScriptedModuleNode()
        if node:
            conductance = node.GetParameter('Conductance')
            if conductance:
                self.ConductanceScale.SetValue(conductance)
            numberOfIterations = node.GetParameter('NumberOfIterations')
            if numberOfIterations:
                self.NumberOfIterationsScale.SetValue(numberOfIterations)
            timeStep = node.GetParameter('TimeStep')
            if timeStep:
                self.TimeStepScale.SetValue(timeStep)

    def ProcessMRMLEvents(self,caller,event):
        if self.GetScriptedModuleNode() == caller:
            self.UpdateGUI()

    def BuildGUI(self):
        self.GetUIPanel().AddPage("GradientAnisotropicDiffusionFilter","GradientAnisotropicDiffusionFilter","")
        pageWidget = self.GetUIPanel().GetPageWidget("GradientAnisotropicDiffusionFilter")
        helpText = "Flex, dude!"
        aboutText = "This work is supported by NA-MIC, NAC, BIRN, NCIGT, and the Slicer Community. See http://www.slicer.org for details."
        self.BuildHelpAndAboutFrame(pageWidget,helpText,aboutText)
    
        moduleFrame = slicer.vtkSlicerModuleCollapsibleFrame()
        moduleFrame.SetParent(self.GetUIPanel().GetPageWidget("GradientAnisotropicDiffusionFilter"))
        moduleFrame.Create()
        moduleFrame.SetLabelText("Gradient Anisotropic Diffusion Filter")
        moduleFrame.ExpandFrame()
        widgetName = moduleFrame.GetWidgetName()
        pageWidgetName = self.GetUIPanel().GetPageWidget("GradientAnisotropicDiffusionFilter").GetWidgetName()
        slicer.TkCall("pack %s -side top -anchor nw -fill x -padx 2 -pady 2 -in %s" % (widgetName,pageWidgetName))
    
        self.GadNodeSelector.SetNodeClass("vtkMRMLScriptedModuleNode", "ScriptedModuleName", self.GetLogic().GetModuleName(), "GADParameters")
        self.GadNodeSelector.NewNodeEnabledOn()
        self.GadNodeSelector.NoneEnabledOn()
        self.GadNodeSelector.ShowHiddenOn()
        self.GadNodeSelector.SetParent(moduleFrame.GetFrame())
        self.GadNodeSelector.Create()
        self.GadNodeSelector.SetMRMLScene(self.GetLogic().GetMRMLScene())
        self.GadNodeSelector.UpdateMenu()
        self.GadNodeSelector.SetBorderWidth(2)
        self.GadNodeSelector.SetLabelText("GAD Parameters")
        self.GadNodeSelector.SetBalloonHelpString("select a GAD node from the current mrml scene.")
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.GadNodeSelector.GetWidgetName())
    
        self.ConductanceScale.SetParent(moduleFrame.GetFrame())
        self.ConductanceScale.SetLabelText("Conductance")
        self.ConductanceScale.Create()
        w = self.ConductanceScale.GetScale().GetWidth()
        self.ConductanceScale.SetRange(0,10)
        self.ConductanceScale.SetResolution(0.1)
        self.ConductanceScale.SetValue(1.0) 
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.ConductanceScale.GetWidgetName())
    
        self.TimeStepScale.SetParent(moduleFrame.GetFrame())
        self.TimeStepScale.SetLabelText("Time Step")
        self.TimeStepScale.Create()
        self.TimeStepScale.GetScale().SetWidth(w)
        self.TimeStepScale.SetRange(0.0,1.0)
        self.TimeStepScale.SetValue(0.1)
        self.TimeStepScale.SetResolution(0.01)
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.TimeStepScale.GetWidgetName())
    
        self.NumberOfIterationsScale.SetParent(moduleFrame.GetFrame())
        self.NumberOfIterationsScale.SetLabelText("Iterations")
        self.NumberOfIterationsScale.Create()
        self.NumberOfIterationsScale.GetScale().SetWidth(w)
        self.NumberOfIterationsScale.SetValue(1)
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.NumberOfIterationsScale.GetWidgetName())
    
        self.VolumeSelector.SetNodeClass("vtkMRMLScalarVolumeNode","","","")
        self.VolumeSelector.SetParent(moduleFrame.GetFrame())
        self.VolumeSelector.Create()
        self.VolumeSelector.SetMRMLScene(self.GetLogic().GetMRMLScene())
        self.VolumeSelector.UpdateMenu()
        self.VolumeSelector.SetBorderWidth(2)
        self.VolumeSelector.SetLabelText("Input Volume: ")
        self.VolumeSelector.SetBalloonHelpString("select an input volume from the current mrml scene.")
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.VolumeSelector.GetWidgetName())
    
        self.OutVolumeSelector.SetNodeClass("vtkMRMLScalarVolumeNode","","","GADVolumeOut")
        self.OutVolumeSelector.SetNewNodeEnabled(1)
        self.OutVolumeSelector.SetParent(moduleFrame.GetFrame())
        self.OutVolumeSelector.Create()
        self.OutVolumeSelector.SetMRMLScene(self.GetLogic().GetMRMLScene())
        self.OutVolumeSelector.UpdateMenu()
        self.OutVolumeSelector.SetBorderWidth(2)
        self.OutVolumeSelector.SetLabelText("Output Volume: ")
        self.OutVolumeSelector.SetBalloonHelpString("select an output volume from the current mrml scene.")
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 4" % self.OutVolumeSelector.GetWidgetName())
    
        self.ApplyButton.SetParent(moduleFrame.GetFrame())
        self.ApplyButton.Create()
        self.ApplyButton.SetText("Apply")
        self.ApplyButton.SetWidth(8)
        slicer.TkCall("pack %s -side top -anchor e -padx 20 -pady 10" % self.ApplyButton.GetWidgetName())

    def TearDownGUI(self):
        self.GadNodeSelector.SetParent(None)
        self.GadNodeSelector = None
        self.ConductanceScale.SetParent(None)
        self.ConductanceScale = None
        self.TimeStepScale.SetParent(None)
        self.TimeStepScale = None
        self.NumberOfIterationsScale.SetParent(None)
        self.NumberOfIterationsScale = None
        self.VolumeSelector.SetParent(None)
        self.VolumeSelector = None
        self.OutVolumeSelector.SetParent(None)
        self.OutVolumeSelector = None
        self.ApplyButton.SetParent(None)
        self.ApplyButton = None
   
        if self.GetUIPanel().GetUserInterfaceManager():
            pageWidget = self.GetUIPanel().GetPageWidget("GradientAnisotropicDiffusionFilter")
            self.GetUIPanel().RemovePage("GradientAnisotropicDiffusionFilter")