Receiving data in python callback function from dll - python

I am writing a program in Python that communicates with a spectrometer from Avantes. There are some proprietary dlls available whose code I don't access to, but they have some decent documentation. I am having some trouble to find a good way to store the data received via callbacks.
The proprietary shared library
Basically, the dll contains a function that I have to call to start measuring and that receives a callback function that will be called whenever the spectrometer has finished a measurement. The function is the following:
int AVS_MeasureCallback(AvsHandle a_hDevice,void (*__Done)(AvsHandle*, int*),short a_Nmsr)
The first argument is a handle object that identifies the spectrometer, the second is the actual callback function and the third is the amount of measurements to be made.
The callback function will receive then receive another type of handle identifying the spetrometer and information about the amount of data available after a measurement.
Python library
I am using a library that has Python wrappers for many equipments, including my spectrometer.
def measure_callback(self, num_measurements, callback=None):
self.sdk.AVS_MeasureCallback(self._handle, callback, num_measurements)
And they also have defined the following decorator:
MeasureCallback = FUNCTYPE(None, POINTER(c_int32), POINTER(c_int32))
The idea is that when the callback function is finally called, this will trigger the get_data() function that will retrieve data from the equipment.
The recommended example is
#MeasureCallback
def callback_fcn(handle, info):
print('The DLL handle is:', handle.contents.value)
if info.contents.value == 0: # equals 0 if everything is okay (see manual)
print(' callback data:', ava.get_data())
ava.measure_callback(-1, callback_fcn)
My problem
I have to store the received data in a 2D numpy array that I have created somewhere else in my main code, but I can't figure out what is the best way to update this array with the new data available inside the callback function.
I wondered if I could pass this numpy array as an argument for the callback function, but even in this case I cannot find a good way to do this since it is expected that the callback function will have only those 2 arguments.
Edit 1
I found a possible solution here but I am not sure it is the best way to do it. I'd rather not create a new class just to hold a single numpy array inside.
Edit 2
I actually changed my mind about my approach, because inside my callback I'd like to do many operations with the received data and save the results in many different variables. So, I went back to the class approach mentioned here, where I would basically have a class with all the variables that will somehow be used in the callback function and that would also inherit or have an object of the class ava.
However, as shown in this other question, the self parameter is a problem in this case.

If you don't want to create a new class, you can use a function closure:
# Initialize it however you want
numpy_array = ...
def callback_fcn(handle, info):
# Do what you want with the value of the variable
store_data(numpy_array, ...)
# After the callback is called, you can access the changes made to the object
print(get_data(numpy_array))
How this works is that when the callback_fcn is defined, it keeps a reference to the value of the variable numpy_array, so when it's called, it can manipulate it, as if it were passed as an argument to the function. So you get the effect of passing it in, without the callback caller having to worry about it.

I finally managed to solve my problem with a solution envolving a new class and also a closure function to deal with the self parameter that is described here. Besides that, another problem would appear by garbage collection of the new created method.
My final solution is:
class spectrometer():
def measurement_callback(self,handle,info):
if info.contents.value >= 0:
timestamp,spectrum = self.ava.get_data()
self.spectral_data[self.spectrum_index,:] = np.ctypeslib.as_array(spectrum[0:pixel_amount])
self.timestamps[self.spectrum_index] = timestamp
self.spectrum_index += 1
def __init__(self,ava):
self.ava = ava
self.measurement_callback = MeasureCallback(self.measurement_callback)
def register_callback(self,scans,pattern_amount,pixel_amount):
self.spectrum_index = 0
self.timestamps = np.empty((pattern_amount),dtype=np.uint32)
self.spectral_data = np.empty((pattern_amount,pixel_amount),dtype=np.float64)
self.ava.measure_callback(scans, self.measurement_callback)

Related

Passing additional reference parameters as arguments to a callback object in a class

I am using a library that takes some keyword arguments during the initialization of the object. These keyword arguments define the callback objects/functions that are called when a specific event occurs. The callback objects themselves need to reference the object that called them and have some additional data that comes from the class/object instance itself.
What I would like to do is pass a reference to another object into one of these call backs when initializing in order to avoid using a global variable.
How/where would I pass the reference to the object? Do I need to subclass? If I do need to subclass can I accomplish this without understanding the working behind the class/library? I tried looking at the source to try and figure something out but unfortunately it's a bit past my understanding.
Example which will hopefully get my point across:
def One(eI):
*...do some stuff...*
def Two(eI, data, ref_I_want_to_add):
*do some other stuff*
ref_I_want_to_pass = anotherClass(diff_arg,diff_arg2):
*...you know the deal...*
eI = exampleClass(argOne=One, ..., argN=N)
The first thing that comes to mind is to use something like
eI = exampleClass(argOne=One, argTwo=Two(ref_I_want_to_pass), ..., argN)
But that obviously doesnt work because I get a TypeError: argTwo takes 0 positional arguments but 1 was given
More precisely I am using the websocket client library and in order to create a WebSocketApp object which handles the connection you instaniate the object with something like
ws = websocket.WebSocketApp(uri, on_open=on_open, on_message=on_message, on_ping=on_ping)
now I have a function named on_message that handles the incoming messages and I told the ws object during instantiation of WebSocketApp that I would like to use that function to handle the incoming messages or rather what to do with them.
I would like to update another object when a new message comes in, now I can of course use a global variable for that but it doesn't seem like the right way to do it and it would be nice if I could pass a reference to that object when initializing a WebSocketApp object, with something like:
ws = websock.WebSocketApp(uri, on_message=on_message(ref_to_Obj), on_open=on_open, on_ping=on_ping)
But obviously that doesnt work because again I get a TypeError: argTwo takes 0 positional arguments but 1 was given error.
I'm not entirely sure how to do this. Subclassing comes to mind but even then I'm kind of lost, I will admit that subclassing is a topic I need more work on, I get the idea behind it and can do all the basic examples they use in tutorials but more complex classes can stump me.
If you want a local variable to be passed onto a function, you need to create a closure around that variable. We can do that with lambda.
eI = exampleClass(argOne=One, ..., argN=lambda x, y: N(x, y, ref_I_want_to_pass))

MDataHandle.setFloat() isn't changing Plug value

Using the Maya Python API 2.0, I'm trying to make a callback that changes the value of a plug. However, none of the methods I've tried are working.
I've tried using the MPlug.setFloat() method, but this didn't lead to expected results; I found no change in the plug's value. I figured this hadn't worked because I needed to clean the plug after changing its value. So, I then tried getting the plug's data handle using the MPlug.asDataHandle() method, then using the data handle's datablock() method in order to use the data handle and datablock to set the plug's value and clean it. However, I got an error saying "RuntimeError: (kFailure): Unexpected Internal Failure" upon using MDataHandle.datablock().
Now I'm trying the following, which uses the data handle to set the plug's value and clean it:
def setPlugFloatValue(node, plugName, val):
fnSet = OpenMaya.MFnDependencyNode(node)
plug = fnSet.findPlug(plugName,True)
handle = plug.asMDataHandle()
handle.setFloat(val)
handle.setClean()
The above function is intended to find a certain plug in a node, then use its data handle to set its value and clean it. In my program, the callback uses this function to change the translateX, translateY and translateZ plugs of a node's child nodes. The callback runs when the translate value of the node it's applied to changes. In a scene I'm using to test this callback, I apply the callback to a polygon mesh object, with one child which is also a polygon mesh object. So, as I translate the parent object, I expect the translate values of its child to change. But when I select the child object after translating its parent, its translate values haven't changed.
Tried your example and used setFloat() on the plug, which appears to work fine.
import maya.api.OpenMaya as OpenMaya
def setPlugFloatValue(node, plugName, val):
fnSet = OpenMaya.MFnDependencyNode(node)
plug = fnSet.findPlug(plugName,True)
plug.setFloat(val)
def applyToSelectedObjects():
sl_list = OpenMaya.MGlobal.getActiveSelectionList()
iterator = OpenMaya.MItSelectionList(sl_list)
while not iterator.isDone():
obj = iterator.getDependNode()
setPlugFloatValue(obj, "translateX", -2.0)
iterator.next()
applyToSelectedObjects()
Perhaps your issue is something else? You can also try to use setMDistance() instead, but it didn't make any difference in my testing.
distance = OpenMaya.MDistance(val)
plug.setMDistance(distance)

How to pass a result from a function that requires GUI input (python)?

I'm writing a python script that takes user input through a GUI and then passes that along to a function that basically reads through text files and checks that what the user requested is there. This function returns an array with True or False for each check.
I want to use this array in a different function (def markup()), but if I call it without giving the function the user input, I get an error.
Is there a way for me to store the results of this function and pass it without needing the user input each time?
Pseudo code:
def clickButton():
userInput = [A,B,C,D,E]
textCheck(userInput)
def textCheck(userInput):
*code for checking text creates an array named allResults*
return allResults
def markup():
results = textCheck()
print(results)
You need to manage allResults as a persistent object. One way is to pass the results everywhere as a parameter, such that whatever thread is executing always has a handle to the list. Another way is to (shudder) make it a global variable -- this is somewhat dangerous as a habit, but may be the easiest for you to implement and maintain. You can also create a Results class and instantiate an object that persists as long as you need it.
I can't recommend one over another without having the flow of the main program.
I ended up calling markup(allResults) at the end of textChecker(). It worked.

Callback to method in Python

I'm just starting to learn Python and I have the following problem.
Using a package with method "bind", the following code works:
def callback(data):
print data
channel.bind(callback)
but when I try to wrap this inside a class:
class myclass:
def callback(data):
print data
def register_callback:
channel.bind(self.callback)
the call_back method is never called. I tried both "self.callback" and just "callback". Any ideas?
It is not clear to me how your code works, as (1) you did not post the implementation of channel.bind, and (2) your second example is incorrect in the definition of register_callback (it is using a self argument that is not part of the list of parameters of the method, and it lacks parentheses).
Nevertheless, remember that methods usually require a "self" parameter, which is implicitly passed every time you run self.function(), as this is converted internally to a function call with self as its first parameter: function(self, ...). Since your callback has just one argument data, this is probably the problem.
You cannot declare a method bind that is able to accept either a function or a class method (the same problem happens with every OOP language I know: C++, Pascal...).
There are many ways to do this, but, again, without a self-contained example that can be compiled, it is difficult to give suggestions.
You need to pass the self object as well:
def register_callback(self):
channel.bind(self.callback)
What you're doing is entirely possible, but I'm not sure exactly what your issue is, because your sample code as posted is not even syntactically valid. (The second method has no argument list whatsoever.)
Regardless, you might find the following sample code helpful:
def send_data(callback):
callback('my_data')
def callback(data):
print 'Free function callback called with data:', data
# The follwing prints "Free function callback called with data: my_data"
send_data(callback)
class ClassWithCallback(object):
def callback(self, data):
print 'Object method callback called with data:', data
def apply_callback(self):
send_data(self.callback)
# The following prints "Object method callback called with data: my_data"
ClassWithCallback().apply_callback()
# Indeed, the following does the same
send_data(ClassWithCallback().callback)
In Python it is possible to use free functions (callback in the example above) or bound methods (self.callback in the example above) in more or less the same situations, at least for simple tasks like the one you've outlined.

Is it possible to retrieve the content of Qt Signals with a lambda function?

I'm working on a PySide based App in which I continously get values and want to put them onto the GUI.
When I receive a value (I receive them via a CAN device using the PCANBasic library) I convert him to an int and emit him via the .emit() attributte of PySide.QtCore.Signal
Signal = PySide.QtCore.Signal(int)
# as soon as a new value is received and processed
Signal.emit(new_value)
Now I try to display my new_value on a PySide.QtGui.QSlider, thats what I do at the moment:
my_slider = PySide.QtGui.QSlider()
Signal.connect(change_slider_value)
# with a simple helper function
def change_slider_value(value):
my_slider.setValue(value)
What I wanna do is:
Signal.connect(lambda value = data : my_slider.setValue(value))
With data being the what I emited with Signal (I'd love to somehow mark it, but the formating disappeaered on me and its my first post -.-)
When I test this I get the following Traceback:
self.calibrate.bar_val_signal.connect(lambda value = data: self.UI.calibrate.ctrl.Bar.setValue(value)) # self.change_bar_value)
NameError: global name 'data' is not defined
(You see the program is probably somewhat more complicated)
Translated to our pseudo code it would probably look like this:
Signal.connect(lambda value = data: my_slider.setValue(value))
NameError: global name 'data' is not defined
In my opinion the issue is that the lambda function can't get the value out of the signal.
Has anybody a idea if there's a possibility to work without the need for a helper function.
Thanks in advance
You don't need to use a lambda. Since your change_slider_value function only takes the argument that your signal would emit, you can just connect the signal to that.
Signal.connect(change_slider_value)
But as for why your lambda wasn't working, think of data as the parameter of a function. data will contain whatever the lambda is called with, therefore you could just do this, omitting 'value':
Signal.connect(lambda data: my_slider.setValue(data))
But I would suggest using the first solution, unless your parameters for chang_slider_value change.

Categories

Resources