python - 为什么从 QListWidgetItem 和 QObject 派生时会崩溃

标签 python multiple-inheritance signals-slots pyqt5

以下最小示例在 Windows 上的 pyqt 5.7.1 中崩溃(将其复制粘贴到 .py 文件中并运行):

from PyQt5.QtWidgets import QListWidgetItem, QListWidget, QApplication
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal


class MyListItem(QListWidgetItem):
    def __init__(self, obj):
        QListWidgetItem.__init__(self, 'example')
        obj.sig_name_changed.connect(self.__on_list_item_name_changed)

    def __on_list_item_name_changed(self, new_name: str):
        self.setText(new_name)


class MyListItem2(QListWidgetItem, QObject):
    def __init__(self, obj):
        QListWidgetItem.__init__(self, 'example')
        QObject.__init__(self)
        obj.sig_name_changed.connect(self.pyqt_slot)

    @pyqtSlot(str)
    def __on_list_item_name_changed(self, new_name: str):
        self.setText(new_name)


class Data(QObject):
    sig_name_changed = pyqtSignal(str)


class SearchPanel(QListWidget):
    def __init__(self, parent=None):
        QListWidget.__init__(self, parent)
        obj = Data()
        hit_item = MyListItem(obj)  # OK
        hit_item = MyListItem2(obj)  # crashes
        self.addItem(hit_item)
        obj.sig_name_changed.emit('new_example')


app = QApplication([])
search = SearchPanel()
search.show()
app.exec()

现在只需注释掉“崩溃”这一行,它就可以正常工作了。此外,列表小部件显示“new_example”,表明信号已通过。

有没有办法让它与 MyListItem2 一起使用?即我希望能够用 pyqtSlot 装饰插槽,这反过来又要求(在 PyQt 5.7 中)我从 QObject 派生项目。

这里的目的是列表中的每个项目都有几个可以根据来自关联数据实例的信号进行更改的特征(图标、字体、文本颜色)(每个实例实际上“存在”,在 Qt 意义上,在我们应用程序的第二个线程中)。

最佳答案

这与pyqtSlot无关。

实际问题是您试图从两个 Qt 类继承,即 not generally supported 。唯一的异常(exception)是实现接口(interface)的 Qt 类,以及共享公共(public)基类的 Qt 类(例如 QListWidgetQWidget)。然而,只有前者是官方支持的,并且关于后者有几个限制条件(此处均不相关)。

因此,从 QListWidgetItemQObject 继承的 Python 类将无法工作。当 PyQt 尝试访问未由顶级基类定义的属性时(即使该属性不存在),就会出现主要问题。在早期的 PyQt 版本中,这只会引发错误:

>>> class MyListItem2(QListWidgetItem, QObject): pass
...
>>> x = MyListItem2()
>>> x.objectName()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: could not convert 'MyListItem2' to 'QObject'
>>> x.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: could not convert 'MyListItem2' to 'QObject'

这清楚地表明(用 C++ 术语来说)MyListItem2(QListWidgetItem) 不能转换为 QObject。不幸的是,似乎 PyQt5 的更新版本不再引发此错误,而是立即转储核心(这可能是一个错误)。

如果您确实需要使用pyqtSlot,一个建议是使用组合而不是子类化。所以也许是这样的:

class ListItemProxy(QObject):
    def __init__(self, item, obj):
        QObject.__init__(self)
        self._item = item
        obj.sig_name_changed.connect(self.__on_list_item_name_changed)

    @pyqtSlot(str)
    def __on_list_item_name_changed(self, new_name: str):
        self._item.setText(new_name)

class MyListItem2(QListWidgetItem):
    def __init__(self, obj):
        QListWidgetItem.__init__(self, 'example')
        self._proxy = ListItemProxy(self, obj)

关于python - 为什么从 QListWidgetItem 和 QObject 派生时会崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39292260/

相关文章:

python - 将顺序行号作为第一列添加到多个 Pandas

python - 如何根据多列的值拆分数据框

python - 前一年和明年的天数 - Pandas

c++ - C++ 中的方法解析顺序

c++ - 是否有一种巧妙的方法来避免覆盖模板基类的所有纯虚函数,用于多重继承

c++ - 继承与聚合以及 "has-a"与 "is-a"。

c++ - 如何在退出时调用插槽

python - python程序显示编译错误

c++ - 使用带有继承的 Qt5 新连接语法

c++ - 当 QObject::connect() 失败时,您能否导致 Qt 断言、段错误或以其他方式崩溃?