QLayout Additem - предотвратить передачу права собственности

У меня возникают проблемы со ссылками на объекты при использовании QLayout для размещения виджетов в большом окне рядом друг с другом.

у меня следующая ситуация

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()

Каждый раз, когда я вызываю spawn(), я хочу добавить в коллекцию новый виджет. Кроме того, я хочу открыть новое окно, в котором есть ListView со всеми именами виджетов слева и недавно созданным виджетом справа.

Теперь первый вызов метода spawn() работает, как и ожидалось. Но второй вызов вызывает исключение:

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

Я думаю, что это как-то связано с линией

layout.addWidget(widget)

Я где-то читал, что макет становится владельцем виджета при добавлении в качестве элемента. При этом я теряю виджет, как только выхожу за рамки ссылки на макет (которая в данном случае является локальной). Так что кажется, что элемент виджета в моей коллекции тоже удаляется.

Кто-нибудь может помочь? Как мне это предотвратить.


person RaJa    schedule 23.10.2016    source источник
comment
Вам нужно показать код для класса MyQWidget.   -  person ekhumoro    schedule 23.10.2016


Ответы (2)


Проблема в том, что self._window заменяется каждый раз, когда закрывается spawn(): Python отбрасывает старый виджет окна, а self._window ссылается на новый экземпляр QWidget. Поскольку старый оконный виджет является родительским для представления списка и виджета qlineedit (называемого, как ни странно, просто «виджетом» в коде), они будут уничтожены в смысле этого термина в Qt, т. е. их часть C++ будет уничтожена. . Представление списка больше нигде не упоминается в показанном коде, поэтому часть представления списка Python также будет уничтожена, и так и должно быть.

ОДНАКО, qlineedit упоминается в общеклассовом реестре (widgetCollection), поэтому часть qlineedit для Python НЕ будет уничтожена. Это означает, что каждый раз, когда вызывается spawn(), предыдущий экземпляр QLineEdit (через MyQWidget в общеклассовой коллекции widgetCollection) становится зомби: он мертв на уровне C++ Qt, но все еще жив на уровне Python. Доступ к методам зомби по-прежнему возможен, но во многих случаях вы увидите ошибку Qt о том, что C/C++ был удален, и вы можете столкнуться с крахом или другим непредвиденным поведением.

Из опубликованного кода неясно, каково назначение widgetCollection, но поскольку предыдущий добавленный элемент станет зомби во время spawn(), widgetCollection, как показано, бесполезен. Решение проблемы зависит от подробностей о том, как используется коллекция, но, поскольку можно предположить, что коллекция имеет цель, проблема заключается в замене виджета окна: возможно, коллекция виджетов должна быть коллекцией окон, тогда когда self._window заменяется, предыдущее окно остается активным, поэтому дочерние виджеты также остаются активными.

person Oliver    schedule 24.10.2016
comment
MyQWidget() - это диалоговое окно с некоторыми QLineEdits для пользовательского ввода. В каждом диалоговом окне есть кнопка, связанная с методом Respawn(). _window показывает это диалоговое окно в сочетании со списком всех предыдущих добавленных диалоговых окон. Все обрабатывается суперклассом MyClass(), который является своего рода основной функцией, вызываемой другой программой. Этот класс хранит все ссылки. Поскольку мне нужен ввод из разных диалоговых окон, все они собираются в widgetCollection и только позже анализируются сканером. Respawn порождает диалог из диалога. - person RaJa; 24.10.2016

Решил сам :-)

Мой первоначальный вызов метода spawn() был таким:

mc = MyClass()
mc.spawn()

Первый нормально. Теперь я хотел создать еще один изнутри inst1. Для этого я использовал

self.spawn()

в методе respawn() выдает указанную выше ошибку.

Должно быть следующее

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

Мне пришлось создать еще один экземпляр MyClass, который разделяет widgetCollection со всеми остальными экземплярами. По желанию....

person RaJa    schedule 23.10.2016
comment
Вы можете принять свой собственный ответ, чтобы указать, что вы решили проблему самостоятельно - это распространенный сценарий. - person iksemyonov; 23.10.2016
comment
Я знаю :) Но не в ближайшие два дня. Я получаю сообщение Вы можете принять свой ответ через 2 дня.... - person RaJa; 23.10.2016
comment
Это очень неясно. Во-первых, spawn() ничего не возвращает, поэтому inst1 и 2 будут равны None. Просьба уточнить. - person Oliver; 24.10.2016