Programmatically controlling extensions via separate scripted extension

Is it possible to programmatically control the state of other 3D Slicer extensions via my own scripted extension using the python API? Such as trigger UI events or update their internal state?

E.g: when restoring Slicer from a saved MRML Scene, some extensions need manual prodding to resume operation, such as SlicerOpenIGTLink when activating an existing connection:

Or with SlicerIGT to enable Bullseye View Mode for multiple Scene Cameras:

Rather than necessitating the user to navigate to there respective extension panels and enable these function after startup, I’d like to script a simple one click button to resume these behaviors.


It also seems that the Vewpoint extension included in SlicerIGT doesn’t save all its own settings with the MRML Scene, so I’d like to re-initialize those camera control parameters as well.

I’m guessing I could rewrite these IGT features internal to my own extension, but I’d prefer to reuse the existing extensions for simpler user control override and existing interactive workflows.

Yes, it’s very common to interact with different modules, even ones that come from other extensions. It’s best practice to interact through MRML and parameter nodes, but usually you can import the module and access the Logic classes. If the module doesn’t expose parameter nodes or logic methods you can technically access the GUI but it’s not the way we like to do things. If you are working on a longer term project you might propose changes to the modules you are using to have them expose the functionality you need.

1 Like

Thanks for good pointers!

Is there discovery process to inspect what parameter nodes are available through MRML?
I.e. is there an option for Slicer to show what node my mouse is hovering over or interacting with?

I looked for anything related to SlicerOpenIGTLink or just IGT, and found:

>>> nodes = slicer.mrmlScene.GetNodes()
>>> for node in nodes:
  print(node)
... 
vtkMRMLCrosshairNode (0x35d0b5f0)
  ID: vtkMRMLCrosshairNodedefault
  ClassName: vtkMRMLCrosshairNode
  Name: Crosshair
...
>>> node = slicer.mrmlScene.GetFirstNodeByName('IGTLConnector')
>>> print(node)
vtkMRMLIGTLConnectorNode (0x3cf120d0)
  ID: vtkMRMLIGTLConnectorNode1
  ClassName: vtkMRMLIGTLConnectorNode
  Name: IGTLConnector
  Debug: false
  MTime: 439420
  Description: (none)
  SingletonTag: (none)
  HideFromEditors: false
  Selectable: true
  Selected: false
  UndoEnabled: false
  Node references:
    incoming [incomingNodeRef]: vtkMRMLLinearTransformNode4 vtkMRMLLinearTransformNode5
    outgoing [outgoingNodeRef]: (none)
  Connector Type: CLIENT
  Server Hostname: localhost
  Server Port #: 18944
  State: CONNECTED
  Persistent: 0
  Restrict Device Name: 0
  Push Outgoing Message Flag: 0
  Check CRC: 1
  Number of outgoing nodes: 0
  Number of incoming nodes: 2

After using the internal python console as a REPEL, I found a working stop and start function:

>>> node.Stop()
1
>>> node.Start()
1
[VTK] Connector is already running!

But where is this node interface documented or define in an extension codebase?
Simply the header?

but usually you can import the module and access the Logic classes.

Is that still feasible for a scripted extension when attempting to control a C++ extension?

In any case, I think I may have to do that for the ViewpointInstance class, as the extension is scripted, however I can’t find it among the MRML nodes while the Viewpoint extension is loaded and running.

>>> nodes = slicer.mrmlScene.GetNodes()
>>> for node in nodes:
  if 'viewpoint' in node.GetName().lower():
    print(node)
>>> 

How should I go about getting a object handle for this extension class?

If the module doesn’t expose parameter nodes or logic methods you can technically access the GUI but it’s not the way we like to do things.

Understood. Although as a last resort, what would that look like in C++ & Python for example?

If you are working on a longer term project you might propose changes to the modules you are using to have them expose the functionality you need.

Know of any reference PRs I could look at as an example? I’m still learning Slicer’s APIs and extension code layouts. Simply classes with a public vtkMRMLNode interface?


In the short term, I’d also be fine with something like equivalent of an HTML iframe for QTM, where our custom extension could just render parts of another extension UI within its own pannel.

We try to make sure the core is reasonably documented (and even that’s hard) but the extensions can vary a lot.

This documentation describes a general method for exploring functionality, which is a few steps to follow but generally gets you to the right place in the source code.

It’s very common to use a widget from another module, but not all GUIs are broken out for easy reuse.

If you want to “drive the GUI” from a script you can look at how it’s done in the SelfTests, but again, that’s going to be fragile.