2015-05-14 18 views
9

layout.addWidget(widget) gibi bir şeye çok sayıda özel pencere öğesi eklediğim bir düzen var. Daha sonra tüm bu özel widget'ları kaldırmak ve yenilerini eklemek istiyorum. deleteLater ve setParent(None) söz konusu olduğunda bunu yapmanın en iyi yolu konusunda kafam karıştı. Ben tüm bu satırların gereci tarafından kullanılan düzgün temizleme bellek ihtiyaç olduğunu bir şüphe varPyQt4 setParent vs deleteLater

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

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

: Örneğin, burada düzende tüm widget'lar için deniyor benim temizleme fonksiyonu var. Ben C++ ve Python referansları ile olanların widget referansından emin değilim.

PyQt'deki QObjects için C++ ve Python başvuruları hakkında anladığım şey.

Bir forma bir widget eklediğinizde, widget'ın düzenin alt öğesi haline geldiğine inanıyorum. Yani, removeWidget'u çağırırsam, üst ilişki bozuk olur, bu nedenle C++ ve Python başvurusunu kendim de başka bir üst öğe olmadığı için kendim temizlemem gerekir. setParent numaralı çağrı, ana ilişkiyi düzen ile kaldırmanın açık bir yoludur. deleteLater numaralı çağrı, C++ referansına dikkat çekmek içindir.

widget değişkeni kapsam dışında kaldığından ve widget işaret eden başka bir Python nesnesi olmadığından Python başvurusu çöp toplanır.

setParent ve deleteLater numaralı telefonu arayabilir miyim veya deleteLater bunu düzgün şekilde temizlemek için yeterli olur mu?

Bir yan not olarak, bu senaryoda setParent(None) numaralı telefonun aranmasının çok pahalı bir işlev çağrısı olduğunu buldum. Bu temizleme mesajını kaldırarak tüm temizleme sürecimi hızlandırabilirim. Her şeyi doğru şekilde temizlemek için deleteLater'un yeterli olup olmadığından emin değilim. İşte benim profil çıkışı line_profiler geliyor:

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

PyQt4 kullanarak bu temizlik yapmak için bir 'kabul' yolu var mı? setParent, deleteLater veya her ikisini de kullanmalı mıyım? Muhtemelen

cevap

7

aslında neler olup bittiğini görmenin en kolay yolu interaktif oturumda şeylere adım adım şudur:

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

Yani düzen widget ebeveyn haline gelmez. Bu, anlamlıdır, çünkü widget'lar sadece diğer widget'lar ebeveynler olarak olabilir ve düzenler widget değildir. Bir platforma eklenen tüm widget'lar sonunda ebeveynlerinin mizanpajın ebeveyne sıfırlanmasını sağlayacaktır (ne zaman gelirse). düzenleri bewteen hiçbir ebeveyn/çocuk ilişkisi ve içerdikleri widget'ları olduğundan

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

, farklı bir API altında yatan nesnelere erişim için gereklidir. Ürünler mizanpaja aittir, ancak alttaki nesnelerin mülkiyeti değişmeden kalır. Bu noktada

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

, düzen (o sahipliğini vardı çünkü) kendi öğe sildi ve böylece artık Widget içerdiği ilişkin referansları tutar gelmiştir. Bunu göz önüne aldığımızda, öğe için python sarmalayıcı ile fazla bir şey yapmak artık güvenli değil (tercümecisi, herhangi bir yöntemini çağırmaya çalıştıysak muhtemelen çökecektir).biz hala çocuk widget sahipliğini olduğundan

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

, biz de bunun üzerine deleteLater çağırabilir. Traceback'den görülebileceği gibi, bu temel C++ nesnesini silecektir, ancak python sarmalayıcı nesnesi geride bırakılacaktır. Bununla birlikte, bu sarıcı, geri kalan python referansları geçtikten sonra çöp toplayıcı tarafından silinecektir. Bu işlem sırasında hiçbir zaman setParent(None)'u aramanıza gerek olmadığını unutmayın.

Son nokta: Yukarıdaki yorumlayıcı oturum biraz yanıltıcıdır, çünkü olay sırası her satır yürütüldüğünde işlenir. Bu, deleteLater'un etkilerinin hemen görüldüğü anlamına gelir, bu kodun bir komut dosyası olarak çalıştırılması durumunda geçerli olmaz. Bir komut dosyasında anında silinmesi için,

>>> import sip 
>>> sip.delete(child) 
modülünü kullanmanız gerekir.