0

I am having problems with the object references when using the QLayout to arrange my widgets in on bigger window next to each other.

I have the following situation

class MyClass(QObject):
     widgetCollection = []

     def spawn(self):
         widget = MyQWidget() #containing a QLineWidget called "nameEdit"
         self.widgetCollection.append(widget)
         self._window = QtGui.QWidget()
         layout = QtGui.QHBoxLayout()

         listView = QtGui.QListWidget()            
         for equation in self.wigdetCollection:
              equationName = equation.nameEdit.text()
              item = QtGui.QListWidgetItem(equationName)
              listView.addItem(item)

         layout.addWidget(listView)
         layout.addWidget(widget)

         self._window.setWindowTitle("Equation Editor")
         self._window.setLayout(layout)

         self._window.show()

    def respawn(self):
         self.spawn()

Each time I call spawn(), I want to add a new widget to the collection. Addionally, I want to open a new window where there is a ListView with all widget names on the left and the newly created widget on the right.

Now the first call to the spawn()-method works like expected. But the second call throws an exeption:

equationName = widget.nameEdit.text()
RuntimeError: wrapped C/C++ object of type QLineEdit has been deleted

I think it has something to do with the line

layout.addWidget(widget)

I have read somewhere that the layout takes ownership of the widget when added as an item. With that I loose the widget as soon as I am out of the scope of the layout-reference (which is local in this case). So it seems that the widget-item in my collection gets deleted too.

Can someone help? How do I prevent that.

RaJa
  • 1,471
  • 13
  • 17

2 Answers2

1

The problem is that self._window is replaced every time spawn() is closed: Python discards the old window widget, and self._window is made to reference a new QWidget instance. Since the old window widget is parent to a list view and qlineedit widget (called -- confusingly -- just "widget" in the code), these will be destroyed in the Qt sense of the term -- i.e. their C++ portion will be destroyed. The list view is not referenced anywhere else in the shown code so the Python portion of the list view will be destroyed too, and this is the way it should be.

HOWEVER, the qlineedit is referenced in the class-wide registry (widgetCollection) and so the Python portion of the qlineedit will NOT be destroyed. This means that every time spawn() is called, the previous instance of QLineEdit (via MyQWidget in class-wide widgetCollection) becomes a zombie: it is dead at the C++ Qt level, but it is still alive at the Python level. A zombie's methods can still be accessed, but in many cases you will see the Qt error about C/C++ having been deleted, and you may get a crash or other undefined behavior.

It is not clear from the code posted what is the intent of the widgetCollection, but since the previous item appended will become a zombie during a spawn(), widgetCollection is useless as shown. The solution to the problem depends on details about how the collection is used, but since one can presume that the collection has a purpose, then the issue is the replacement of the window widget: perhaps the widget collection should be a window collection, then when self._window is replaced, the previous window stays alive so the child widgets stay alive too.

Oliver
  • 27,510
  • 9
  • 72
  • 103
  • MyQWidget() - is a Dialog window with some QLineEdits for user input. Each Dialog-window has a button that is connected to the Respawn()-method. _window shows this Dialog in combination with a list of all previous added Dialogs. Everything is handled by the super-class MyClass() which is kind of a main-Function called by a another program. This class keeps all the references. As I need the input from the different Dialog-Windows, the are all collected in the widgetCollection and only later analyzed by a crawler. Respawn is spawning a Dialog from within a Dialog. – RaJa Oct 24 '16 at 09:42
-1

Solved it by myself :-)

My initial call to the spawn()-method was like that:

mc = MyClass()
mc.spawn()

First one okay. Now I wanted to spawn another from within inst1. For that I have used

self.spawn()

in the respawn()-method throwing the above mentioned error.

It has to be the following

 def respawn(self):
     mc = MyClass()
     mc.spawn()

I had to create another instance of MyClass that shares the widgetCollection with all other instances. As desired....

RaJa
  • 1,471
  • 13
  • 17
  • You can accept your own answer to indicate that you've solved the problem yourself - that's a common scenario. – iksemyonov Oct 23 '16 at 18:31
  • I know :) But not for the next two days. I am getting the message "You can accept your own answer in 2 days".... – RaJa Oct 23 '16 at 19:18
  • This is very unclear. For one, spawn() doesn't return anything so inst1 and 2 will be None. Please clarify. – Oliver Oct 24 '16 at 03:24