【问题标题】:Unable to change __class__ of PySide.QtGui objects无法更改 PySide.QtGui 对象的 __class__
【发布时间】:2012-05-22 18:09:47
【问题描述】:

我经常使用 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 模块。

【问题讨论】:

    标签: python pyqt4 pyside


    【解决方案1】:

    PySide 使用Shiboken 从 它的 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 失败,因为 self 实际上是 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)
    
    • 注意:您可以通过self.__class__ 直接拨打QPushButton.text() 因为你不会直接使用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''
    

    【讨论】:

      【解决方案2】:

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

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

      现在单击“Add”,然后单击“Promote”,您将在 Object Inspector 窗格中看到类从“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。

      【讨论】:

      • 我的函数接收到一个已经实例化的小部件,我需要以编程方式“提升”小部件。我无法控制小部件的创建。猴子补丁似乎是必要的。
      • @Hernan。与其在每次有人提供解决方案时都添加额外的条件,不如在问题中明确说明您的要求是什么。
      • @cpburnz。 OP 实际上说:“......因为对象是在由 pyuic 编译的库中创建的”。这向我表明问题由pyuic创建的对象的结果。他也没有提到无法控制对象的创建。但无论如何,我不会删除我的答案,因为它可能仍然会帮助一些正在搜索这个一般主题的人。
      • @ekhumoro 我同意 cpburnz。这个问题对我来说很清楚。我对“……对象是在库中创建的……”的理解是您无法控制该库。它还给出了在 PySide 中不起作用的代码示例。这就是我理解问题的方式(这不是我的问题!)以及我提供赏金的原因。
      • @Hernan。道歉:我完全错过了你不是 OP - 如果现在还不明显!
      猜你喜欢
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 2021-10-04
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-26
      相关资源
      最近更新 更多