从 Qt4 更新到 Qt5

问题描述

我最近参与了一个使用 PyQt 的项目。它使用的是 Qt4,但由于在最新系统上安装它以及所有其他工具和软件包的痛苦,我决定将所有内容更新为 python3、Qt5 等。

在旧代码中,

self.selectionModel().selectionChanged.connect(self.selection_changed_event)

有效且有效。

@QtCore.pyqtSlot("QTreeView,QItemSelection,QItemSelection")
    def selection_changed_event(self,selected,deselected):
        """Event handler.
        Triggered by TreeView selection change.
        Get Title from selected/deselected items. Send SelectionEvent(title) to widgets to show/hide content as needed.
        :param selected: New selected item.
        :param deselected: Item that was selected.
        :return:
    """

自从更新到 Qt5 后,现在说 connect() 失败了。虽然我是个菜鸟,但我的理解是我确实想检索视图的 selectionModel,然后连接到它的 selectionChanged 信号。所以我应该有 self.ITEM.selectionModel().selectionChanged.connect(self.SLOT) 的形式。 在插槽内,它似乎符合标准,我应该有 QModelIndex 告诉我们在事件期间选择了什么,而 QModelIndex 告诉我们在事件期间取消选择了什么。

因此,我对此所做的唯一其他显着更改是从 QTGui 切换到 QtWidget。这是因为 QtGui 和 QtWidget 从 Qt4 转移到 Qt5。

旧:

class QMenuTreeView(QtGui.QTreeView):
    selectionChangeEvent = QtCore.pyqtSignal(str)
    def __init__(self,parent=None,log=None):
        QtGui.QTreeView.__init__(self,parent)
        self.parent = parent
        ...
        self.selectionModel().selectionChanged.connect(self.selection_changed_event)

新:

class QMenuTreeView(QtWidgets.QTreeView):
    selectionChangeEvent = QtCore.pyqtSignal(str)
    def __init__(self,log=None):
        QtWidgets.QTreeView.__init__(self,parent)
        self.parent = parent
        ...             
        self.selectionModel().selectionChanged.connect(self.selection_changed_event)

Qt4 和 QT5 之间是否还有其他我不理解的变化,并且会导致这种变化? 似乎我不能使用 QtGui,因为对 QTreeView 的引用不再有效,另外,它现在在他们的文档中列为 QtWidgets。我还有什么需要转换的吗,或者这个 connect() 问题是否与其他一些问题有关?

解决方法

那个 pyqtSlot 语法似乎是错误的(即使对于 PyQt4):签名应该包括视图的类,因为它不是信号的一部分。
它在 PyQt4 上工作的原因是,在该版本中,插槽装饰器语法和类型检查不如 PyQt5 严格。

考虑到,在正常情况下,应该不需要槽装饰器(特别是对于“常见”小部件和不处理线程时),所以我会立即删除装饰器,至少通过评论它.

然后,如果确实需要该特定要求,则语法应为:

    @QtCore.pyqtSlot(QtCore.QItemSelection,QtCore.QItemSelection)
    def selection_changed_event(self,selected,deselected):
        # ...

请注意,没有引号,它们仅在 PyQt 不提供类型并且插槽无论如何需要它们的非常特定情况下才需要(例如,QStringList)。此外,参数的连接应该在同一个字符串中。

这是正确调用并打印两个选择(注意:

    @pyqtSlot('QItemSelection','QItemSelection'):
    def test(self,deselected):
        print(selected,deselected)

这引发了一个异常,因为插槽只识别第一种类型,但函数需要两种类型:

    @pyqtSlot('QItemSelection,QItemSelection')
    def test(self,deselected)

最后,这可行,但只给出了第一个参数:

    @pyqtSlot('QItemSelection,*args):
        print(args)