问题
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?
回答1:
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.
回答2:
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.)
回答3:
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()
回答4:
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