使用gi.repository从内存地址创建Python对象

8
有时我需要调用一个只存在于C语言中的gtk/gobject函数,但返回一个具有Python包装器的对象。 以前我使用了一个基于ctypes的解决方案,效果很好:http://faq.pygtk.org/index.py?req=show&file=faq23.041.htp。现在我已经从PyGtk(“import gtk”)转换到GObject-introspection(“from gi.repository import Gtk”),我应该使用什么替代方法?
4个回答

8

_PyGObject_API接口已经发生了变化。我需要删除register_sinkfunc函数。以下是可以使用的代码:

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()

从指针获取一个对象:
obj = capi.pygobject_new(pointer)

从(g)对象获取一个指针的方法:

pointer = hash(obj)

我必须提醒的是,对于我的实际问题来说这并没有帮助我解决。我正在尝试与dconf接口,但是dconf返回的值是GVariant类型,它不继承自GObject。看起来PyGI/GObject不幸地没有暴露将C(* GVariant)转换为Python GLib.Variant所需的函数。我想这是那种时候,你必须放弃初始的方法,尝试一些不同的方法。


4
jdm的回答中的代码不兼容Python 3。由于CObject在Python 2.7和3.1中已被弃用,并且自3.2开始删除,我调整了代码以使用Capsule自2.7以来可用)。
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()

我还将其重命名为PyGobjectC API--不确定CPAI代表什么,但这样对我来说更有意义。


2

AlliedEnvy的更新以来,PyGOject API已发生变化。

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()

0
随着内省文件(.typelib.gir)的出现,无论使用哪种语言,相同的API都应该可用。也就是说,如果您正在使用不在API中的C函数,则可能正在使用仅供内部使用的函数。

2
理论上是正确的。然而,我想使用libdconf中的一个函数,但我的操作系统(Ubuntu Oneric)目前没有绑定该函数。此外,在处理返回指向GObject的指针的自定义库(不属于Gtk&co.的一部分)时,仍可能有用处。 - jdm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接