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

时间:2016-09-02 12:18:13

标签: 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来装饰插槽,而pyqtSlot又需要(在PyQt 5.7中)我从QObject派生项目。

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

1 个答案:

答案 0 :(得分:1)

这与pyqtSlot无关。

实际问题是你试图从两个Qt类继承,那就是not generally supported。唯一的例外是实现接口的Qt类,以及共享公共基类的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)