尝试复制 pyside 对象时出现问题

Posted

技术标签:

【中文标题】尝试复制 pyside 对象时出现问题【英文标题】:Issue while trying to copy pyside object 【发布时间】:2014-03-27 09:47:29 【问题描述】:

我在使用 pyside 时遇到了一个相当令人沮丧的问题,我欢迎任何建议。

首先,一些上下文

我使用 Qt Designer 创建了一个简单的 GUI,并在我的 .ui 文件中使用了pyside-uic.exe 以生成关联的Python 文件。

我正在使用 Python 3.3pyside 1.2.1Qt Designer 4Qt 4.8.5)。

我正在使用以下代码来启动我的 GUI:

class my_dialog(QMainWindow, my_gui.Ui_main_window):
    def __init__(self, parent=None):
        super(my_dialog, self).__init__(parent)
        self.setupUi(self)

if ("__main__" == name):
    app = QApplication(sys.argv)
    main_dialog = my_dialog()

    # (1)

    main_dialog.show()
    sys.exit(app.exec_())

我想要达到的目标

我的 GUI 有几个选项卡。选项卡的数量不是预先确定的,而是在运行时评估的。因此,我决定在 Qt Designer 中创建一个选项卡,用作模板。

我第一次需要添加标签时,我修改了这个模板,如果我需要任何额外的标签,我打算复制那个标签然后适当地修改该副本

我遇到的问题

我的问题是我似乎找不到复制标签小部件的方法。经过一番研究,我认为copy 模块(或pickle 模块,请参阅编辑)可能会解决问题(以下代码插入在 (1) ):

new_tab = copy.deepcopy(main_dialog.my_tab)
main_dialog.my_tabs.addTab(new_tab, "")

但这引发了以下错误:

    main_dialog.my_tabs.addTab(new_tab, "")

RuntimeError: 内部 C++ 对象 (Pyside.QtGui.QWidget) 已删除

我自己能找到什么

我在 SO 和其他网站上看到,在使用 pyside 时可能会出现收集对象的问题,因为在 中没有对它们的引用Python.

然而,事实仍然是,即使我将这段代码移到 pyside 生成的 .py 文件中的 setupUi() 方法中,我仍然会得到完全相同的错误。

另外值得注意的是,我可以毫无问题地访问my_tab 对象来修改其内容。

我可以在我的代码中从头开始创建另一个选项卡,main_dialog.my_tabs.addTab(new_tab, "") 在这种情况下工作得非常好。

经过一些实验,我意识到问题可能出现在 my_tab 对象的副本上。事实上,复制我刚刚创建的选项卡对象,我可以看到尝试将副本添加到 GUI 选项卡也失败了,并且出现了同样的错误。

看起来复制以某种方式失败,或者由于某种原因该对象被立即删除。反正我就是这么推断的……

我的问题

考虑到这一切,我想找到一种方法来成功复制对象,找到另一种方法来使用现有的 pyside 对象作为其他类似对象的模板对象。

我当然可以从生成的文件中取出选项卡的代码并编写我自己的addTab() 方法。但是,我应该从现有的 .ui 文件构建并避免硬编码 GUI 元素。

编辑:

使用pickle时:

new_tab = pickle.loads(pickle.dumps(main_dialog.my_tab, -1))

我收到以下错误:

    new_tab = pickle.loads(pickle.dumps(main_dialog.my_tab, -1))

_pickle.PicklingError: Can't pickle : 属性查找 Pyside.QtCore.SignalInstance 失败。

【问题讨论】:

最困扰我的是,虽然picklemarshal 明确告诉我他们无法处理该对象,但copy 似乎可以工作,不会引发任何异常,但是对象不存在... 【参考方案1】:

为要复制的小部件创建单独的 ui 文件的建议似乎是一个合理的解决方案。尽管使用 pyside-uic 为小部件生成单独的 gui 模块似乎与使用 QUiLoader 一样有效(实际上,它会更有效)。

至于为什么使用例如克隆小部件的问题copy.deepcopy 不起作用:这是因为它只会复制 python 包装器,而不是底层的 C++ 对象。在this answer 中可以找到更全面的解释。

【讨论】:

【参考方案2】:

经过更多研究,我相信使用其中一种技术复制 pyside 对象是不可能的。

首先要注意的是there is no built-in function to clone a Qt widget,因此应该使用copypicklemarshal等模块来完成克隆。

使用picklemarshal 失败,因为发现对象不可拾取

虽然copy.copycopy.deeepcopy 没有引发任何警告/异常/错误,但副本不会发生,或者由于某种原因被立即删除。

当尝试将 deepcopy 作为参数传递给 addTab 时,不会引发警告/异常/错误,但程序会在该行停止并返回 Python 命令提示符。在退出之前在那条线上需要几秒钟的事实使我假设deepcopy 尝试浏览对象属性并在某些时候失败。对copy 执行相同操作会导致问题中提到先前的C++ object deleted 错误,因此我只能推断deepcopy 操作确实失败了。

因此,我能给寻找类似答案的人的唯一建议是实现他们自己的 copy-widget 函数,这最终是我现在要做的。

不过,我想了解deepcopy 是如何失败的,如此无声无息,却引发了执行的结束。我开始了一个线程来尝试找到这个there的答案

编辑:

我找到了解决这个问题的方法,该解决方案符合我对 GUI 元素不进行硬编码以及使用 Qt Designer 为可重复元素创建 GUI 和模板的要求。希望对遇到同样问题的人有所帮助:

这个想法是可以使用 Qt -- 和 pyside -- 在运行时加载给定的.ui 文件,使用QUiLoader() 方法。因此可以解析 .ui 文件以提取给定的小部件(.ui 文件是简单的 XML 文件)并使用以下代码来使用它:

loader = QUiLoader()
ui_file = QFile("path_to_ui_file.ui")
ui_file.open(QFile.ReadOnly)
new_tab = loader.load(ui_file)
ui_file.close()
main_dialog.my_tabs.addTab(new_tab, "")

而且它有效!

关于上面例子的几点说明:

第二行假设您已在文件path_to_ui_file.ui 中隔离了您的小部件 在我的示例中,小部件是一个选项卡,当然它适用于您可能已经完成的任何小部件,最后一行仅用于显示不再引发错误 最后,这种方法的优点是允许您使用 Qt Designer 之类的工具来开发您的 GUI 元素,即使涉及到一些变量,例如 您想要多少个选项卡?

【讨论】:

我用一个主要问题的解决方案更新了我的答案,该解决方案是将 Qt Designer 用于主窗口和计划在应用程序的多个实例中使用的模板小部件。

以上是关于尝试复制 pyside 对象时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

复制 json 文件时出现 Amazon Redshift 错误 - JSONPath 格式无效:成员不是对象

复制表时出现约束异常?

在使用 matplotlib 和 PySide2 运行的应用程序中使用 pdb 调试器时出现“事件循环已在运行”

C - 当我尝试将结构从数组复制到结构中包含的数组时出现问题

尝试从常量 char * 类型的指针复制数据时出现“超出内存访问”错误。为啥?

尝试将胶水表复制到红移时出现“在 awaitResult 中引发的异常:”错误