Domanda

I am having a rather frustrating problem using pyside and I would welcome any advice.

First, some context

I have created a simple GUI using Qt Designer and I have used pyside-uic.exe onto my .ui file in order to generate the associated Python file.

I am using Python 3.3 and pyside 1.2.1 with Qt Designer 4 (Qt 4.8.5).

I am using the following code to launch my 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_())

What I would like to achieve

My GUI features several tabs. The number of tabs is not pre-determined and is evaluated at run time. As a result, I've decided to create one tab in Qt Designer, to use as a template.

The first time I need to add a tab, I modify this template, and if I need any additionnal tab, I was planning on making a copy of that tab and then modify that copy appropriately.

The issue I have encountered

My problem is that I can't seem to find a way to copy the tab widget. After some research, I thought the copy module (or the pickle module, see edit) might do the trick (the following code was inserted at (1)):

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

But that triggered the following error:

    main_dialog.my_tabs.addTab(new_tab, "")

RuntimeError: Internal C++ object (Pyside.QtGui.QWidget) already deleted

What I could find on my own

I have seen on SO and other sites that there may be issues, when using pyside, of objects being collected because there is no reference to them in Python.

The fact remains, however, that even if I move this code to very setupUi() method in the .py file generated by pyside, I still get the exact same error.

It is also worth noting that I am able to access the my_tab object to modify its content without any trouble.

I am able to create another tab from scratch in my code and main_dialog.my_tabs.addTab(new_tab, "") works perfectly fine in that context.

After some experimentations, I realized the problem probably occurs at the copy of the my_tab object. Indeed, copying a tab object that I just created, I could see that trying to add the copy to the GUI tabs failed too, and with the same error.

It looks like the copy fails somehow, or the object is being immediately deleted for some reason. That's what I'm infering anyway...

My question

Considering all this, I would like to find a way to either succeed in the object copy, find another way to use an existing pyside object as template for other similar objects.

I could of course take the code for the tab out of the generated file and code my own addTab() method. However, I am expected to build from an existing .ui file and avoid hardcoding GUI elements.

EDIT:

When using pickle:

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

I get the following error:

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

_pickle.PicklingError: Can't pickle <class 'Pyside.QtCore.SignalInstance'>: attribute lookup Pyside.QtCore.SignalInstance failed.

È stato utile?

Soluzione

The suggestion of creating a separate ui file for the widget you want to copy seems a reasonable solution. Although it would seem that generating a separate gui module for the widget using pyside-uic would work just as well as using QUiLoader (in fact, it would be slightly more efficient).

As for the question of why cloning widget using e.g. copy.deepcopy doesn't work: this is because it will only copy the python wrapper, and not the underlying C++ object. A somewhat fuller explanation can be found in this answer.

Altri suggerimenti

After some more research, I believe copying a pyside object using one of those techniques is not possible.

The first thing to note is that there is no built-in function to clone a Qt widget, so the cloning should be done using modules like copy, pickle or marshal.

Using pickle or marshal fails because the object is found to be not pickable.

Whilst the copy.copy or copy.deeepcopy do not raise any warning/exception/error, the copy does not occur, or is deleted right afterwards for some reason.

When trying to pass in the deepcopy as parameter to addTab, no warning/exception/error is thrown, yet the program stops at that line and exits back to the Python command prompt. The fact that it takes a few seconds on that line before exiting makes me assume deepcopy tries to browse through the object attributes and fails at some point. Doing the same with copy results in the previous C++ object deleted error mentionned in the question, so I can only infer the deepcopy operation does fail.

As a result, the only advice I could give someone looking for a similar answer is to implement their own copy-widget function, which is ultimately what I will do now.

Still, I wish to understand how is that deepcopy fails like this, so silently, yet provoking the end of the execution. I started a thread to try and find an answer to this there

EDIT:

I found a solution for this problem, that respects my requirements of not hard-coding GUI elements and of using Qt Designer to create the GUI and templates for repeatable elements. I hope it helps anyone having the same issue:

The idea is that it is possible using Qt -- and pyside -- to load a given .ui file at run time, using the QUiLoader() method. It is thus possible to parse the .ui file to extract a given widget (.ui files are simple XML files) and use the following code to use it:

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, "")

And it works!

A few things about the above example:

  • the second line assumes you've isolated your widget in the file path_to_ui_file.ui
  • in my example the widget is a tab, of course it works with any widget you might have done, the last line is only provided to show that the error is no longer thrown
  • finally, this approach has the pro of allowing you to use tools like Qt Designer to develop your GUI elements even when some variables are involved, such as just how many of that tabs do you want?
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top