# Interactively adding control point with a given label

**URL:** <https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598>\
**Category:** Development\
**Tags:** python, markups-fiducials\
**Created:** [January 24, 2022, 3:26pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598 "2022-01-24T15:26:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 24, 2022, 3:26pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/1 "2022-01-24T15:26:43Z")

</div>

I build a user interface in which the user will click on the 3D scene (or the scenes of the three anatomical planes) and a fiducial node control point with a predetermined label will be placed.  
The problem is that I cannot set the label in the `slicer.modules.markups.logic().StartPlaceMode` function (there is no argument for that). A workaround I thought of is to place the control point with the label 3D Slicer automatically gives, and rename it after the point placement. To do so:

```python
slicer.modules.markups.logic().StartPlaceMode(0)
last_point_index = self.markup_node.GetNumberOfControlPoints() - 1
self.markup_node.SetNthControlPointLabel(last_point_index, my_preferred_label)

```

However, the last two lines get executed before the user can click on the scene. How can I pause the execution of the code until `StartPlaceMode` returns? The best solution would be my initial intention, i.e. being able to provide the label of the control point _before_ placing it on the scene.

---

<div class="post-metadata">

**Author:** ![cpinter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/cpinter/32/7995_2.png) [@cpinter](https://discourse.slicer.org/u/cpinter)\
**Post date:** [January 24, 2022, 3:48pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/2 "2022-01-24T15:48:10Z")

</div>

I assume you use an older version of Slicer. In the latest version it is possible to pre-define fiducials with names. I haven’t used that feature yet personally but I assume the way to do that is to create a fiducial node with the points you want and keep them undefined. Then when the user clicks, the next undefined point will be defined.

---

<div class="post-metadata">

**Author:** ![jamesobutler](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/jamesobutler/32/7511_2.png) [@jamesobutler](https://discourse.slicer.org/u/jamesobutler)\
**Post date:** [January 24, 2022, 3:49pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/3 "2022-01-24T15:49:21Z")

</div>

You may want to consider using a Markups Point List template. See the following linked post below. You can add undefined points to the list where you have predefined the name. Then when going into placement mode for this Point List node it will be going through the list to define the point location so will use your already defined labels.

> [@How to create Markups Point List template with GUI](https://discourse.slicer.org/t/how-to-create-markups-point-list-template-with-gui/20870/2):
>
> Under the control points advanced options, there is a button to add an unplaced point. Previously, it was supposed to add a point at the origin, but it had been broken for a while. It’s not very easy to find, maybe it should be moved to the main control points menu? [image]

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 24, 2022, 6:37pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/4 "2022-01-24T18:37:31Z")

</div>

I have version _4.11.20210226_ installed.  
By fiducial, do you mean the Markups node object or the control points that are defined within the active node? In case of the former, I can set the name at construction time, for the control points I cannot. Could you please send me a link to the relevant API doc entry?

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 24, 2022, 7:02pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/5 "2022-01-24T19:02:26Z")

</div>

Thank you for the tip, that could work for me. However, following the [referenced video](https://www.youtube.com/watch?v=m-z9vNRIhxg), the button at 0:21 does not add control points (I can still add normal control points, so I don’t know why unnamed control points cannot be defined).  
Assuming that the issue above is solved, here is a follow-up question. Can I select programmatically which unnamed control point to add? E.g. when the user clicks on the pushbutton `P1`, the control point with label `P-1` will be placed by the mouse, while if he clicks on another pushbutton `P7`, the control point with label `P-7` will be added to the scene. Note that the order may change, i.e. the user might first click on `P7` and then on `P1`.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [January 24, 2022, 8:04pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/6 "2022-01-24T20:04:40Z")

</div>

If you have a markup point list template, you can set the index of an unplaced point that will be placed on the next click using [SetControlPointPlacementStartIndex() method](https://apidocs.slicer.org/master/classvtkMRMLMarkupsNode.html#a3fbca86f7ca3d28616d60b974cdf13c0).

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 24, 2022, 10:09pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/7 "2022-01-24T22:09:17Z")

</div>

I cannot access that method from my `vtkMRMLMarkupsFiducialNode` object, which is strange as `SetControlPointPlacementStartIndex` is a public method of the `vtkMRMLMarkupsNode` class. Maybe it is not exposed to the Python interface? This is the traceback I get:

```python
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'vtkSlicerMarkupsModuleMRMLPython.vtkMRMLMarkupsFid' object has no attribute 'SetControlPointPlacementStartIndex'

```

My next try was looking into the source code to see what happens when I click on that button in the _Advanced_ settings. Behind the scenes, [that button](https://github.com/Slicer/Slicer/blob/d36e96bd544db2489d624c85727194cde6da7484/Modules/Loadable/Markups/qSlicerMarkupsModuleWidget.cxx#L1528) calls [this method](https://github.com/Slicer/Slicer/blob/d36e96bd544db2489d624c85727194cde6da7484/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.cxx#L686) (again, not available through Python). But even that function seems to need the (R,A,S) triplet, so how can you instantiate an undefined control point?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [January 24, 2022, 10:26pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/8 "2022-01-24T22:26:37Z")

</div>

For all the features that we have been discussing here you need a recent Slicer Preview Release.

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 24, 2022, 10:42pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/9 "2022-01-24T22:42:16Z")

</div>

Thank you, it works interactively with the latest preview release! I will try it tomorrow programmatically.

---

<div class="post-metadata">

**Author:** ![CsatiZoltan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/csatizoltan/32/13807_2.png) [@CsatiZoltan](https://discourse.slicer.org/u/CsatiZoltan)\
**Post date:** [January 25, 2022, 11:08am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/10 "2022-01-25T11:08:43Z")

</div>

I tried it, and it works just fine! All of you gave me very useful tips; since I can select only one solution, I accept the one from @jamesobutler as he pointed me to the GUI settings, which helped me find the corresponding implementation.

Here is an MWE so that others who stumble upon a similar problem can benefit from it.

1. All the code enumerated below went into the _main.py_ file, which was generated by the _Extension Wizard_.
2. Created a **Markup** node in the `setup` method of the class `mainWidget`. This is the node I will populate with the control points.

```python
node_id = slicer.modules.markups.logic().AddNewFiducialNode('your_label')
self.markup_node = slicer.mrmlScene.GetNodeByID(node_id)

```

3. Added the class variable `N_POINT = 9` into the class `mainWidget`, indicating the number of push buttons.
4. Initialized the control points by calling `self.initialize_points()` in the `setup method`. This function is defined as

```python
def initialize_points(self):
   slicer.modules.markups.logic().SetActiveListID(self.markup_node)
   self.markup_node.SetControlPointLabelFormat('P%d')
   for i in range(1, self.N_POINT + 1):
       self.markup_node.AddControlPoint([0, 0, 0])
   self.markup_node.UnsetAllControlPoints()
   self.markup_node.SetMaximumNumberOfControlPoints(self.N_POINT)

```

5. Created the nine push buttons in Qt Designer, and in the `setup` method I connected them with the function I want to execute. For instance, for button 4:

```python
self.ui.P4PushButton.connect('clicked(bool)', lambda: self.onPointPushButton('P4'))

```

The function is defined as

```python
def onPointPushButton(self, button):
   slicer.modules.markups.logic().SetActiveListID(self.markup_node)
   n_point = self.markup_node.GetNumberOfControlPoints()
   index = None
   for i in range(n_point):
       label = self.markup_node.GetNthControlPointLabel(i)
       if label == button:
           index = i
           print('Index: ', index)
           break
   if not index:
       raise ValueError('Point {0} not found.'.format(button))
   self.markup_node.UnsetNthControlPointPosition(index)
   self.markup_node.SetControlPointPlacementStartIndex(index)
   self.markup_node.SetNthControlPointLabel(index, button)
   slicer.modules.markups.logic().StartPlaceMode(0)

```

When writing this function, I was inspired by what the GUI does. Hence, I searched for the [corresponding GUI function](https://github.com/Slicer/Slicer/blob/d36e96bd544db2489d624c85727194cde6da7484/Modules/Loadable/Markups/qSlicerMarkupsModuleWidget.cxx#L1528).

* * *

I will polish this code (e.g. moving the non-GUI related stuff into the `mainLogic` class), though the main functionality works.  
One issue with this approach is that the control points are identified by labels, so if the user changes the label (e.g. by clicking on the markup in the scene and choosing _Rename control point…_), the control point is no longer found by the loop in the `onPointPushButton` method. Is there a way to disable the renaming, or better yet: identifying the control points by a kind of persistent property ?

---

<div class="post-metadata">

**Author:** ![jamesobutler](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/jamesobutler/32/7511_2.png) [@jamesobutler](https://discourse.slicer.org/u/jamesobutler)\
**Post date:** [January 25, 2022, 1:08pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/11 "2022-01-25T13:08:50Z")

</div>

You can whitelist certain right-click context menu actions. See the following:

[https://slicer.readthedocs.io/en/latest/developer\_guide/script\_repository.html#use-whitelist-to-customize-view-menu](https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#use-whitelist-to-customize-view-menu)

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [April 26, 2023, 4:49am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/12 "2023-04-26T04:49:15Z")

</div>

> [@jamesobutler](#):
>
> your already defined labels.

Hi Andras,  
Would it be possible to select the point (x,y,z) location in a ply file, for all the ply files opening up one by one and closing once the point is selected.  
The point needed to be selected by the user on the model and then the next model file opens up automatically and the user selects a point on that model and will do until points in all the ply files are selected. can this process be automated.

Thank you

regards,  
Saima

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [April 30, 2023, 1:50pm UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/13 "2023-04-30T13:50:25Z")

</div>

Yes, this can be very easily automated. Examples for all the necessary operations (loading a model, adding a markup point list, observing a markup node to get notified about changes) are available in the script repository but if you get stuck with automation of any specific step then let us know.

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [May 9, 2023, 4:17am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/14 "2023-05-09T04:17:11Z")

</div>

Hi Andras,  
I started working on the script. I loaded models from a given folder. used a for loop to load models one by one.

But i do not get the control point selection for each model.  
How can I stop for loop to get input from user first and then iterate the next one.  
The code is below:

import time  
startTime = time.time()  
logging.info(‘Processing started’)  
logging.info(‘Search for .ply files’)  
modelDir = inputDir  
modelFileExt = “ply”

```
    import math
    import os
    
    modelFiles = list(f for f in os.listdir(modelDir) if f.endswith("."+modelFileExt))
    print(modelFiles)
    
    #load models and show in 3D view
    for modelIndex, modelFile in enumerate(modelFiles):
        name = os.path.basename(modelFile)
        modelNode = slicer.util.loadModel(modelDir + "/" + modelFile)
        placeModePersistence = 1
        slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)    
        markup_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
        placeModePersistence = 0
        slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)    
        #slicer.util.updateMarkupsControlPointsFromArray(markup_node, np.random.rand(10,3))
        #markup_node.GetDisplayNode().SetTextScale(0)    
        markup_node.GetDisplayNode().SetPointLabelsVisibility(False)

```

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 9, 2023, 4:22am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/15 "2023-05-09T04:22:06Z")

</div>

You don’t need a loop. Instead, you can add an observer to the markup node. If an event invoked that indicates a new point is added then in your callback function you save the point and load the next model.

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [May 9, 2023, 4:27am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/16 "2023-05-09T04:27:46Z")

</div>

> [@lassoan](#):
>
> ed a loop. Instead, you can add an observer to the markup node.

could you please refer to the script for adding an observer.

Thanks alot.

regards,  
Saima

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [May 9, 2023, 4:39am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/17 "2023-05-09T04:39:47Z")

</div>

Do i need to keep the index for modelFiles[index] and pass it through the function in addObserver.

modelFiles = list(f for f in os.listdir(modelDir) if f.endswith(“.”+modelFileExt))  
print(modelFiles)

```
    #load models and show in 3D view
    #for modelIndex, modelFile in enumerate(modelFiles):
    global index = 0
    name = os.path.basename(modelFile)
    modelNode = slicer.util.loadModel(modelDir + "/" + modelFiles[index])
    placeModePersistence = 1
    slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)    
    markup_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
    updateindex
    markup_node.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent, onMarkupsAdded, index)

```

and in the function onMarkupsAdded I load the next model. right?

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [May 9, 2023, 6:14am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/18 "2023-05-09T06:14:51Z")

</div>

> [@lassoan](#):
>
> ew point is added then in your callback function you save the point and load the

Hi Andras,  
Could you please refer to the code snippets or scripts of what you suggested?

thank you

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 9, 2023, 10:57am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/19 "2023-05-09T10:57:03Z")

</div>

See [Script repository — 3D Slicer documentation](https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#define-edit-a-circular-region-of-interest-in-a-slice-viewer)

It is probably better to observe the new point definition event instead of any point modification.

This observation would immediately switch to the next case when you add the point, therefore you could not adjust the point after placement. So, maybe it is better to add a keyboard shortcut for going to the next case.

---

<div class="post-metadata">

**Author:** ![Saima](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@Saima](https://discourse.slicer.org/u/Saima)\
**Post date:** [May 11, 2023, 2:32am UTC](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598/20 "2023-05-11T02:32:15Z")

</div>

is new point definition event is PointAddedEvent?  
Also, I want to explore the model in the 3D view before placing a point on the model but because the mouse is in the fiducial placement mode I cant do that. whats the solution?

regards,  
Saima

[Next page](https://discourse.slicer.org/t/interactively-adding-control-point-with-a-given-label/21598.md?page=2)
