Difference between revisions of "Slicer3:Python:ScriptedModulesTipsNTricks"

From Slicer Wiki
Jump to: navigation, search
Line 1: Line 1:
 
= Python Scripted Modules - Tips'N'Tricks =
 
= Python Scripted Modules - Tips'N'Tricks =
  
This page shows tips and tricks for writing Python Scripted Modules in Slicer3.
+
This page shows tips and tricks for writing Python Scripted Modules in Slicer3. Most examples are taken from the AtlasCreator module.
  
 
== Adding your Logo to the Help&Acknowledgement Panel ==
 
== Adding your Logo to the Help&Acknowledgement Panel ==
Line 55: Line 55:
  
 
== MRML Events ==
 
== MRML Events ==
 +
 +
There are several example on how to observe GUI events in ScriptedModules. It is also possible to observe MRML events from the MRML Scene or from a node other than the vtkMRMLScriptedModuleNode.
 +
 +
The MRML observers can be safely added in the ''AddGUIObservers'' method.
 +
 
=== Observe MRML Scene events ===
 
=== Observe MRML Scene events ===
 +
 +
The following example shows how to observe the '''MRMLScene::NodeAddedEvent''' which is fired when a new MRML Node gets added to the scene. Also, we observe the '''MRMLScene::CloseEvent''' to update the GUI when the MRMLScene gets closed. Please remember to use the '''''RemoveMRMLNodeObservers''''' method to remove the MRML Scene observers (it is called automatically on teardown of the module).
 +
 +
<pre>
 +
# The event ids can be found in the MRML C++ header files (f.e. Libs/MRML/vtkMRMLScene.h..)
 +
vtkMRMLScene_NodeAddedEvent = 66000
 +
vtkMRMLScene_CloseEvent = 66003
 +
 +
class AtlasCreatorGUI(ScriptedModuleGUI):
 +
 +
    [...]
 +
 +
    '''=========================================================================================='''
 +
    def AddGUIObservers(self):
 +
        '''
 +
        Add the Observers. This method gets called automatically when the module gets created.
 +
       
 +
        For convenience, we also add the MRML observers here.
 +
        '''
 +
        # listen to MRML scene events
 +
        self._mrmlNodeAddedTag = self.AddMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_NodeAddedEvent)
 +
        self._mrmlSceneCloseTag = self.AddMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_CloseEvent)
 +
 +
    [...]
 +
 +
    '''=========================================================================================='''   
 +
    def RemoveMRMLNodeObservers(self):
 +
        '''
 +
        Remove MRML Node and MRML Scene Observers
 +
        '''
 +
       
 +
        self.RemoveMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_NodeAddedEvent)
 +
        self.RemoveMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_CloseEvent)
 +
 +
 +
 +
    '''=========================================================================================='''   
 +
    def ProcessMRMLEvents(self,callerID,event,callDataID = None):
 +
    ''' gets called, when an observed MRML event was fired '''
 +
   
 +
        # observe MRMLScene events
 +
        if callerID == "MRMLScene" and event == vtkMRMLScene_NodeAddedEvent and callDataID:
 +
           
 +
            callDataAsMRMLNode = slicer.MRMLScene.GetNodeByID(callDataID)
 +
           
 +
            if isinstance(callDataAsMRMLNode, slicer.vtkMRMLScalarVolumeNode):
 +
                print "A new vtkMRMLScalarVolumeNode was added: " + callDataID
 +
 +
 +
        # observe MRMLScene Close events
 +
        elif callerID == "MRMLScene" and event == vtkMRMLScene_CloseEvent:
 +
 +
            print "The MRMLScene was closed."
 +
            self.UpdateGUI()               
 +
 +
    [...]
 +
 +
</pre>
 +
 +
 
=== Observe MRML Node events ===
 
=== Observe MRML Node events ===
 +
 +
In this example, an existing '''vtkMRMLScalarVolumeNode''' is observed for changes in the associated ImageData which results in a '''vtkMRMLScalarVolumeNode::ImageDataModifiedEvent'''. It is required to know the ID of the MRML node to add observers. This can be coupled with listening to the MRMLScene events above (save the ID when a new MRMLNode of a certain type was added to the scene and then create observers). Here, we add the observer also in the ''AddGUIObservers'' method but in fact this can happen everywhere - even in ''ProcessMRMLEvents'' itself.
 +
 +
<pre>
 +
# The event ids can be found in the MRML C++ header files (f.e. Libs/MRML/vtkMRMLVolumeNode.h)
 +
vtkMRMLVolumeNode_ImageDataModifiedEvent = 18001
 +
 +
 +
class AtlasCreatorGUI(ScriptedModuleGUI):
 +
 +
    [...]
 +
 +
    def AddGUIObservers(self):
 +
        # first, we listen to a MRML node event
 +
        self._scalarVolumeNode = slicer.MRMLScene.GetNodeByID("vtkMRMLScalarVolumeNode1")
 +
        self._imagedataModifiedTag = self.AddMRMLObserverByNumber(self._scalarVolumeNode,vtkMRMLVolumeNode_ImageDataModifiedEvent)
 +
 +
    [...]
 +
 +
 +
    def ProcessMRMLEvents(self,callerID,event,callDataID = None):
 +
    ''' gets called, when an observed MRML event was fired '''
 +
   
 +
        # observe MRMLNode events
 +
        if callerID == "vtkMRMLScalarVolumeNode1" and event == vtkMRMLVolumeNode_ImageDataModifiedEvent:
 +
            print "ImageData of vtkMRMLScalarVolumeNode1 was modified."
 +
 +
    [...]
 +
 +
</pre>
 +
 
=== Use a custom MRML Node for your Scripted Module ===
 
=== Use a custom MRML Node for your Scripted Module ===
  

Revision as of 01:30, 13 April 2011

Home < Slicer3:Python:ScriptedModulesTipsNTricks

Python Scripted Modules - Tips'N'Tricks

This page shows tips and tricks for writing Python Scripted Modules in Slicer3. Most examples are taken from the AtlasCreator module.

Adding your Logo to the Help&Acknowledgement Panel

It is possible to show your custom logo in the Help&Acknowledgement Panel of your Python Scripted Module.

1. Add your logo to the modules directory in .png format. 2. Edit your CMakeLists.txt and add the following section to copy the logo into Slicer's shared module directory. In this example, we include the UPenn logo:

# copy UPenn_logo.png
configure_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/UPenn_logo.png
  ${CMAKE_BINARY_DIR}/${Slicer3_INSTALL_MODULES_SHARE_DIR}/${PROJECT_NAME}/UPenn_logo.png
  COPYONLY)
  
install(
  FILES ${CMAKE_CURRENT_SOURCE_DIR}/UPenn_logo.png
  DESTINATION ${Slicer3_INSTALL_MODULES_SHARE_DIR}/${PROJECT_NAME}
  )

3. In your ScriptedModuleGUI class, edit the BuildGUI function and add the following code after calling self.BuildHelpAndAboutFrame:

    def BuildGUI(self):
        '''
        Creates the Graphical User Interface (GUI) of the AtlasCreator. Gets called once during loading of the module.
        '''
...
        self._helpAboutFrame = self.BuildHelpAndAboutFrame(self._atlascreatorPage,helpText,aboutText)

        # include the UPenn logo
        logoFrame = self.GetLogoFrame()
        pathToLogo = os.path.normpath(slicer.Application.GetBinDir() + '/../share/Slicer3/Modules/AtlasCreator/UPenn_logo.png')
        logo = slicer.vtkKWIcon()
        
        logoReader = slicer.vtkPNGReader()
        logoReader.SetFileName(pathToLogo)
        logoReader.Update()
        
        logo.SetImage(logoReader.GetOutput())
        
        self._logoLabel = slicer.vtkKWLabel()
        self._logoLabel.SetParent(logoFrame)
        self._logoLabel.Create()
        self._logoLabel.SetImageToIcon(logo)
        slicer.TkCall("pack %s -side top -anchor nw -fill x -padx 2 -pady 2" % self._logoLabel.GetWidgetName())
...

The logo should appear after CMake gets executed the next time.

MRML Events

There are several example on how to observe GUI events in ScriptedModules. It is also possible to observe MRML events from the MRML Scene or from a node other than the vtkMRMLScriptedModuleNode.

The MRML observers can be safely added in the AddGUIObservers method.

Observe MRML Scene events

The following example shows how to observe the MRMLScene::NodeAddedEvent which is fired when a new MRML Node gets added to the scene. Also, we observe the MRMLScene::CloseEvent to update the GUI when the MRMLScene gets closed. Please remember to use the RemoveMRMLNodeObservers method to remove the MRML Scene observers (it is called automatically on teardown of the module).

# The event ids can be found in the MRML C++ header files (f.e. Libs/MRML/vtkMRMLScene.h..)
vtkMRMLScene_NodeAddedEvent = 66000
vtkMRMLScene_CloseEvent = 66003

class AtlasCreatorGUI(ScriptedModuleGUI):

    [...]

    '''=========================================================================================='''
    def AddGUIObservers(self):
        '''
        Add the Observers. This method gets called automatically when the module gets created.
        
        For convenience, we also add the MRML observers here.
        '''
        # listen to MRML scene events
        self._mrmlNodeAddedTag = self.AddMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_NodeAddedEvent)
        self._mrmlSceneCloseTag = self.AddMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_CloseEvent)

    [...]

    '''=========================================================================================='''    
    def RemoveMRMLNodeObservers(self):
        '''
        Remove MRML Node and MRML Scene Observers
        '''
        
        self.RemoveMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_NodeAddedEvent)
        self.RemoveMRMLObserverByNumber(slicer.MRMLScene,vtkMRMLScene_CloseEvent)



    '''=========================================================================================='''    
    def ProcessMRMLEvents(self,callerID,event,callDataID = None):
    ''' gets called, when an observed MRML event was fired '''
    
        # observe MRMLScene events
        if callerID == "MRMLScene" and event == vtkMRMLScene_NodeAddedEvent and callDataID:
            
            callDataAsMRMLNode = slicer.MRMLScene.GetNodeByID(callDataID)
            
            if isinstance(callDataAsMRMLNode, slicer.vtkMRMLScalarVolumeNode):
                print "A new vtkMRMLScalarVolumeNode was added: " + callDataID


        # observe MRMLScene Close events
        elif callerID == "MRMLScene" and event == vtkMRMLScene_CloseEvent:

            print "The MRMLScene was closed."
            self.UpdateGUI()                

    [...]


Observe MRML Node events

In this example, an existing vtkMRMLScalarVolumeNode is observed for changes in the associated ImageData which results in a vtkMRMLScalarVolumeNode::ImageDataModifiedEvent. It is required to know the ID of the MRML node to add observers. This can be coupled with listening to the MRMLScene events above (save the ID when a new MRMLNode of a certain type was added to the scene and then create observers). Here, we add the observer also in the AddGUIObservers method but in fact this can happen everywhere - even in ProcessMRMLEvents itself.

# The event ids can be found in the MRML C++ header files (f.e. Libs/MRML/vtkMRMLVolumeNode.h)
vtkMRMLVolumeNode_ImageDataModifiedEvent = 18001


class AtlasCreatorGUI(ScriptedModuleGUI):

    [...]

    def AddGUIObservers(self):
        # first, we listen to a MRML node event
        self._scalarVolumeNode = slicer.MRMLScene.GetNodeByID("vtkMRMLScalarVolumeNode1")
        self._imagedataModifiedTag = self.AddMRMLObserverByNumber(self._scalarVolumeNode,vtkMRMLVolumeNode_ImageDataModifiedEvent)

    [...]


    def ProcessMRMLEvents(self,callerID,event,callDataID = None):
    ''' gets called, when an observed MRML event was fired '''
    
        # observe MRMLNode events
        if callerID == "vtkMRMLScalarVolumeNode1" and event == vtkMRMLVolumeNode_ImageDataModifiedEvent:
            print "ImageData of vtkMRMLScalarVolumeNode1 was modified."

    [...]

Use a custom MRML Node for your Scripted Module

Using KWWidget Callbacks

Writing Tests in Python