Hi, I’m exploring how the vtk server “event system” works.
I would like to ask a question about the “InvokeEvent” operation (server side).
Into the “protocols.py” there is the “mouseInteraction” method which has the following line:
Into the class “vtk_override_protocols.py” the event is intercepted by the method “addRenderObserver” which prepare the “callback” in the following way (click here to see the code on github):
observerCallback = lambda *args, **kwargs: self.pushRender(realViewId)
tag = self.getApplication().AddObserver('UpdateEvent', observerCallback)
So in that way each time the “UpdateEvent” is fired, the “pushRender” is invoked. That’s great!
My question is:
How can I send a value throught the “InvokeEvent” call?
Basically I need to pass a timestamp, so for example:
timeStamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
And I would like to read the value in the “pushRender” method.
Is that possible? Here I found some hints, but this is for the c++ staff, not for the python ones.
Thanks and regards
You can pass a few simple data types and objects that are derived from
vtkObject class by using
@vtk.calldata_type decorator. See detailed information here: https://www.slicer.org/wiki/Documentation/Nightly/Developers/FAQ#How_can_I_access_callData_argument_in_a_VTK_object_observer_callback_function
@dgobbi is there a user documentation of VTK’s Python wrapper? I couldn’t find a relevant section in in VTK API documentation and full-text search is not enabled, so I couldn’t even see if “vtk.calldata_type” is mentioned.
Thank you Andras,
at the end I figurated it out.
Thank you anyway for sharing me a couple of useful links and documentations.
Still have a couple of issues when passing the complex objects, but I will (hopefully) fix them in the next days.
just a quick question: I would like to pass a string or an vtk_object to the callback.
At the moment only a simple integer has been sent as value.
I tried to use VTK_STRING but the program unexpectedly exited (so I think an error has accured).
I really can’t figure out why is not working.
I just substituted the @vtk.calldata_type(vtk.VTK_LONG) with @vtk.calldata_type(vtk.VTK_STRING).
Is there something wrong here?
@dgobbi is there a way to pass a string from Python as event data?
@Ennio_Bolondi until you figure out how to send a single string, you can use vtkStringArray object.
Sure, it’s the example in the doc string for
Help on function calldata_type in vtk:
vtk.calldata_type = calldata_type(type)
set_call_data_type(type) -- convenience decorator to easily set the CallDataType attribute
for python function used as observer callback.
import vtkmodules.vtkCommonCore import vtkCommand, vtkLookupTable
def onError(caller, event, calldata):
print("caller: %s - event: %s - msg: %s" % (caller.GetClassName(), event, calldata))
lt = vtkLookupTable()
Or without bothering to import, you can use a string name for the type:
def vtkErrorEventHandler(caller, event, calldata):```
Thank you Eric and Andreas, ok I’ll give a second chance.
I wrote the same code you have in the example above, but maybe I misspelled it.
Thanks and regards
Oops, I didn’t add anything to what you already knew. Do you know the C++ type of the object being passed, or is all your code pure Python? I’m guessing that mapping the object to
VTK_LONG is simply casting and sending the address of whatever object is coming out of the C++ call, which is why it works. What’s the value of that parameter on the receiving end when you use
More importantly, does
InvokeEvent have a second parameter? I can’t find its definition to verify, but all the examples I’ve scanned simply allow a single, event name parameter.
I’m getting it to work in a little example. Maybe start here and work up?
def vtkErrorEventHandler(object, event, callData):
print(object, event, callData)
o = vtk.vtkObject()
print(o.InvokeEvent('Blah', 'Hello, world!'))
Modified Time: 62
Reference Count: 1
NoEvent Hello, world!
Ok finally it worked.
I figured out what was the problem: I was passing a python object which was “”“containing”"" a string.
In other words: I didn’t “typitize” it as a string.
So in my final solution I used:
self.getApplication().InvokeEvent('UpdateEvent', str(stringArgument)) # WORKING SOLUTION
So finally it’s working with @vtk.calldata_type(vtk.VTK_STRING)
Thank you so much!
What’s the value of that parameter on the receiving end when you use
I printed it out and it is: ‘8’
“VTK_STRING” is: ‘13’
And I’m happy to say that it’s working with the following as well:
@vtk.calldata_type('string0') # THIS ONE WORKS AS WELL :-)
last question: I was wondering if there is a way to pass an Object (maybe it could be usefull in the future). In the meanwhile I can use a string array as “Andreas” suggested.
Hi Andras, I’ve been on vacation and haven’t been able to read through this thread yet, @jcfr implemented parts of the python event code and he might be able to answer.
Thank you, I think @Ennio_Bolondi figured it out.
You can pass any objects derived from vtkObject.
If you want to pass custom Python objects, then you can serialize it to string: create a json representation manually; or use pickle to get a byte array and convert to string using base-64 encoding.
Thank you to everyone.
I think I’m definitely ready to master the callbacks arguments
Really appreciate the time and patience you put in this thread
Ennio, my understanding of
calldata_type came from looking at its implementation in util/misc.py. Here’s the part I find the most interesting:
supported_call_data_types = ['string0', vtkCommonCore.VTK_STRING,
vtkCommonCore.VTK_LONG, vtkCommonCore.VTK_DOUBLE, vtkCommonCore.VTK_FLOAT]
Ennio, in the VTK C++ source, the second parameter (
callData) is a
void*. From the VTK Python Wrappers docs:
void* pointer can accept a pointer in two different ways: either from an Python object that supports the Python buffer protocol (this includes all numpy arrays along with the Python bytes and bytearray types), or from a string that contains a mangled pointer for the form
'hhhhhhhhhhhh' is the hexadecimal address. Return-valid
void* will always be a string containing the mangled pointer.
This is why wrapping it in a
str() worked: you called the object’s
__str__() that returned its string representation.
- Python 3
bytes = Python 2.7
- Python 3
str = Python 2.7
str works for me as well.