2015-05-14 17 views
9

Mam układ, do którego dodaję wiele niestandardowych widżetów z czymś takim jak layout.addWidget(widget). Później chcę usunąć wszystkie niestandardowe widżety i dodać nowe. Jestem zdezorientowany najlepszym sposobem, aby to zrobić, jeśli chodzi o deleteLater i setParent(None). Na przykład, oto moja funkcja czyszczenia, która jest wywoływana dla wszystkich widgetów w układzie:PyQt4 setParent vs deleteLater

def _removeFilterWidgetFromLayout(self, widget): 
    """Remove filter widget""" 

    self._collection_layout.removeWidget(widget) 
    widget.setParent(None) 
    widget.deleteLater() 

Mam podejrzenie, że nie wszystkie z tych linii są potrzebne do prawidłowego oczyszczania pamięci używanej przez widget. Nie jestem pewien, co się dzieje z odwołaniami C++ i Python do widget.

Oto, co rozumiem w odniesieniu do odwołań C++ i Python do QObjects w PyQt.

Wierzę, że po dodaniu widżetu do układu widget staje się dzieckiem układu. Tak więc, jeśli zadzwonię pod numer removeWidget, wówczas relacja nadrzędna jest zerwana, więc muszę sam posprzątać referencje C++ i Python, ponieważ widget nie ma innego rodzica. Wywołanie funkcji setParent jest jawnym sposobem usuwania relacji nadrzędnej z układem. Wywołanie funkcji deleteLater służy do dbania o referencje C++.

Odwołanie do Pythona jest zbędne, ponieważ zmienna widget wykracza poza zakres i żadne inne obiekty Pythona nie wskazują na widget.

Czy muszę zadzwonić pod numer setParent i deleteLater lub czy deleteLater będzie wystarczający do prawidłowego wyczyszczenia tego?

Na marginesie, stwierdziłem, że wywołanie setParent(None) jest bardzo kosztownym wywołaniem funkcji w tym scenariuszu. Mogę znacznie przyspieszyć cały proces czyszczenia, usuwając to połączenie. Nie jestem pewien, czy wystarczy deleteLater, aby wszystko poprawnie wyczyścić. Oto moje wyjście profilowanie od line_profiler:

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    2167            @profile 
    2168            def _removeFilterWidgetFromLayout(self, widget): 
    2169             """Remove filter widget""" 
    2170 
    2171  233   1528  6.6  1.0   self._collection_layout.removeWidget(widget) 
    2172  233  143998 618.0  97.9   widget.setParent(None) 
    2173  233   1307  5.6  0.9   widget.deleteLater() 

Podczas korzystania PyQt4 jest tam „zaakceptowane” sposób to zrobić porządki? Czy powinienem używać setParent, deleteLater, czy obu?

Odpowiedz

7

Prawdopodobnie najprostszym sposobem, aby zobaczyć, co się rzeczywiście dzieje jest przejść przez rzeczy w sesji interaktywnej:

>>> parent = QtGui.QWidget() 
>>> child = QtGui.QWidget() 
>>> layout = QtGui.QHBoxLayout(parent) 
>>> layout.addWidget(child) 
>>> child.parent() is layout 
False 
>>> child.parent() is parent 
True 

więc układ nie stał się dominującym w widgecie. Ma to sens, ponieważ widżety mogą mieć tylko inne widżety jako rodzice, a układy nie są widżetami. Wszystkie widżety dodane do układu ostatecznie będą miały zresetowane ich rodziców do rodzica układu (gdy tylko otrzyma jeden).

>>> item = layout.itemAt(0) 
>>> item 
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318> 
>>> item.widget() is child 
True 

Ponieważ nie ma relacji rodzic/dziecko bewteen układów i widżety one zawierają, inny API jest potrzebny dostęp do obiektów bazowych. Elementy są własnością układu, ale własność podstawowych obiektów pozostaje niezmieniona.

>>> layout.removeWidget(child) 
>>> child.parent() is parent 
True 
>>> layout.count() 
0 
>>> repr(layout.itemAt(0)) 
'None' 
>>> item 
<PyQt4.QtGui.QWidgetItem object at 0x7fa1715fe318> 

W tym momencie układ usunął swoją pozycję (bo miał go na własność), a więc nie posiada żadnych odniesień do zawartych widget. Biorąc pod uwagę to, nie jest już bezpiecznie robić wiele z paczką pythona dla przedmiotu (interpreter prawdopodobnie zawiesiłby się, gdybyśmy spróbowali wywołać którąkolwiek z jego metod).

>>> child.deleteLater() 
>>> parent.children() 
[<PyQt4.QtGui.QHBoxLayout object at 0x7fa1715fe1f8>] 
>>> child.parent() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
RuntimeError: wrapped C/C++ object of type QWidget has been deleted 
>>> 

Ponieważ wciąż mamy na własność widget dziecka, możemy wywołać deleteLater na nim. I jak widać z tracebacka, spowoduje to usunięcie bazowego obiektu C++, ale jego obiekt opakowania Pythona pozostanie w tyle. Jednak ten wrapper zostanie (ostatecznie) usunięty przez garbage collector, gdy zniknie z niego każdy pozostały python. Pamiętaj, że podczas tego procesu nigdy nie będzie potrzeby dzwonienia pod numer setParent(None).

Jeden ostatni punkt: powyższa sesja tłumacza jest nieco myląca, ponieważ kolejka zdarzeń jest przetwarzana za każdym razem, gdy linia jest wykonywana. Oznacza to, że efekty deleteLater są widoczne natychmiast, co nie byłoby możliwe, gdyby kod był uruchamiany jako skrypt. Aby natychmiast usunąć skrypty, musisz użyć modułu sip:

>>> import sip 
>>> sip.delete(child)