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

时间:2014-03-27 09:47:30

标签: python qt user-interface copy pyside

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

首先,某些背景

我使用 Qt Designer 创建了一个简单的GUI,我在我的pyside-uic.exe文件中使用了.ui来生成关联的< strong> 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 生成的setupUi()文件中的.py方法,我仍然得到完全相同的错误。

值得注意的是,我能够毫无困难地访问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:无法挑选&lt; class&#39; Pyside.QtCore.SignalInstance&#39;&gt;:属性查找Pyside.QtCore.SignalInstance失败。

2 个答案:

答案 0 :(得分:1)

经过一些研究,我认为使用其中一种技术复制一个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元素,即使涉及一些变量,例如你想要多少个标签?

答案 1 :(得分:1)

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

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