Python 方法解析之谜
我搞不明白为什么这个程序会出错。
#!/usr/bin/env python
from __future__ import division, print_function
from future_builtins import *
import types
import libui as ui
from PyQt4 import QtCore
import sip
p = ui.QPoint()
q = QtCore.QPoint()
def _q_getattr(self, attr):
print("get %s" % attr)
value = getattr(sip.wrapinstance(self.myself(), QtCore.QPoint), attr)
print("get2 %s returned %s" % (attr, value))
return value
p.__getattr__ = types.MethodType(_q_getattr, p)
print(p.__getattr__('x')()) # Works! Prints "0"
print(p.x()) # AttributeError: 'QPoint' object has no attribute 'x'
我用了Boost.Python来创建libui,这个库里有一个叫QPoint的类。我还引入了PyQt4,它里面也有一个sip暴露的QPoint。我想把这两种类型做个映射。
我检查过p
是一个新式类,那为什么p.x()
没有调用__getattr__
呢?
3 个回答
1
这是一个关于如何将 PyQt4 和 boost::python 集成的例子。
首先,我们需要定义一些函数来处理裸指针,也就是直接指向内存地址的指针。
long int unwrap(QObject* ptr) {
return reinterpret_cast<long int>(ptr);
}
template <typename T>
T* wrap(long int ptr) {
return reinterpret_cast<T*>(ptr);
}
接下来,我们必须注册所有想要集成的类。
class_<QObject, QObject*, boost::noncopyable>("QObject", no_init)
.def("unwrap", unwrap)
.def("wrap", make_function( wrap<QObject>, return_value_policy<return_by_value>() ))
.staticmethod("wrap");
class_<QWidget, bases<QObject>, QWidget*, boost::noncopyable>("QWidget")
.def("wrap", make_function( wrap<QWidget>, return_value_policy<return_by_value>() ))
.staticmethod("wrap");
class_<QFrame, bases<QWidget>, QFrame*, boost::noncopyable>("QFrame")
.def("wrap", make_function( wrap<QFrame>, return_value_policy<return_by_value>() ))
.staticmethod("wrap");
class_<QLabel, bases<QFrame>, QLabel*, boost::noncopyable>("QLabel")
.def("wrap", make_function( wrap<QLabel>, return_value_policy<return_by_value>() ))
.staticmethod("wrap");
比如说,我们有一个与 QLabel 相关的类:
class worker: public QObject {
...
void add_label(QLabel*);
};
我们也需要把这个类暴露给 Python 使用:
class_<worker, bases<QObject>, worker*, boost::noncopyable>("worker")
.def("add_label", &worker::add_label);
现在我们准备好进行交互了,在 C++ 这边可以做类似这样的事情:
worker* w = new worker;
main_namespace["worker"] = boost::ref(w);
在 Python 这边:
from PyQt4.Qt import *
import sip
import mylib as MyLib
#...
#If you are using QApplication on C++-size you don't need to create another one
lb = QLabel("label from PyQt4!")
lb_ptr = sip.unwrapinstance(f)
my_lb = MyLib.QLabel.wrap(lb_ptr)
worker.add_label(my_lb)
如果你想把自己的 Q 对象发送到 PyQt4:
QLabel* lb = new QLabel("C++ label");
main_namespace["lb"] = boost::ref(lb);
在 Python 这边:
from PyQt4.Qt import *
import sip
import mylib as MyLib
#...
my_lb_ptr = lb.unwrap()
qt_lb = sip.wrapinstance(my_lb_ptr, QLabel)
这是我真正的小助手:
from PyQt4.Qt import *
import sip
def toQt(object, type):
ptr = object.unwrap()
return sip.wrapinstance(ptr, type)
def fromQt(object, type):
ptr = sip.unwrapinstance(object)
return type.wrap(ptr)
1
我建议你不要试着在boost python中直接使用QPoint。你应该能够通过boost注册一些转换器,这些转换器可以把QPoint和Python之间进行转换,使用的是SIP的API函数,将QPoint转换成SIP对象,或者把SIP对象转换回QPoint。
我之前做过这个,但最近没有再做,所以不能提供更多细节。
5
这有点像昨天有人遇到的一个问题。简单来说,似乎在新式类的实例中,特殊方法(比如 __getattr__
、__str__
、__repr__
、__call__
等)是不能被重写的,也就是说,你只能在它的类型里定义这些方法。
下面是我为那个问题调整的解决方案,希望对你的问题也有帮助:
def _q_getattr(self, attr):
print("get %s" % attr)
return getattr(self, 'x')
def override(p, methods):
oldType = type(p)
newType = type(oldType.__name__ + "_Override", (oldType,), methods)
p.__class__ = newType
override(p, { '__getattr__': _q_getattr})
print(p.__getattr__('x')()) # Works! Prints "0"
print(p.x()) # Should work!