尝试复制 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.3 和 pyside 1.2.1 与 Qt Designer 4(Qt 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 失败。
【问题讨论】:
最困扰我的是,虽然pickle
或marshal
明确告诉我他们无法处理该对象,但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,因此应该使用copy
、pickle
或marshal
等模块来完成克隆。
使用pickle
或marshal
失败,因为发现对象不可拾取。
虽然copy.copy
或copy.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 - 当我尝试将结构从数组复制到结构中包含的数组时出现问题