Handle Markups events from an external module

Hi,

I noticed there is a massive work in progress on the markups widget which affects some modules using markups interaction. As the markup position is now previewed when hovering a slice, “PointAddedEvent” is triggered when entering the view and “PointModifiedEvent” is permanently triggered when hovering the view.

I wanted to use “PointStartInteractionEvent” but it is listed as deprecated in @lassoan recent commit. I had a quick look at the newly created vtkMRMLInteractionEventData.h and the way it is done in the SlicerMarkupsWidget but I can’t retranslate it into a python scripted module.

Thank you.

The API is still being worked on. What would you like to do?

I would like to get a callback function only when a marker is placed and dragged in a view. Basically, the behavior that an observer of “PointModifiedEvent” did before the update and preventing this callback to be called during the preview phase.
I want to use that when you move a markup in one view, so the other views follow. (Double left-click current action)

Also, in this commit you mention that markups should now “Slide on surface”, but can’t find what changed compared to previous versions. Most of the modules I have been working on were using this:

@vtk.calldata_type(vtk.VTK_INT)
def onPointModifiedEvent(self, caller, event, call_data):
    caller.RemoveObserver(tag["PointModifiedEventTag"])

    markupCoord = [0,0,0]
    caller.GetNthFiducialPosition(call_data, markupCoord)
    pointLocator = vtk.vtkPointLocator()
    pointLocator.SetDataSet(self.myModel.GetPolyData())
    pointLocator.AutomaticOn()
    pointLocator.BuildLocator()
    indexClosestPoint = pointLocator.FindClosestPoint(markupCoord)
    self.myModel.GetPolyData().GetPoints().GetPoint(indexClosestPoint, markupCoord)
    caller.SetNthFiducialPositionFromArray(call_data, markupCoord)

    PointModifiedEventTag = obj.AddObserver(obj.PointModifiedEvent, self.onPointModifiedEvent)

You can check the status of a point (undefined/previewed/placed) to distinguish between previewed and already placed state. Let me know if this information is enough.

Jumping slice views while moving a markup seems to be a common need, so we may just add a flag to the markups display widget to enable/disable this.

Also note that now you can click on a markup to jump to it in all slice views. This, combined with the ability to see out-of-plane markup points (enable projection in display settings), tracking the point on other slices might not be as important as before.

Yes, all markups slide on visible surface now. There is a single picker that is shared between all widgets to improve performance.

Oh true, this is done by GetNthControlPointPositionStatus().

The projection helps a lot to keep track of where the markup is, but when the markup slides on a round surface, it may shift to an adjacent slice and this might be confusing.

Anyway, thank you for the tips. I just downloaded today’s nightly version and many issues were solved compared to the one I had last week. I thought more maintenance would be needed for modules using markups but not anymore. I should not try to develop on WIP features.

It is useful if you can test markups module and provide early feedback (what works, what you would need), because we can take those into account when refining the design or prioritizing development of new features.
We’ll keep fixing and improving markups in the coming few months, so for production use, you might want to stick to the latest stable version of Slicer.

Hi Andras. This is a related question so I’m putting it here. We need to fix the SlicerDMRI usage of markups (interactive tractography) so SlicerDMRI can be available again in the nightlies. Specifically, what now replaces these events and calls (below), or where can I best look for an example of something similar?
Thanks!!

MarkupAddedEvent
MarkupRemovedEvent
GetNthMarkupSelected

Slicer5 migration guide should answer these questions, but let me know if it’s not enough.

https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer#Markups

Thanks! I’ll check it out and let you know.

Also let me know if there is any feature that you would like markups to have. Many things that were not feasible to implement with the old design are possible now.

From what I can see here, “Markups.MovingInSliceView” and “Markups.MovingMarkupIndex” are only accessible when hovering the views (preview mode), why not when the point is placed and moved afterward?

“MovingInSliceView” means that the point is moving. Anyway, it is kept for a while for backward compatibility only, since there is now much richer API (that does not rely on custom MRML node attributes) for getting much more information.

Event data is now vtkMRMLInteractionEventData for many events (that contains information of what part of the markup was manipulated in which view node), you can also query active component type and index from the display node, and “active” state is stored in the display node (so you can choose to highlight hovered-over markups in only selected views).

Let me know if you find any inconveniences with the new API. We can make improvements as needed.

I understand which information vtkMRMLInteractionEventData can carry and this is very helpful. When you say event data is vtkMRMLInteractionEventData, do you mean the third argument of the callback function should be that type?
Would I change the callback definition to (following)?

@vtk.calldata_type(vtk.VTK_OBJECT)
def onMarkupModified(self, caller, event, calldata):

And get current view like this:

if isinstance(calldata, slicer.vtkMRMLInteractionEventData):
    calldata.GetViewNode()

Yes, all above is correct.

So far, Slicer crash without error message in the console using @vtk.calldata_type(vtk.VTK_OBJECT) when the mouse hovers a Slice View.

And from Visual Studio Debugger, it says:
error

Anyone tried to replicate this bug? Should I submit it?

Hi @mirclem - Yes, if you have a simple way to reproduce this behavior definitely file an issue and put the link to it here. It would be great to have the snippet of python code to paste in the interactor and any steps needed that lead to the crash.

I was able to reproduce the problem but did not have time to investigate much. The problem must likely is that the event data is not derived from vtkObject but from vtkObjectBase. I don’t know if there is a VTK calldata hint for vtkObjectBase, it would need to be investigated or asked on the VTK forum.

Hi, Ok I will do that then. Here is the code I used to get to this error:

@vtk.calldata_type(vtk.VTK_OBJECT)
def onMarkupChanged(caller,event,call_data):
	print(event)

markupsFiducialNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLMarkupsFiducialNode')
markupsFiducialNode.AddObserver(slicer.vtkMRMLMarkupsFiducialNode.PointModifiedEvent, onMarkupChanged)

selectionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSelectionNodeSingleton")
selectionNode.SetReferenceActivePlaceNodeID(markupsFiducialNode.GetID())
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")
interactionNode.SetCurrentInteractionMode(1)
interactionNode.SetPlaceModePersistence(0)

Then, just mouse over any views.