Markup nodes - option for locked/unlocked on creation?

It looks like the default lock status for markup nodes is unlocked by default - looking in the vtkMRMLMarkupsNode() constructor, it sets Locked to 0 on the second line of code.

I almost never move markups, unless to move them back after creating them and accidentally moving them if they’re unlocked. I basically have added hitting the “lock all” button periodically to my workflow to make sure I don’t inadvertently move them around.

Is there a reason they’re unlocked by default? Would there be any interest in an option for toggling unlocked/locked on creation?

Thanks!

-Hollister

As far as I can see, moving markup points is very common: most often you place it approximately in one view, potentially in 3D, then you verify and fine-tune the position in multiple, zoomed-in slice views. If that’s not your workflow then no problem, you can modify default properties of new nodes by specifying a default node - see examples in script repository. You can put those few lines into your .slicerrc.py file to make it always the default on your computer.

Ah, ok - that’s MUCH cleaner than my solution. Right now I have a hotkey set to place a fiducial, and in that function I run SetAllMarkupsLocked().

Thanks Andras! Hope you’re doing well.

-Hollister

1 Like

This is a very stale thread, sorry, but I’m finally getting to doing this the right way rather than my old hacky solution.

I’m trying to make a default markups node like this:

defaultMarkupNode = slicer.vtkMRMLMarkupsNode()
defaultMarkupNode.SetLocked(1)
slicer.mrmlScene.AddDefaultNode(defaultMarkupNode)

but when I do this, new markups still show up as unlocked. What am I doing wrong here?

THANKS!

-Hollister

It is all good, you just need to create more concreate default node class(es). To prevent moving line node endpoints after placement:

defaultMarkupNode = slicer.vtkMRMLMarkupsLineNode()
defaultMarkupNode.SetLocked(1)
slicer.mrmlScene.AddDefaultNode(defaultMarkupNode)

Hmph… something still missing, I put both these code snippets in my .slicerrc.py file and when I make a new markup node and add points, they’re still unlocked. Is there something I need to do to tell the vtkMRMLMarkupsFiducialNode that it needs to use those particular vtkMRMLMarkupsNodes as defaults? (This question likely doesn’t make any sense…)

For a Fiducial node, the following works for me where the control points are not movable and are locked. If it doesn’t work for you, download a new Slicer version, don’t install any extensions, and test this snippet.

defaultMarkupNode = slicer.vtkMRMLMarkupsFiducialNode()  # fiducial node markup
defaultMarkupNode.SetLocked(1)
slicer.mrmlScene.AddDefaultNode(defaultMarkupNode)

OK, that does make the point immovable, but it does not show as locked in the control points list. I’m also not able to rename the point by right-clicking on the point - it’s completely locked and not modifiable.

What I’m looking for is to lock the position by default but be able to rename it via right clicking.

My hacky work-around has been okay thus far, so if this takes any real amount of time to look into, no need to bother.

Thanks!!!

Yes the example code has been setting the Locked state for the node which applies to all control points rather than setting the locked state for the individual control point. The locked state in the example code is indicated by the locked icon in the GUI as shown with the tooltip.
image

Ah, okay. Is it possible to make a default control point that is locked?

Control points are currently always initialized unlocked. You could lock them without Slicer core change by adding an observer to control point add event and lock every newly added control points. However, if “locked by default” is a common need then it would be quite easy to add a flag for this in the markups node.

I guess you need locking because you want to avoid accidental modification of placed points (e.g., when you rotate the view you may accidently click on a control point), which must be a common need. @smrolfe How do your users solve the problem of accidentally misplaced control points?

@hherhold would you mind describing your use case on this page so that we can get more complete picture of how people use markups?

Yeah, as described in the use case I added (below), I set up a shortcut key to run a quick function that locks all control points. The minus is that it locks all the ones done thus far, so the one just placed is unlocked, but it’s been better than nothing. (Yes, very hacky.)

I’ll take a look at adding an observer - that’s far more elegant.

Done.

1 Like

Yes, this is a common need. We’ve had some requests for an undo button to correct these kind of mistakes. Using a landmark template should help with this. If the landmark points are locked when the template is created, they can be placed but not moved in the scene (by default) when the template is imported.

2 Likes

how to individually lock control points (python script) when they are created? are they still locked when saving and when opening them with a new application program.

regards,
saima

This is how I do it, it is absolutely not the best way, and not even the right way, but it works for my workflow. I have a 5-button programmable mouse, so I’ve set a hotkey/shortcut of ; to add a markup point and then programmed a mouse button to type that key. You can also just type ; from the keyboard. Anyway, this shortcut runs a python function that sets the interaction node to placement node, then locks all markups for that node. For whatever reason, it locks all nodes except the one that’s just been placed, but since I place a lot of points in a row, it’s good enough for me.

Locked markups are saved as locked and reloaded as locked. You can look at the JSON file that has the markups and check the “locked” attribute and see that it is set to “on”.

The right way, as described above, is to change the default markup node to be locked. I’ve been unable to get this to work but I haven’t spent enough time debugging it since my very hacky method works for me.

The shortcut function is:

def hh_place_fiducial():
    try:
        interactionNode = slicer.app.applicationLogic().GetInteractionNode()
        interactionNode.SetCurrentInteractionMode(interactionNode.Place)
        mod = slicer.util.getModule("Markups")
        ml = mod.logic()
        n = slicer.util.getNode(ml.GetActiveListID())
        ml.SetAllMarkupsLocked(n, True)
    except AttributeError:
        pass

And to set it as a hotkey/shortcut: (You don’t need the loop below for setting only one, I have a number of shortcuts I’ve defined but edited those out for clarity.)

shortcuts = [
  (';', lambda: hh_place_fiducial())
]

for (shortcutKey, callback) in shortcuts:
    shortcut = qt.QShortcut(slicer.util.mainWindow())
    shortcut.setKey(qt.QKeySequence(shortcutKey))
    shortcut.connect( 'activated()', callback)

(As an aside, this what I have tried for setting the default node to be locked, but it does not work. If anybody’s got ideas on why, any help would be awesome.)

### THIS DOES NOT WORK
defaultMarkupNode = slicer.vtkMRMLMarkupsFiducialNode() 
defaultMarkupNode.SetLocked(1)
slicer.mrmlScene.AddDefaultNode(defaultMarkupNode)

Hi,
Thanks for your reply. I am locking the control points while creating them using the markupnode.SetNthFiducialControlPointLocked(cp_no, True).

regards,
Saima