How to access Node in Scene?

Operating system: window 11
Slicer version: 4.13.0

Hi , Slicer users

I’m implementing an extension that draws a depthmap in slicer through a python module.
Depthmap is almost done, but there is a problem in considering convenience.

Question

Q1. How do I access nodes belonging to a Scene? ( not getNode(‘F’) )

=> Whenever markups are added, I would like to obtain a list of markups and extract them
in the form of stack.pop() rather than importing getNode(‘F_1’) or getNode(‘F_2’) individually.

Q2. When deleting markup, why isn’t it completely deleted?

=> If you delete the F-3 markup, when you take a new markup,
it should be named F-3, but why does it become f-4?

As a result, I want to handle nodes internally to increase the usability of the function that draws the depthmap(I made), So that I want to know about the functions that can be involved in their insertion and deletion.

You can observe events and use callbacks to react to these events. A simple example could be something like this (from Documentation/4.10/Developers/FAQ/Python Scripting - Slicer Wiki):

@vtk.calldata_type(vtk.VTK_OBJECT)
def nodeAddedCallback(caller, eventId, callData):
  print("Node added")
  print("New node: {0}".format(callData.GetName()))

nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, nodeAddedCallback)

You can test this snippet directly on the python interpreter. In this example, every time that a new node is added to the scene, the nodeAddedCallback function wil lbe called (you can make this function a member of your Logic in your module. caller, eventId and callData have all the information you need about the event happening and you can use it to discriminate the node type you are interested in and react to it.

Another thing is that you probably want to act on new control points added instead of new markups nodes added. In that case, you could add the observer on the NodeModifiedEvent instead; or even better you can (1) observe the scene for new nodes (vtkMRMLMarkupsFiducialNode) and whenever one of them is added, (2) you add a new callback that observes the ModifiedEvent of that particular node to monitor the changes on the number of control points.

Not decrementing the control point number is the expected behaviour according to the code documentation (https://github.com/Slicer/Slicer/blob/38d8d9911dcf0a72df73bec658cf2e985c95c049/Modules/Loadable/Markups/MRML/vtkMRMLMarkupsNode.h#L706-L712):

  ///@{
  /// This value is used for generating number in the control point's name when a new point is added.
  /// The value is not decremented when a control point is deleted to keep the control point names unique.
  /// The value is reset to 0 when \sa RemoveAllControlPoints is called.
  vtkGetMacro(LastUsedControlPointNumber, int);
  vtkSetMacro(LastUsedControlPointNumber, int);
  ///@}

I suppose you need to manage the control points names in your module.

2 Likes

Wow, I love you man (very x 100 Thx)

1 Like

Dear @RafaelPalomar
Looking at your answer, I think I misunderstood the question, so I’m going to ask the question again.

function flow

mouse_click_event_occured → get params → start my depthmap(‘new markup name’, ‘other infomation’ )

I plan to design a module that receives the markup name information when a new markup appears through a mouse click event and draws a depth map.

So, my current situation is as follows.

  • Draw depthmap module ( okay )

  • Mouse click event (not yet)

  • bring node’s information (not yet)

Question
When the latest markup ‘F_3’ appears through a mouse click, my module needs to receive ‘F-3’(new_markup_name’) as an input.

This part seems to be all you need to know how to access information about all the data that node has, but I didn’t understand this part through the doc you mentioned. can i know how?

Hello again,

If I understand your problem correctly, you are probably not interested in the mouse click itself, you are interested to know when a new markups control point is placed (and yes, usually this comes after a mouse click, but it could also come from a python command). Therefore, I think you should focus on the event of adding a new control point.

If your design only requires to have one vtkMRMLMarkupsFiducialNode with several control points, I would create one in my code during initialization, and observe

# This should go in your logic initialization and should be called only
# once
self.markupsFiducialNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLMarkupsFiducialNode')
self.markupFiducialNode.AddObserver(self.markupsFiducialNode.PointPositionDefinedEvent, self.onControlPointAdded)

self.onControlPointAdded is a callback function that will be called every time a new control point on self.markupsFiducialNode is added. The callback function will have some parameters that will help you get all the information about the node and the control points. There is more information about this mechanism in: Handle Markups events from an external module - #28 by Fangwen_Zhai

I hope this works for you.

@RafaelPalomar

Hi again,


@vtk.calldata_type(vtk.VTK_OBJECT)
def Margin_added_Callback(caller, eventId, callData):
 print("Margin added" )
 print("New node: {0}".format(callData.GetName()))
 # find margin data
 if callData.GetClassName() == 'vtkMRMLMarkupsClosedCurveNode' :
  # TODO ... 

nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, Margin_added_Callback)



Through the code you gave me, I understood how to access any data expressed in node.
But I have one more question.

In the case of the ‘markups’ , as soon as the mouse is clicked on the markups, the information of ‘vtkMRMLMarkupsFiducialNode’ is obtained, so there is no position coordinate.

In the case of other data, it doesn’t matter because it is loaded in advance, but in the case of markup, creating and locating are separate actions, so it seems to be a problem.

In this case, how can I get the coordinates of the markup after placing it in the 3D view??

In nodeAddedCallback you will “get notified” about new vtkMRMLMarkupsFiducialNode nodes. Every time a new of these nodes is added, you can add an observer to that node for observing the addition of new control points on that node. Something on the lines of:

markupFiducialNode.AddObserver(self.markupsFiducialNode.PointPositionDefinedEvent,
self.onControlPointAdded)

Then again onControlPointAdded should also be a callback function that will react to the addition of a control point to the fiducial markup.

If you think that only one vtkMRMLMarkupsFiducialNode will be used, you can generate it during initialization of your module yourself and you can save the first callback.

1 Like