无法更改 PySide.QtGui 对象的 __class__ [英] Unable to change __class__ of PySide.QtGui objects

查看:72
本文介绍了无法更改 PySide.QtGui 对象的 __class__的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常使用 PyQt4 - 有时我喜欢重载一些对象以允许我添加一些功能.这在 PyQt4 中工作正常,例如:

from PyQt4 import QtGui按钮 = QtGui.QPushButton()类 MyPushButton(QtGui.QPushButton):通过button.__class__ = MyPushButton

但是,我正在尝试调整我的一些代码,以便它使用 PySide 而不是 PyQt4.我的理解是它们应该具有相同的功能.PySide 不允许我做同样的事情.

from PySide import QtGui按钮 = QtGui.QPushButton()类 MyPushButton(QtGui.QPushButton):通过button.__class__ = MyPushButton

此错误:

TypeError: __class__ assignment: only for heap types

是否有另一种方法可以更改对象的类以避免此错误?我不确定是什么原因造成的.

注意:创建对象后,我需要更改对象的类,因为对象是在由 pyuic 编译的库中创建的.我的 PySide 安装也没有 uic 模块.

解决方案

PySide 使用 Shiboken从生成 Qt 的 CPython 绑定它的 C++ 类.所有 Qt python 类,例如 QPushButton 都是用 C++ 实现,这就是为什么你不能覆盖 __class__.

<预><代码>>>>按钮 = QPushButton()>>>button.__class__ = MyButton类型错误:__class__ 赋值:仅适用于堆类型

根据 Shiboken 的文档,您可以猴子补丁(或鸭拳)已经实例化的对象上的方法,只要这些方法是虚拟(可覆盖):

导入类型def override_text(self):返回覆盖"# 将 override_text() 绑定到按钮.button.text = types.MethodType(override_text, button, QPushButton)

更进一步,您可以将 QPushButton 子类化为 MyButton,并且将 MyButton 中的方法动态注入 QPushButton实例.使 MyButton 成为 QPushButton 的子类纯粹是可选,但允许您创建自己的 MyButton 实例除了修改后的 QPushButton 实例.

让我们定义 MyButton 作为 QPushButton 的子类.

class MyButton(QPushButton):定义文本(自我):# 这将覆盖 QPushButton 的 text() 方法.打印(在 MyButton.text() 里面")返回 QPushButton.text(self)

  • 注意:您必须使用调用父类方法的旧样式.super() 失败并返回 TypeError 因为 self 实际上是一个 QPushButton当方法被注入时,而不是 MyButton.

或者,如果您想采用更多的混合方法,让我们定义 MyButtonOverrides:

class MyButtonOverrides(object):定义文本(自我):# 这将覆盖 QPushButton 的 text() 方法.打印(在 MyButtonOverrides.text() 中")返回 self.__class__.text(self)

  • 注意:您可以通过self.__class__直接调用QPushButton.text()因为您不会直接使用 MyButtonOverrides.

现在让我们定义 extend_instance() 它将注入您的覆盖从 MyButton(或 MyButtonOverrides)到 QPushButton 的方法实例:

导入检查defextend_instance(obj, cls):对于名称,vars(cls).items() 中的属性:如果inspect.isroutine(attr):# 将实例、类和静态方法绑定到 *obj*.setattr(obj, name, attr.__get__(obj, obj.__class__))

如果您希望您的类方法保持绑定到它们的原始类(例如,MyButton)然后使用以下内容:

def extend_instance(obj, cls):对于名称,vars(cls).items() 中的属性:如果inspect.isroutine(attr):if isinstance(attr, classmethod):# 将类方法绑定到 *cls*.setattr(obj, name, attr.__get__(cls, cls))别的:# 将实例和静态方法绑定到 *obj*.setattr(obj, name, attr.__get__(obj, obj.__class__))

通过我的测试,这适用于 Python 2.7 和 3.3,但应该在 2.6+ 和 3+ 上工作.

最后修改按钮,使用extend_instance().

<预><代码>>>>按钮 = QPushButton()>>>扩展实例(按钮,MyButton)>>>按钮.文本()在 MyButton.text() 里面你''

I've used PyQt4 quite a lot - sometimes I like to overload some of the objects to allow me to add some functionality. This works fine in PyQt4, eg:

from PyQt4 import QtGui
button = QtGui.QPushButton()
class MyPushButton(QtGui.QPushButton): pass
button.__class__ = MyPushButton

However, I'm trying to adapt some of my code so that it uses PySide instead of PyQt4. My understanding is that they should have the same functionality. PySide will not allow me to do the same thing.

from PySide import QtGui
button = QtGui.QPushButton()
class MyPushButton(QtGui.QPushButton): pass
button.__class__ = MyPushButton

This errors with:

TypeError: __class__ assignment: only for heap types

Is there another way I can change the class of my object to avoid this error? I'm not really sure what's causing it.

NOTE: I need to change the class of the object after it is created as the object is created in a library which is compiled by pyuic. Also my PySide installation does not have the uic module.

解决方案

PySide uses Shiboken to generate the CPython bindings for Qt from its C++ classes. All of the Qt python classes such as QPushButton are implemented in C++ which is why you cannot overwrite __class__.

>>> button = QPushButton()
>>> button.__class__ = MyButton
TypeError: __class__ assignment: only for heap types

According to the Shiboken's documentation, you can monkey patch (or duck punch) the methods on an already instantiated object so long as the methods are virtual (overridable):

import types

def override_text(self):
    return 'overridden'

# Bind override_text() to button.
button.text = types.MethodType(override_text, button, QPushButton)

Taking this further you can sub-class QPushButton as MyButton, and dynamically inject the methods from MyButton into the QPushButton instance. Making MyButton a sub-class of QPushButton is purely optional, but would allow you to make your own instances of MyButton in addition to the modified QPushButton instances.

Let's define MyButton as a sub-class of QPushButton.

class MyButton(QPushButton):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButton.text()")
        return QPushButton.text(self)

  • NOTE: You have to use the old-style of calling parent class methods. super() fails with a TypeError because self is actually a QPushButton and not a MyButton when the method is injected.

Or if you wanted to take more of a mixin approach, let's define MyButtonOverrides:

class MyButtonOverrides(object):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButtonOverrides.text()")
        return self.__class__.text(self)

  • NOTE: You can call the QPushButton.text() directly through self.__class__ because you won't be using MyButtonOverrides directly.

Now let's define extend_instance() which will inject your override methods from MyButton (or MyButtonOverrides) into the QPushButton instance:

import inspect

def extend_instance(obj, cls):
    for name, attr in vars(cls).items():
        if inspect.isroutine(attr):
            # Bind instance, class and static methods to *obj*.
            setattr(obj, name, attr.__get__(obj, obj.__class__))

If you'd like your class methods to remain bound to their originating class (e.g., MyButton) then use the following:

def extend_instance(obj, cls):
    for name, attr in vars(cls).items():
        if inspect.isroutine(attr):
            if isinstance(attr, classmethod):
                # Bind class methods to *cls*.
                setattr(obj, name, attr.__get__(cls, cls))
            else:
                # Bind instance and static methods to *obj*.
                setattr(obj, name, attr.__get__(obj, obj.__class__))

Through my testing this works in Python 2.7 and 3.3, but should work on 2.6+ and 3+.

Finally to modify the button, use extend_instance().

>>> button = QPushButton()
>>> extend_instance(button, MyButton)
>>> button.text()
inside MyButton.text()
u''

这篇关于无法更改 PySide.QtGui 对象的 __class__的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆