无法更改PySide.QtGui对象的__class__

时间:2012-05-22 18:09:47

标签: python pyqt4 pyside

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

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

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

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

此错误:

TypeError: __class__ assignment: only for heap types

还有其他方法可以更改对象的类以避免此错误吗?我不确定是什么导致了它。

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

2 个答案:

答案 0 :(得分:3)

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

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

根据Shiboken的文档,你可以monkey patch (or duck punch) 只要方法是已经实例化的对象上的方法 虚拟(可覆盖):

import types

def override_text(self):
    return 'overridden'

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

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

让我们将MyButton定义为QPushButton的子类。

class MyButton(QPushButton):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButton.text()")
        return QPushButton.text(self)
  • 注意:您必须使用旧式的调用父类方法。 super()TypeError而失败,因为自我实际上是QPushButton 注入方法时不是MyButton

或者如果你想采取更多的mixin方法,让我们定义MyButtonOverrides

class MyButtonOverrides(object):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButtonOverrides.text()")
        return self.__class__.text(self)
  • 注意:您可以通过QPushButton.text()直接致电self.__class__ 因为您不会直接使用MyButtonOverrides

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

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

如果您希望您的班级方法与其原始班级保持联系 (例如,MyButton)然后使用以下内容:

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

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

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

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

答案 1 :(得分:1)

猴子修补__class__完全没必要。相反,您应该在Qt Designer中promote相关小部件,以便它们自动使用您的子类。

为此,在Qt Designer中,右键单击小部件并选择“Promote to ...”。在对话框中,将“Promoted class name”设置为您的子类(例如“MyPushButton”),并将“Header file”设置为包含子类的模块的python导入路径(例如“myapp”或“myapp.gui”,或者其他)。

现在单击“添加”,然后单击“提升”,您将在“对象检查器”窗格中看到类从“QPushButton”更改为“MyPushButton”。

当您使用pyuicpyside-uic重新生成ui模块时,它将包含以下代码:

class Ui_Window(object):
    def setupUi(self, Window):
        ...
        self.button = MyPushButton(Window)
        self.button.setObjectName("button")
        ...

from myapp import MyPushButton

因此,提升的按钮将被创建为MyPushButton的实例,并且不再需要破解__class__

这种推广小部件的技术适用于PyQt和PySide。