Python method resolution mystery
I can't figure out why this program is failing.
#!/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'
I used Boost.Python to create libui, which exposes the class QPoint. I aso included PyQt4, which has a sip-exposed QPoint. I'm trying to accomplish a mapping betw开发者_如何学JAVAeen the two types.
I checked that p
is a new-style class, so why isn't __getattr__
being called for p.x()
?
This is somewhat similar to the issue someone else has encountered just yesterday. In short, it seems like special methods (like __getattr__
, __str__
, __repr__
, __call__
and so on) aren't overridable in new-style class instance, i.e. you can only define them in its type.
And here's an adaptation of my solution for that problem which should hopefully work for yours:
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!
I suggest that you not attempt to expose QPoint in boost python. You should be able to register converters to/from python with boost that will use the SIP api functions to convert QPoint from/to python as the sip objects.
I've done it, but not recently enough to give more details.
This is an example how to integrate PyQt4 and boost::python
first of all we must define wrap/unwrap function to deal with bare pointers
long int unwrap(QObject* ptr) {
return reinterpret_cast<long int>(ptr);
}
template <typename T>
T* wrap(long int ptr) {
return reinterpret_cast<T*>(ptr);
}
after that we must register all classes we want integrate to
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");
and for example we have class that works with.. QLabel:
class worker: public QObject {
...
void add_label(QLabel*);
};
we must expose this class to python too:
class_<worker, bases<QObject>, worker*, boost::noncopyable>("worker")
.def("add_label", &worker::add_label);
now we a ready to interaction, on C++-size do something like this
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)
In other case if you wan't send you own Q-object to 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)
And this is my real little helper:
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)
精彩评论