Create, invoke and observe vtk custom event

Hi all,
I’m using the function SetParameter() of a vtkMRMLScriptedModuleNode object.
From a different class I observe that node and I run a function if the parameter changes…all run smoothly right so far, since I have a single parameter to take care of.

What about if I need to set two parameters (not at the same time) and I want to get a different notification for each modified parameter?

Do I have to create, invoke and observe two different vtk custom events?

If so, do you have a python snippet of code to suggest?

Thank you!

Paolo

Hi Paolo -

Good point - right now you get the same ModifiedEvent no matter what parameter changed. You would need to keep track of the old values and check which one had changed.

Currently we just call Modified in SetParameter, but we could change that to be this->InvokeEvent(vtkCommand::ModifiedEvent, name)

Then you should be able to get the parameter using the method described here

Hi Steve,
thanks for helping.

Let me explain better my use case, maybe I do not need two events.
We are working on arduino connection layer, the COVID19 situation slowed down the work, but we are confident to release something fully working soon.

I have my extension and I created a dedicated node. When a message is received from arduino I set it into the node and an event is fired as well. If one or more classes (e.g. something built on top of connection layer) are observing the node, they will be notified. I already did this and it works very well.
In this case I need an event since I do not know when a message will be received.

At the same time I need also to send a message from Slicer to the board.
The idea was to observe the node in the opposite direction and, in case, run the proper function into my logic extension. That’s why I asked for two events.
Actually I do not need an event, because I know when I want to send a message.
Which is the best way of exposing a function from my logic to someone that wants to send a message form his own piece of code?

Something “easy and raw” could be put in my logic something like:
slicer.sendMessageToArduino = self.sendMessage()
but I mean, it’s quite brutal!

Thanks a lot!
Paolo

Hi Paolo - Ah, yes, I see what you mean.

Well, in this case maybe just do something like:

import Arduino
Arduino.ArduinoLogic(arduinoNode).sendMessage("ciao!")
1 Like

Thanks @pieper!
It is exactly what I was looking for!

Just a little edit: the node should be not given

This works:

import Arduino
Arduino.ArduinoLogic().sendMessage(“ciao!”)

Thanks again!

1 Like

Hi @pieper,
actually I didn’t reach the point, sorry.

By using

Arduino.ArduinoLogic().sendMessage(“ciao!”)

I have access to the generic function, but I actually need to access the method of an instance of the class.

When I run the module (by UI) I create an object of the logic class and then I establish a connection (with all the required parameters). I need to access the function of this specific object, not to the (static) method of the class.
That’s why I thought the extremely raw was of putting into the logic constructor

slicer.sendMessageToArduino = self.sendMessage()

Thanks for helping!

Hi Paulo -

Right - that’s what I was thinking in my original suggestion, to include an instance of your node in the constructor of the logic class. That way the node could provide the “context” so that the sendMessage could determine the right connection to use, perhaps by storing a connection ID as a parameter in the node.

Do you have your code available on line?

I’m afraid that a connection ID is not enough.
Yes, the code is here

Thanks a lot!

Are you planning to have multiple connections to different devices? If so, then your module can maintain a mapping from a connection ID, also stored in the node, and any other info you need, like file descriptor, state, or device config. Maybe make a helper class for all that and then in the logic you just have a dictionary that tracks them by ID.

By the way, did you try using QSerialPort instead of python serial? If you did you could replace all the polling with signal/slot event driven code that would be more efficient and also probably scale better.

Right now no, I’m not planning to support multiple devices (maybe in the future).
Thanks for the qt solution…it’s incredible, qt has a solution for whatever problem!

Regarding the ID in the node, as far as I know, it is not enough for reading/writing, since I have to access the object.
That’s why I thought to two events or to the raw top level exposure of the method during the object initialization.

Another raw solution could be to rely on a single event, store the action into the node parameter, and inspect the parameter to figure out which function has to be fired.
Something like:

  • “READ:50” if 50 has been read
  • “WRITE:100” if I want to write 100

but, I mean, it’s a huge huge workaround!

Hmm, I’m not seeing where the issue is. It seems to me that if you have code that is build on top of the Arduino infrastructure you can have it access the methods of the Logic class as needed. Can you describe the use case scenario a bit more?

I have the logic where I run the arduino connection.
When the data (a string) is received, I exposeit to the Slicer environment by putting it into a node parameter. In this way someone else interested in reading data (e.g. to built another module) can have access to the data just observing the node.

The problem is if the user (not me) wants to write a string from whatever point of slicer (again, for example, from his own module) to arduino. I have the function to send the data, but it’s “locked” into the instantiated logic object. I should expose this inner function of the instantiated logic object in order to be reachable from whatever point.
That’s why I suggested (even if is not elegant) to punt into the constructor of the logic a line like:

slicer.sendMessageToArduino = self.sendMessage

In this way the user ha just to run

slicer.sendMessageToArduino(“test”)

to write the message.

Sorry for being not so linear!

Module logics are available from other modules. In scripted modules, typically the logic is instantiated by the widget (we did not want to make the module manager to instantiate it, as it would have made module reload more complicated). This is how is done in current scripted module template. You can access it by something like this: slicer.modules.mymodule.widgetRepresentation.self().logic. See some more information in the PerkLab Slicer programming tutorials.

1 Like

Thanks!
That’s what I was looking for!

1 Like