J'ai prêt de nombreux messages sur la façon de connecter plusieurs signaux au même gestionnaire d'événements en python et PyQt. Par exemple, connecter plusieurs boutons ou combobox à la même fonction.pyqt: Une bonne façon de connecter plusieurs signaux à la même fonction dans pyqt (QSignalMapper non applicable)
De nombreux exemples montrent comment faire cela avec QSignalMapper, mais il est pas applicable lorsque le signal comporte un paramètre, comme combobox.currentIndexChanged
Beaucoup de gens suggèrent qu'il peut être fait avec lambda. C'est une solution propre et jolie, je suis d'accord, mais personne ne mentionne que lambda crée une fermeture, qui contient une référence - ainsi l'objet référencé ne peut pas être supprimé. Bonjour la fuite de mémoire!
Preuve:
from PyQt4 import QtGui, QtCore
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
# create and set the layout
lay_main = QtGui.QHBoxLayout()
self.setLayout(lay_main)
# create two comboboxes and connect them to a single handler with lambda
combobox = QtGui.QComboBox()
combobox.addItems('Nol Adyn Dwa Tri'.split())
combobox.currentIndexChanged.connect(lambda ind: self.on_selected('1', ind))
lay_main.addWidget(combobox)
combobox = QtGui.QComboBox()
combobox.addItems('Nol Adyn Dwa Tri'.split())
combobox.currentIndexChanged.connect(lambda ind: self.on_selected('2', ind))
lay_main.addWidget(combobox)
# let the handler show which combobox was selected with which value
def on_selected(self, cb, index):
print '! combobox ', cb, ' index ', index
def __del__(self):
print 'deleted'
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
wdg = Widget()
wdg.show()
wdg = None
sys.exit(app.exec_())
Le widget n'est pas supprimé si nous dégageons la référence. Supprimer la connexion à lambda - il est supprimé correctement. Donc, la question est: quelle est la bonne façon de connecter plusieurs signaux avec des paramètres à un seul gestionnaire sans fuite de mémoire?
deleteLater() semble cacher le widget, mais le destructor est pas appelé pas moins. L'ajout d'un second widget après la programmation de la suppression du premier montre le second, mais aucun signe de suppression du premier. Comme précédemment, la suppression des connexions résout le problème. wdg.deleteLater() = wdg2 Widget() wdg2.move (300100) wdg2.show() –
@GrigoryMakeev. Non, ce n'est pas du tout ce qui se passe. Évidemment, l'encapsuleur Python n'est pas supprimé immédiatement, puisque vous y avez toujours une référence globale. Mais tout ce que vous avez à faire est 'del wdg', et' __del__' sera appelé une fois que Qt aura supprimé la partie C++. J'ai ajouté un autre exemple de débogage à ma réponse qui devrait montrer encore plus clairement ce qui se passe réellement. – ekhumoro
En effet cela fonctionne maintenant, merci! Une seule chose ne me reste pas claire: si j'ajoute gc.collect() juste après del wdg, le destructeur n'est pas encore appelé. Une idée pourquoi? –