Sometimes I need to call a gtk/gobject function that only exists in C, but returns an object that has a python wrapper. Previously I used a solution based on ctypes that worked well:
http://faq.pygtk.org/index.py?req=show&file=faq23.041.htp
Now that I swiched from PyGtk ("import gtk") to GObject-introspection ("from gi.repository import Gtk"), what can I use instead?
The _PyGObject_API
interface has changed at some point. I needed to drop the register_sinkfunc
function. The following works:
from gi.repository import Gio, GLib
import gi
import ctypes
class _PyGObject_Functions(ctypes.Structure):
_fields_ = [
('register_class',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
ctypes.c_int, ctypes.py_object,
ctypes.py_object)),
('register_wrapper',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
('lookup_class',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
('newgobj',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
]
class PyGObjectCPAI(object):
def __init__(self):
PyCObject_AsVoidPtr = ctypes.pythonapi.PyCObject_AsVoidPtr
PyCObject_AsVoidPtr.restype = ctypes.c_void_p
PyCObject_AsVoidPtr.argtypes = [ctypes.py_object]
addr = PyCObject_AsVoidPtr(ctypes.py_object(
gi._gobject._PyGObject_API))
self._api = _PyGObject_Functions.from_address(addr)
def pygobject_new(self, addr):
return self._api.newgobj(addr)
capi = PyGObjectCPAI()
To get an object from a pointer:
obj = capi.pygobject_new(pointer)
to get a pointer from a (g)object:
pointer = hash(obj)
I have to add, in my case this didn't help me solve my actual problem. I was trying to interface with dconf, but dconf returns values of type GVariant, which does not inherit from GObject. It seems PyGI/GObject unfortunately does not expose the neccessary functions to turn a C (*GVariant) into a Python GLib.Variant. I guess it's of those times when you have to throw away your initial approach and try something different.
The code in jdm's answer isn't compatible with Python 3. Since CObject
is deprecated in Python 2.7 and 3.1, and removed starting with 3.2, I tweaked the code to use Capsule
(available in 2.7 and since 3.1):
import ctypes, gi
class _PyGObject_Functions(ctypes.Structure):
_fields_ = [
('register_class',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
ctypes.c_int, ctypes.py_object, ctypes.py_object)),
('register_wrapper',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
('lookup_class',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
('newgobj',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
]
class PyGObjectCAPI(object):
def __init__(self):
self._as_void_ptr.restype = ctypes.c_void_p
self._as_void_ptr.argtypes = [ctypes.py_object]
addr = self._as_void_ptr(ctypes.py_object(
gi._gobject._PyGObject_API))
self._api = _PyGObject_Functions.from_address(addr)
@staticmethod
def _as_void_ptr(obj):
name = ctypes.pythonapi.PyCapsule_GetName(obj)
return ctypes.pythonapi.PyCapsule_GetPointer(obj, name)
def pygobject_new(self, addr):
return self._api.newgobj(addr)
capi = PyGObjectCAPI()
(I also renamed it to PyGobjectCAPI -- not sure if CPAI stood for something, but it made more sense to me this way.)
PyGOject api changed since AlliedEnvy update
import gi
import ctypes
from ctypes import pythonapi
class _PyGObject_Functions(ctypes.Structure):
_fields_ = [
('pygobject_register_class',
ctypes.PYFUNCTYPE(ctypes.c_void_p)),
('pygobject_register_wrapper',
ctypes.PYFUNCTYPE(ctypes.c_void_p)),
('pygobject_lookup_class',
ctypes.PYFUNCTYPE(ctypes.c_void_p)),
('pygobject_new',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
]
class PyGObjectCAPI(object):
def __init__(self):
addr = self._as_void_ptr(gi._gobject._PyGObject_API)
self._api = _PyGObject_Functions.from_address(addr)
@classmethod
def _capsule_name(cls, capsule):
pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p
pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object]
return pythonapi.PyCapsule_GetName(capsule)
@classmethod
def _as_void_ptr(cls, capsule):
name = cls._capsule_name(capsule)
pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
pythonapi.PyCapsule_GetPointer.argtypes = [
ctypes.py_object, ctypes.c_char_p]
return pythonapi.PyCapsule_GetPointer(capsule, name)
def to_object(self, addr):
return self._api.pygobject_new(addr)
capi = PyGObjectCAPI()
With the advent of instrospection files (.typelib
, .gir
), the same API should be available regardless of the language being used, that is, if you're using a C function not in the API, you're probably using a function intended to be used only internally.
来源:https://stackoverflow.com/questions/8668333/create-python-object-from-memory-address-using-gi-repository