为什么类似的信号/槽连接在 Pyqt5 到 PySide6 端口后表现不同

mah*_*tah 4 python qt pyqt5 pyside6

我正在将 pyqt5 GUI 移植到 pyside6,但遇到了一个我不明白的问题。

我有两个 QSpinbox,它们控制两个 GUI 参数:一个 Spinbox 控制拆分器的重量,另一个控制 QtableView 中的行高。

这是pyqt5中的代码:

spinbox1.valueChanged.connect(my_splitter.setHandleWidth)
spinbox2.valueChanged.connect(my_view.verticalHeader().setDefaultSectionSize)
Run Code Online (Sandbox Code Playgroud)

在 Pyside6 spinbox1 工作正常,但 spinbox2 没有完成其工作,并且有此警告:

您无法在源自 C++ 的对象上添加动态槽。

将第二行代码修改为:

spinbox2.valueChanged.connect(lambda x: my_view.verticalHeader().setDefaultSectionSize(x))
Run Code Online (Sandbox Code Playgroud)

很高兴找到解决方案,但我还想了解为什么两个连接在 PySide6 中表现不同以及为什么使用 lambda 可以解决问题。

警告消息可能提供了线索,但我不知道动态插槽是什么(快速谷歌对我没有多大帮助)。

编辑: 由于我更改了两件事:Qt5 > QT6 和 pyqt > pyside 我在 4 个 python 包装器(pyqt5、pyqt6、pyside2、pyside6)中查看了这一点,以了解哪些更改导致了问题。我可以看出 pyside 2 和 6 都显示了这种行为,并且 pyqt 都没有

mus*_*nte 5

看起来 PySide(或者更准确地说,Shiboken,允许 Python 访问 Qt 对象的包装器)无法直接连接到 Qt 直接创建的对象的插槽。

这似乎是一个 PySide 错误:PyQt 没有显示该行为,这意味着完全有可能实现它。

PyQt 有时也会发生类似的行为,但这只是在非常特殊的情况下:对于Qt 直接创建的对象的受保护方法。例如,尝试调用initStyleOption()项目视图的默认委托会引发RuntimeError(“无法访问不是从 Python 创建的对象的受保护函数或信号”)。

尽管如此,对于像这样的公共职能来说,这种情况setDefaultSectionSize()应该发生。

不过,有一些可能的解决方法。

使用 lambda

正如您已经发现的,您可以只使用 lambda。这将强制 PySide 连接到 python 函数而不是 Qt 函数:PySide 始终允许这种连接。

这种方法的缺点是 lambda 的常见问题:如果直接使用它作为参数connect(),则完全失去对它的任何引用,因此无法专门断开与该函数的连接,除非您断开该信号的所有函数(或整个物体)。

不过,可以参考 Lambda:

        header = my_view.verticalHeader()
        header.setDefaultSectionSize_ = lambda s: header.setDefaultSectionSize(s)
        spinbox2.valueChanged.connect(header.setDefaultSectionSize_)
Run Code Online (Sandbox Code Playgroud)

使用上面的代码,您可以断开该函数的连接,因为您现在拥有对该函数的持久引用。

使用方法

这与上面的类似,不同之处在于我们创建一个特定的方法来处理这个问题,假设您保留对标头(或者更好的是视图)的引用:

        spinbox2.valueChanged.connect(self.updateMyViewSectionSize)

    def updateMyViewSectionSize(self, size):
        self.my_view.verticalHeader().setDefaultSectionSize(size)
Run Code Online (Sandbox Code Playgroud)

它可能有点冗长,但它也是一种更好的方法,因为它考虑了函数的动态性质,verticalHeader() 提供了对在其他情况下可能需要调用的函数的公共访问。

显式设置标题

这是我通常用于解决上述委托问题的一个技巧:每当我需要根据其从委托获取一些信息时initStyleOption(),我只需创建一个新的“虚拟”委托;由于它是用Python创建的,所以不再出现这个问题。

对于这种情况也同样适用:创建并设置一个虚拟标题视图。

        my_view.setVerticalHeader(QHeaderView(Qt.Vertical, my_view))
Run Code Online (Sandbox Code Playgroud)

请注意,方向和参数都是强制性的,并且上述操作应始终尽快完成(在创建表格小部件之后)。


我仍然建议您在Qt bug tracker中提交一份报告,希望他们能够至少为 PySide6 修复它(PySide2 可能会被忽略,但你永远不知道)。