使用Cython封装包含wxString的C++类

3 投票
2 回答
770 浏览
提问于 2025-04-16 19:41

我正在开发一个Python扩展,用来和一个用wxWidgets写的C++应用程序连接,这个应用程序有图形用户界面(GUI)。我使用的是Cython,现在基本的系统(构建工具,还有一个带有适当版本信息的初始扩展)都能正常工作。

我主要想实现一些后台功能(不涉及GUI),比如文件解析和处理。不过,所有的类——不仅仅是GUI相关的类——都使用了wxString来处理字符串数据,比如下面这个简单的例子:

#include <wx/string.h>

class MyClass{
    wxString name;
    wxString get_name(){
        return this->name;
    }
};

我想问的是,怎么才能最好地处理这样的类?有没有简单的方法可以让Python字符串和wxString实例之间进行转换?还是说我需要把wxString类也封装一下?我能不能和wxPython的版本结合起来,避免重复造轮子?

2 个回答

0

我会尝试的第一种方法是使用wxString的构造函数:

wxString(const wxChar* psz, size_t nLength = wxSTRING_MAXLEN)

然后把const char*类型的字符串传给它,这样就可以创建一个对象。

接着可以写一些简单的函数,把Python字符串转换成wxString,或者把wxString转换回Python字符串。

PyObject* PyString_FromStringAndSize(const char *v, Py_ssize_t len)

不过我看到的一个缺点是,这样一来,字符串可能会在wxString和Python两个地方各自存在一份,造成重复。

第二种方法是创建一个wxString的子类,并重新实现所有操作,让它使用Python的PyString对象的字符缓冲区。Cython可以帮助你编写这样的子类。

2

我通过使用静态的 wxString::FromUTF8() 函数来把 Python 的字符串转换成 wxString,反过来用 wxString.ToUTF8() 来转换回去,这样就搞定了。下面是我写的代码:

# Import the parts of wxString we want to use.
cdef extern from "wx/string.h":
    cdef cppclass wxString:
        char* ToUTF8()


# Import useful static functions from the class.
cdef extern from "wx/string.h" namespace "wxString":
   wxString FromUTF8(char*)


# Function to convert from Python string to wxString. This can be given either
# a unicode string, or a UTF-8 encoded byte string. Results with other encodings
# are undefined and will probably lead to errors.
cdef inline wxString from_python(python_string):
    # If it is a Python unicode string, encode it to a UTF-8 byte string as this
    # is how we will pass it to wxString.
    if isinstance(python_string, unicode):
        byte_string = python_string.encode('UTF-8')

    # It is already a byte string, and we have no choice but to assume its valid
    # UTF-8 as theres no (sane/efficient) way to detect the encoding.
    else:
        byte_string = python_string

    # Turn the byte string (which is still a Python object) into a C-level char*
    # string.
    cdef char* c_string = byte_string

    # Use the static wxString::FromUTF8() function to get us a wxString.
    return FromUTF8(c_string)


# Function to convert a wxString to a UTF-8 encoded Python byte string.
cdef inline object to_python_utf8(wxString wx_string):
    return wx_string.ToUTF8()


# Function to convert a wxString to a Python unicode string.
cdef inline object to_python_unicode(wxString wx_string):
    # Since the wxString.ToUTF8() method returns a const char*, we'd have to try
    # and cast it if we wanted to do it all in here. I've tried this and can't
    # seem to get it to work. But calling the to_python_utf8() function
    # means Cython handles the conversions and it all just works. Plus, since
    # they are defined as inline functions this may well be simplified down when
    # compiled.
    byte_string = to_python_utf8(wx_string)

    # Decode it to a unicode string and we're done.
    return byte_string.decode('UTF-8')

简单来说,把这个放在一个 .pxd 文件里(我个人是放在一个子目录下,命名为 wx/string.pxd - 如果你也这样做,记得还要创建一个 wx/__init__.pdx 文件)。然后用 cimport 导入它,并根据需要调用这些函数:

cimport wx.string

wx_string = wx.string.from_python(python_string)
python_string = wx.string.to_python_unicode(wx_string)

撰写回答