Both CControl and EventArgs defined in pxd file.
So basically, I want to execute a python function in Button’s wndproc function. The wndproc of this button is in C world. So I want to pass a python function from cython world to C world.
If pfunc is an arbitrary Python function (with EventArgs as arguments), then I think you need a bridge between that Python object (pfunc) and the EventHandler type. One way might be to add a C-level wrapper function (with EventHandler signature) that when called executes the python level function using PyObject_CallObject.
I would be kind of wary of using ctypes for this, because of this warning
Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.
Also, note that if the callback function is called in a thread created outside of Python’s control (e.g. by the foreign code that calls the callback), ctypes creates a new dummy Python thread on every invocation.
Thank you for the reply. I did that once but faced a dead end at WINFUNCTYPE. But yeah, I think I can use ctypes directly from python for this purpose. setClickHandler can be called with a WINFUNCTYPE object. But it’s burden for my users. They need to set the argtypes and restype for this function in their scripts.
Yes, that’s good to read anyway - also if you’re working with Cython or ctypes.
I’ve written my own extension module code in the distance past (even for Python 1.6 when Python didn’t have nice macros yet for dealing with the Python thread state). The main thing for asynchronous callbacks from C to Python (as here) was that the C-level code needed to create a fake PyThreadState, and then also needed to get hold of the GIL before it could safely execute Python code. (Not sure how this works in the current CPython, but those are the things you still need to look out for, I think.)
I would worry about ctypes if that creates a new thread for every button click… (But might be good as initial prototype code.)
That code was actually proprietary, so I cannot share it (and no longer have access). But let me see if I can come up with a skeleton for this (I’ll have to think a bit and double-check.)
Or perhaps someone else has something available: What is wanted is an asynchronous callback from C into Python that executes an arbitrary python function (with fixed argument types). So, this also implies either a registration function to register the python function for the C code or another way to retrieve the function (registration is in principle better, since it allows more control).
find is called from python. And f is the user’s python function. But f is converted to void* and passed as user_data in find_cheeses. In my case, I need to pass f as user_func parameter. That’s confusing me.
I was just reminding myself about that. So, typically when a library supports callbacks, it will add an extra void* argument (to the registration function). The design of any kind of generic callback almost makes this unavoidable. The extra argument can be used to pass user data, or extra context, so the user can actually do sth with that. In this case, you need to be able to pass the actual Python function (Python object) into the C code - and it needs to be converted into a void* just to satisfy the function signature (the C code in principle doesn’t need to known about Python).
The void callback(...) function here is the C-level wrapper for the inner Python function.
In this case, find is not called asynchronously though - it will be called from the main Python thread.
Yeah, registration is what I am trying to do. I can easily convert a Cython cdef function to an EventHandler. But I can’t execute a python function inside it.
tkinter.init_register functions register user-written Python callback functions with C-coded tcl/tk for use with tk objects. They usually wrap the callback with a tkinter CallWrapper, then create a cbname as a handle, then create a tcl function with tk.createcommand, which must be somewhere in _tkinter.c. Perhaps these code pieces can help.