【问题标题】:Python method resolution mysteryPython方法解析之谜
【发布时间】:2011-08-20 21:17:33
【问题描述】:

我不知道为什么这个程序会失败。

#!/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-exposed QPoint。我正在尝试完成这两种类型之间的映射。

我检查了p 是一个新式类,那么为什么__getattr__ 不被称为p.x()

【问题讨论】:

  • 我建议先尝试使用多重继承进行子类化。
  • 如果我子类化,会不会有底层 C++ QPoint 对象的两个副本?那么,在 Python 中设置成员变量对 C++ 来说是不可见的,反之亦然?
  • 你不能只使用其中一个吗?还是使用mixin类?只是问一下,我对 Qt 和 Boost 都不熟悉。
  • @Keith:我正在尝试用 C++ 和 Python 编写一些代码。 Boost.Python 是一个库,可以让我将 C++ 类导出到 Python。 sip 做同样的事情,并被 PyQt 用来将 Qt 导出到 Python。在你不想来回传递 Qt 对象的 Qt 应用程序中,很难有一些 Python 代码和一些 C++ 代码。

标签: python pyqt boost-python python-sip


【解决方案1】:

这有点类似于昨天的问题someone else has encountered。简而言之,似乎特殊方法(如__getattr____str____repr____call__ 等)在新式类 instance 中是不可覆盖的,即您可以只在其类型中定义它们。

这是我针对该问题的解决方案的改编版,希望对您有用:

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!

【讨论】:

  • 是的,这就是为什么我建议使用系统(例如使用继承)而不是尝试“hacks”。但我不确定在这种情况下它会如何使用两个包装类。必须调查。
  • 我没有使用 SIP 的经验,所以我无法判断。如果继承不起作用,那么我的解决方案也不会起作用。但是如果继承不起作用,恐怕你能做的很少,因为所有这些特殊方法都只能在类型级别被覆盖。
  • @Boav:感谢 Python 课程(__getattr__ 必须在类型级别设置。)这解决了这个谜。我会考虑您的解决方案并回复您。
【解决方案2】:

我建议你不要试图在 boost python 中暴露 QPoint。您应该能够使用 boost 向/从 python 注册转换器,它将使用 SIP api 函数将 QPoint 从/向 python 转换为 sip 对象。

我已经完成了,但最近还不足以提供更多细节。

【讨论】:

  • 这听起来正是我想要的。我会投票。如果您偶然发现旧的解决方案,请告诉我。谢谢。
  • @Neil G,我的旧解决方案是在以前雇主的 svn 中。我可能不会偶然发现它。
  • 大声笑,没问题。到目前为止,我已经找到了这个:gitorious.org/avogadro/avogadro/blobs/… 将进一步研究它。
  • @Neil G,这与我所拥有的非常相似。
【解决方案3】:

这是一个如何集成 PyQt4 和 boost::python 的示例

首先我们必须定义 wrap/unwrap 函数来处理裸指针

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

蟒蛇:

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

蟒蛇:

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)

【讨论】:

  • 谢谢!谢谢!这太棒了!
猜你喜欢
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 2020-08-29
  • 2015-04-16
  • 2021-03-05
  • 2015-04-01
  • 1970-01-01
相关资源
最近更新 更多