IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

[QListWidget] Limites et nettoyage [QtGui]


Sujet :

PyQt Python

  1. #1
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut [QListWidget] Limites et nettoyage
    Bonjour à tous

    Depuis quelque temps j'étais embêté dans mes applications PyQt car elles plantaient à la fermeture. Cet évènement ne se produit que sur les applications tournant sous Windows (quoique j'ai eu quelques cas rares de memory fault sous Linux) et en plus pas tout le temps.

    Je me suis attaqué récemment à ce soucis. J'avais une application utilisant des fenêtres similaires, chaque fenêtre gérant une table de bdd. Or quand j'ouvre une fenêtre X avant de quitter ça plante mais quand j'ouvre une fenêtre Y ça ne plante pas. De quoi devenir fou (chaque fenêtre faisant la même chose, avec ajout, modif, suppression dans sa table, j'avais alors calquée la seconde sur la première). Après quelques heures de tests, j'ai découvert que cela se passait avec les QListWidget lorsque le nombre d'items dépassait une certaine limite (variable en plus). Dans la fenêtre X j'avais une quinzaine de lignes et dans la Y j'en avais quelques centaines.

    Pensant que cela était dû à ma façon de programmer (je progamme mes IHM à la main) j'ai rapidement développé avec designer une petite appli destinée à tester la QListWidget. Or même avec le code généré par designer+pyuic4, cela se produit aussi. Je clique sur la croix rouge, la fenêtre se ferme et windows me dit que python.exe a cessé de fonctionner.

    Toutefois, si je rajoute un bouton "Quitter" et que je connecte ce bouton "quitter" à un slot dédié lequel se charge de nettoyer ma QListWidget (clear()) avant de fermer, alors plus de soucis.

    Je vous mets le code de test
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    # Form implementation generated from reading ui file 'list.ui'
    #
    # Created: Fri Apr 24 20:32:07 2015
    #	  by: PyQt4 UI code generator 4.11.3
    #
    # WARNING! All changes made in this file will be lost!
     
    from PyQt4 import QtCore, QtGui
    import sys
     
    try:
    	_fromUtf8 = QtCore.QString.fromUtf8
    except AttributeError:
    	def _fromUtf8(s):
    		return s
     
    try:
    	_encoding = QtGui.QApplication.UnicodeUTF8
    	def _translate(context, text, disambig):
    		return QtGui.QApplication.translate(context, text, disambig, _encoding)
    except AttributeError:
    	def _translate(context, text, disambig):
    		return QtGui.QApplication.translate(context, text, disambig)
     
    class Ui_Dialog(object):
    	__limit=int(sys.argv[1] if len(sys.argv) > 1 else 627)		# 627 semble être la limite max
     
    	def setupUi(self, Dialog):
    		self.dialog=Dialog
    		Dialog.setObjectName(_fromUtf8("Dialog"))
    		Dialog.resize(480, 356)
    		self.verticalLayout = QtGui.QVBoxLayout(Dialog)
    		self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
    		self.listWidget = QtGui.QListWidget(Dialog)
    		self.listWidget.setMaximumSize(QtCore.QSize(291, 192))
    		self.listWidget.setObjectName(_fromUtf8("listWidget"))
     
    		# Permet de générer n éléments de liste (n passé en paramètre)
    		for i in xrange(self.__limit):
    			item = QtGui.QListWidgetItem()
    			self.listWidget.addItem(item)
     
    		self.verticalLayout.addWidget(self.listWidget)
    		self.btn=QtGui.QPushButton(Dialog)
    		self.btn.setObjectName(_fromUtf8("pushButton"))
    		QtCore.QObject.connect(self.btn, QtCore.SIGNAL(_fromUtf8("clicked()")), self.xxx)
    		self.verticalLayout.addWidget(self.btn)
     
    		self.retranslateUi(Dialog)
    		QtCore.QMetaObject.connectSlotsByName(Dialog)
     
    	def xxx(self):
    		self.listWidget.clear()
    		self.dialog.close()
     
    	def retranslateUi(self, Dialog):
    		Dialog.setWindowTitle(_translate("Dialog", "Test %d items" % self.__limit, None))
    		__sortingEnabled = self.listWidget.isSortingEnabled()
    		self.listWidget.setSortingEnabled(False)
    		for i in xrange(self.__limit):
    			item=self.listWidget.item(i)
    			item.setText(_translate('Dialog', 'Elément %d' % (i+1), None))
    		self.listWidget.setSortingEnabled(__sortingEnabled)
    		self.btn.setText(_translate('Dialog', 'Quitter', None))
     
    if __name__ == "__main__":
    	import sys
    	app = QtGui.QApplication(sys.argv)
    	Dialog = QtGui.QDialog()
    	ui = Ui_Dialog()
    	ui.setupUi(Dialog)
    	Dialog.show()
    	sys.exit(app.exec_())

    Suffit d'enregistrer ce code dans un fichier "toto.py" et de l'appeler en lui passant le nb d'items à créer (dépassant 1000). Ensuite si on clique sur "quitter" tout va bien (la liste est nettoyée) mais si on clique sur la croix rouge alors plantage (chez-moi)
    Pour info je suis sous Windows 7 avec Python 2.7.9, Qt 4.8.6, sip 4.16.7 et PyQt 4.11.3. Qt, sip et PyQt ayant été compilés depuis les sources téléchargés en utilisant MinGW.

    Voilà. Si quelqu'un avait déjà rencontré le même problème, ou si inversement cela ne se passe que chez-moi ça m'intéresserait de le savoir.

    Merci à tous
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #2
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 300
    Points : 6 780
    Points
    6 780
    Par défaut
    Salut,

    Je confirme.

    En tous cas sous Linux.

    Mais ce problème est connu et provient généralement de la destruction d'objets Qt et du garbage collector qui se marche sur les pieds.

    Le moment où le gc de Python intervient est considéré comme imprédictible mais normalement en rajoutant ceci dans la classe de la fenêtre principale, le message d'erreur post-mortem disparaît.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        def closeEvent(self, event):
            # Ici sauvegarde des préférences, attente d'éventuel thread ...
            QtCore.QCoreApplication.processEvents()
            sys.exit()

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par VinsS Voir le message
    mais normalement en rajoutant ceci dans la classe de la fenêtre principale, le message d'erreur post-mortem disparaît.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        def closeEvent(self, event):
            # Ici sauvegarde des préférences, attente d'éventuel thread ...
            QtCore.QCoreApplication.processEvents()
            sys.exit()
    Merci mais non malheureusement ça ne fonctionne pas. J'ai donc surchargé le QDialog (qui est ici la fenêtre principale) comme ceci
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class myDialog(QtGui.QDialog):
    	def __init__(self, *args, **kwargs):
    		QtGui.QDialog.__init__(self, *args, **kwargs)
     
    	def closeEvent(self, e):
    		print "QtDialog.event [%s]" % e
    		QtCore.QCoreApplication.processEvents()
    		sys.exit()
     
    if __name__ == "__main__":
    	import sys
    	app = QtGui.QApplication(sys.argv)
    	Dialog = myDialog()
    	ui = Ui_Dialog()
    	ui.setupUi(Dialog)
    	Dialog.show()
    	sys.exit(app.exec_())

    Et non, j'ai bien le print qui montre que je passe dans le closeEvent mais le message post-mortem revient quand-même. Mais je suis rassuré que cela ne vienne pas de moi. C'est pas très grave, je gérais déjà la fermeture de mes fenêtres (pour mémoriser des trucs). Je vais simplement y rajouter un nettoyage de mes QListWidget et puis ça le fera (et ça me réconciliera avec mes habitudes C où je veille bien à libérer ce qui a été alloué).

    [edit] Ok, ça fonctionne si on place le code dans le QAppli et non le QDialog. Merci à toi de ton aide

    Toutefois, petite bizarrerie, le QListWidget possède bien le closeEvent en héritage mais même quand je le rajoute, il ne s'exécute pas...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class myListWidget(QtGui.QListWidget):
    	def __init__(self, *args, **kwargs):
    		QtGui.QListWidget.__init__(self, *args, **kwargs)
     
    	def closeEvent(self, e):
    		print "QtListWidget.event [%s]" % e
    Peut-être qu'il faut que cet objet soit utilisé en tant que "MainWindow" pour que cela fonctionne...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ai déjà eu ce genre de problèmes, et j'ai essayé plusieurs choses qui marchent selon le cas rencontré.

    D'après ce que j'ai compris, il provient d'une destruction des objets dans le désordre.

    Quand j'ai ce problème, voilà ce que j'essaie:

    - il peut provenir d'une création d'objet sans préciser son propriétaire => il suffit d'ajouter le propriétaire

    - il peut provenir d'un manque de destruction de ressources (pas forcément graphique) => j'ajoute une méthode "closeEvent" pour faire le ménage. Je rencontre souvent ce problème avec les bases de données.

    - il peut provenir du fait que la fermeture d'une fenêtre n'entraine pas forcément sa destruction => j'ajoute "fenetre..setAttribute(QtCore.Qt.WA_DeleteOnClose)" juste avant la méthode show()

    Chez moi pour le code de test proposé: c'est la 3ème solution qui marche: il suffit d'ajouter "Dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)" avant "Dialog.show()".

    A confirmer chez vous!

    [edit] concernant le pb closeEvent du QListWidget, ça marche chez moi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from PyQt4 import QtCore, QtGui
     
    class myListWidget(QtGui.QListWidget):
        def __init__(self, *args, **kwargs):
            QtGui.QListWidget.__init__(self, *args, **kwargs)
     
        def closeEvent(self, e):
            print "QtListWidget.event [%s]" % e
     
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        lwidget = myListWidget()
        lwidget.show()
        sys.exit(app.exec_())
    L'exécution ferme bien la fenêtre en affichant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QtListWidget.event [<PyQt4.QtGui.QCloseEvent object at 0x02387440>]
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Chez moi pour le code de test proposé: c'est la 3ème solution qui marche: il suffit d'ajouter "Dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)" avant "Dialog.show()".
    A confirmer chez vous!
    Super, ça marche nickel - Merci de votre aide toujours efficace

    [edit]Toutefois, cela serait-il une mauvaise chose si je nettoyais ma QListWidget quand-même ou alors je peux faire maintenant totalement confiance au gc ???

    Citation Envoyé par tyrtamos Voir le message
    [edit] concernant le pb closeEvent du QListWidget, ça marche chez moi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from PyQt4 import QtCore, QtGui
     
    class myListWidget(QtGui.QListWidget):
        def __init__(self, *args, **kwargs):
            QtGui.QListWidget.__init__(self, *args, **kwargs)
     
        def closeEvent(self, e):
            print "QtListWidget.event [%s]" % e
     
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        lwidget = myListWidget()
        lwidget.show()
        sys.exit(app.exec_())
    L'exécution ferme bien la fenêtre en affichant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QtListWidget.event [<PyQt4.QtGui.QCloseEvent object at 0x02387440>]
    Oui, si on affiche le QListWidget en direct via un show() alors il intercepte bien le closeEvent. Mais il ne l'intercepte pas si le QListWidget se trouve dans un QDialog. C'est le QDialog qui intercepte le closeEvent. A priori quand on le sait ce n'est pas dérangeant (on peut programmer le QDialog pour qu'il nettoie ses enfants) mais au départ j'étais persuadé que le closeEvent du parent se répercutait tout seul sur tous les enfants créés...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour Sve@r,

    Citation Envoyé par Sve@r Voir le message
    Oui, si on affiche le QListWidget en direct via un show() alors il intercepte bien le closeEvent. Mais il ne l'intercepte pas si le QListWidget se trouve dans un QDialog. C'est le QDialog qui intercepte le closeEvent. A priori quand on le sait ce n'est pas dérangeant (on peut programmer le QDialog pour qu'il nettoie ses enfants) mais au départ j'étais persuadé que le closeEvent du parent se répercutait tout seul sur tous les enfants créés...
    Effectivement, le closeEvent des objets contenus dans une fenêtre n'est pas appelé à la fermeture de cette fenêtre.

    Cela veut dire que s'il y a du ménage à faire dans les données, il faut le faire dans le closeEvent de la fenêtre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Python 2.7
     
     
    from PyQt4 import QtCore, QtGui
     
    class Fenetre(QtGui.QWidget):
     
        def __init__(self, *args, **kwargs):
            super(Fenetre, self).__init__(*args, **kwargs)
     
            self.lwidget = QtGui.QListWidget(self)
            self.lwidget.addItems(["toto", "titi", "tata"]) 
     
        def closeEvent(self, event):
            print "Suppression des données du QListWidget"
            self.lwidget.clear()
            print("et fermeture de la fenêtre")
     
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        fenetre = Fenetre()
        fenetre.show()
        sys.exit(app.exec_())
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Limitation DirectSound
    Par Sub0 dans le forum DirectX
    Réponses: 1
    Dernier message: 28/02/2003, 11h21
  2. [Turbo Pascal] Limite de la mémoire virtuelle
    Par moon tiger dans le forum Turbo Pascal
    Réponses: 12
    Dernier message: 08/02/2003, 22h30
  3. Limiter le déplacement de la souris
    Par el_bouleto dans le forum C++Builder
    Réponses: 4
    Dernier message: 08/11/2002, 23h56
  4. Comment limiter les mouvements du curseur??
    Par scorpiwolf dans le forum C++Builder
    Réponses: 9
    Dernier message: 07/07/2002, 22h09
  5. [Comparatifs] Limites nombres tables et quantité de données
    Par benj63 dans le forum Décisions SGBD
    Réponses: 7
    Dernier message: 13/06/2002, 21h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo