我已经使用了很多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模块。
答案 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”。
当您使用pyuic
或pyside-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。