无法更改 PySide.QtGui 对象的 __class__ [英] Unable to change __class__ of PySide.QtGui objects
问题描述
我经常使用 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__
.
根据 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()
.
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 aTypeError
because self is actually aQPushButton
and not aMyButton
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 throughself.__class__
because you won't be usingMyButtonOverrides
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屋!