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 :

Dialogue trop long entre deux fenêtres


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    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 830
    Billets dans le blog
    1
    Par défaut Dialogue trop long entre deux fenêtres
    Bonjour à tous. Mon problème est un peu bizarre.

    J'ai une fenêtre principale (appelons-là "work") qui contient un bouton X permettant d'appeler une seconde fenêtre "bdd" via un show().
    Cette seconde fenêtre "bdd" a pour but d'offrir à l'utilisateur un choix d'infos de la bdd. L'utilisateur choisit sa data et appuie sur "ok". A ce moment là, la fenêtre "bdd" appelle la fonction "work.load()" en lui passant les infos qui vont bien. Puis appelle la méthode "self.close()" pour se fermer.

    Dans la fenêtre de travail, la fonction "work.load()" charge depuis la bdd toutes les infos correspondantes au choix de l'utilisateur, fait certains traitements dessus et affiche le résultat dans différents widgets Qt.

    Le problème, c'est que tant que cette fonction "load()" (avec tous ses traitements) n'est pas terminée, la fenêtre "bdd" est toujours dans son appel à ce fameux load() et n'exécute donc pas le close(). Et si le traitement devient vraiment trop long, ben en fait elle n'exécute plus du tout ce close() et reste donc ouverte et gelée (et ne répond même pas au clic sur croix rouge). Et ce problème ne se présente que sur Windows XP et pas sous Linux.

    J'ai cependant trouvé 2 façons de détourner le problème
    1) je place l'appel à close avant l'appel à load. Il semblerait que malgré ce close(), la fenêtre "bdd" étant toujours en activité dans le programme peut encore exécuter des actions et donc peut appeler ce "work.load()"

    2) je relie la fenêtre "bdd" à un slot("close()") sur un signal "toto" quelconque et je fais envoyer ce signal "toto" en fin de fonction "work.load()".

    Je ne sais pas trop quelle solution serait la mieux appropriée (et la plus propre). D'ailleurs je ne sais même pas si c'est propre de faire directement appeler la fonction "work.load()" depuis la fenêtre "bdd". Je me demande s'il ne vaudrait pas mieux faire envoyer un signal par la fenêtre "bdd", signal récupéré par la fenêtre "work" et relié à la fonction "work.load()"...

    Merci
    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
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Je connais mal (voire pas) qt, mais je vois deux solutions possibles à ton problème:
    1) Lancer une thread pour effectuer ce travail. Attention toutefois, Qt a très certainement des rêgles pour le multithread.
    2) Il doit bien exister une méthode destroy, dispose ou quelque chose du genre qui détruit réellement les ressources et l'objet représentant la fenêtre. Elle remplacerait avantageusement le close.

    Mais comme tu le dis, ce n'est pas propre que la fenêtre bdd touche à work. N'est-il pas possible d'ouvrir cette bdd en modal, et d'effectuer le chargement dès qu'elle est fermée, ou que l'utilisateur clique sur OK ?

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    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 830
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Mais comme tu le dis, ce n'est pas propre que la fenêtre bdd touche à work. N'est-il pas possible d'ouvrir cette bdd en modal, et d'effectuer le chargement dès qu'elle est fermée, ou que l'utilisateur clique sur OK ?
    Hey, merci de ta réponse.

    Concernant la fenêtre bdd, elle est déjà ouverte en modal. Je ne peux cependant pas connecter le work.load() au bdd.closeEvent() car il y a aussi un bouton "annuler" permettant de fermer la fenêtre sans rien charger.

    Et donc pour faire comme tu dis un chargement dès que l'utilisateur clique sur "ok" sans appeler work.load(), il faut alors qu'elle envoie un signal récupérable par work. C'est effectivement possible...
    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
    Membre chevronné
    Homme Profil pro
    Responsable du parc et des réseaux de télécommunication
    Inscrit en
    Mai 2003
    Messages
    290
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Responsable du parc et des réseaux de télécommunication
    Secteur : Industrie

    Informations forums :
    Inscription : Mai 2003
    Messages : 290
    Par défaut
    Bonjour,
    Peut-être en utilisant "QCoreApplication.processEvents()":
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import sys
    from PyQt4 import QtCore, QtGui
     
    class MainWindow(QtGui.QMainWindow):
        def __init__(self):
            QtGui.QMainWindow.__init__(self)
            self.setWindowTitle(self.tr("Work"))
            x = QtGui.QPushButton("X")
            x.connect(x,QtCore.SIGNAL("clicked()"), self.showDialog)
            self.setCentralWidget(x)
     
        def showDialog(self):
            dialog=MyDialog(self)
            if dialog.exec_():
                data=dialog.editData.text()
                app=QtCore.QCoreApplication.instance()
                app.processEvents()
                print data*100000
     
    class MyDialog(QtGui.QDialog):
        def __init__(self, parent=None):
            QtGui.QDialog.__init__(self, parent)
            label=QtGui.QLabel(self.tr("Data"))
            self.editData=QtGui.QLineEdit()
            ok_button = QtGui.QPushButton(self.tr("OK"))
            cancel_button = QtGui.QPushButton(self.tr("Annuler"))
            self.connect(ok_button, QtCore.SIGNAL("clicked()"),
                            self, QtCore.SLOT("accept()"))
            self.connect(cancel_button, QtCore.SIGNAL("clicked()"),
                            self, QtCore.SLOT("reject()"))
     
            button_layout = QtGui.QHBoxLayout()
            button_layout.addWidget(ok_button)
            button_layout.addWidget(cancel_button)
     
            main_layout=QtGui.QGridLayout()
            main_layout.addWidget(label,0,0)
            main_layout.addWidget(self.editData,1,0)
            main_layout.addLayout(button_layout,2,0,1,2)
            self.setLayout(main_layout)
     
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        win = MainWindow()
        win.show()
        sys.exit(app.exec_())

  5. #5
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    il y a aussi un bouton "annuler" permettant de fermer la fenêtre sans rien charger.
    Dans la plupart des libs de GUI, il y a moyen de savoir sur quel bouton des dialogues l'utilisateur a cliqué.

    Si ce n'est pas le cas, tu peux intercepter, dans bdd, les clicks sur ok et annuler, et définir la valeur d'un attribut en conséquence. Ensuite, work peut récupérer cette valeur et agir selon ce qui convient.

    Ca ressemblerait à ça (m'enfin comme tu t'en doutes, ce serait plus propre avec des constantes que des strings.)
    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
    class Bdd:
        def onClickOK(self):
            self.__reponse= "ok"
            self.destroy() # Ou quoi que ce soit qui massacre la fenêtre
     
        def onClickAnnuler(self):
            self.__reponse = "annuler"
            self.destroy()
     
        @property
        def reponse(self):
            return self.__reponse
     
    class Work:
        def onClickLeFameuxBouton(self):
            bdd.showUp()
            bdd.attendreQu'elleSeFerme()
     
            if bdd.reponse == "ok":
                self.load()

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    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 830
    Billets dans le blog
    1
    Par défaut
    Merci de vos réponses. Le code de Pierjean est sympa et ressemble un peu à mon idée => la sous-fenêtre envoie un signal que la fenêtre principale récupère. Mais je n'avais pas pensé à accept() et reject().

    J'aime bien aussi le code d'Antoine_935 qui m'a au-moins appris l'instruction "@property" (je ne connaissais que l'instruction "@staticmethod") ce qui m'évitera de créer des méthodes get() à tout va comme je fais actuellement et qui me semble plus propre (l'objet work appelle la fenêtre et charge lui-même les infos) que le mien où c'est l'objet bdd qui appelle le chargement des infos pour work.
    Quand aux valeurs de retour, ce sera simple => les clefs de mes identifiants pour "ok" ou None pour "Annuler" => Même pas besoin de constantes.

    Je vais aussi examiner le code de Pierjean comme ça, j'apprendrai mieux à utiliser l'objet QProcessEvents

    Aujourd'hui, j'ai fait qq tests pour comprendre la cause de l'erreur et essayer d'arriver à un exemple complet minimal.

    Mon premier code était une sous-fenêtre qui, au moment du close, se met à faire de longs travaux sur la fenêtre principale. Changer le titre, les labels, etc. Et assortis de QThread.sleep() pour bien ralentir le truc. Mais j'ai eu beau monter jusqu'à 180 sleeps de 1s, j'arrivais pas à reproduire le pb.

    Puis je me suis souvenu que le traitement s'assortissait aussi de chargements d'objets en mémoire. Et dans mon cas, cet objet stockait 12000 infos venues de la bdd pour les traiter, les lier, etc.

    J'ai donc pensé à un problème de mémoire. Et j'ai donc créé un exemple minimal dans ce sens et j'ai réussi alors à reproduire le pb. Voici mon code
    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
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    #!/usr/bin/env python
    # coding: UTF-8 -*-
     
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class QtAppli(QApplication):
    	"Fenêtre de l'application"
     
    	# Constructeur fenêtre
    	def __init__(self,
    			argv):
     
    		# Message de contrôle
    		print "QtAppli (qt v%s, pyqt_v%s)" % (QT_VERSION_STR, PYQT_VERSION_STR)
     
    		# Appel constructeur de l'objet hértié
    		QApplication.__init__(self, argv)
     
    		# Widget principale
    		self.__mainWid=QMainWindow()
    		self.__mainWid.setCentralWidget(QWidget(self.__mainWid))
    		self.__mainWid.statusBar()
     
    		# Titre
    		self.__mainWid.setWindowTitle(QString.fromUtf8("Vérification Qt (v%s)" % QT_VERSION_STR))
     
    		# Un espace de rangement
    		layoutG=QVBoxLayout(self.__mainWid.centralWidget())
     
    		# Le bouton
    		btn=QPushButton(QString.fromUtf8("Appel bdd"))
    		self.connect(btn, SIGNAL("clicked()"), self.slotBdd)
    		layoutG.addWidget(btn)
     
    		# Pour quitter
    		quit=QPushButton(QString.fromUtf8("Quitter"))
    		self.connect(quit, SIGNAL("clicked()"), self.__mainWid, SLOT("close()"))
    		layoutG.addWidget(quit)
    	# __init__()
     
    	# Affichage et lancement application
    	def run(self):
    		self.__mainWid.show()
    		self.exec_()
    	# run()
     
    	# Slot qui affiche la fenêtre BDD simulée
    	def slotBdd(self):
    		print "bdd"
     
    		self.subWin=QDialog(self.__mainWid.centralWidget())
    		self.subWin.setModal(True)
    		self.subWin.setWindowTitle(QString.fromUtf8("Bdd..."))
     
    		layout=QVBoxLayout(self.subWin)
     
    		lab=QLabel(("<center><font size='+5'>Chargement Bdd</font></center>"))
    		layout.addWidget(lab)
     
    		btn=QPushButton("Charge...")
    		btn.connect(btn, SIGNAL("clicked()"), self.slotLoadBdd)
    		layout.addWidget(btn)
    		self.subWin.show()
    	# slotBdd()
     
    	# Slot qui simule le chargement bdd - Son but ici est simplement de pomper la ram
    	def slotLoadBdd(self):
    		print "slotLoadBdd"
     
    		# Un gros tableau 
    		tab=[cGrosTruc(i) for i in range(10000)]
    		print len(tab)
     
    		self.subWin.close()
    		print "chargement terminé"
    	# slotLoadBdd()
    # class QtAppli
     
    # Objet à la con qui bouffe de la mémoire
    class cGrosTruc:
    	def __init__(self, n):
    		print "cGrosTruc %d" % n
    		self.__n=n
    		self.tab=range(10000)
    	# __init__()
     
    	def __del__(self):
    		print "~cGrosTruc %d" % self.__n
    	# __del__()
    # class cGrosTruc
     
    Appli=QtAppli(sys.argv)
    Appli.run()
    Ce code assez simple crée une fenêtre principale avec un bouton d'appel bdd.
    Le bouton d'appel crée la sous-fenêtre avec un bouton d'action.
    La sous fenêtre, au lancement de l'action, commence d'abord par mémoriser "n" objets créés pour l'occasion, où chaque objet stocke un tableau de 10000 int avant de les libérer et se fermer.

    Et c'est en jouant avec ce "n" que j'ai pu arriver à des états de gel. Mais ça dépend aussi de la puissance du host qui exécute le script. Par exemple au bureau, avec n=5000 ça suffisait à créer le gel. Ici chez-moi avec un bi-processeur + 4Go de ram, m'a fallu mettre n=10000. Et bon, des fois ça gèle, des fois pas. Mais au-moins je cible mieux la raison => problème de mémoire...

    PS: Exemple fonctionnel uniquement sous Windows avec sa façon de m.... de gérer la mémoire et les processus. Sous Linux, zéro souci.
    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]

  7. #7
    Membre chevronné
    Homme Profil pro
    Responsable du parc et des réseaux de télécommunication
    Inscrit en
    Mai 2003
    Messages
    290
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Responsable du parc et des réseaux de télécommunication
    Secteur : Industrie

    Informations forums :
    Inscription : Mai 2003
    Messages : 290
    Par défaut
    Sans utiliser processEvents :
    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
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    #!/usr/bin/env python
    # coding: UTF-8 -*-
     
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class QtAppli(QApplication):
    	"Fen뵲e de l'application"
     
    	# Constructeur fenetre
    	def __init__(self,
    			argv):
     
    		# Message de controle
    		print "QtAppli (qt v%s, pyqt_v%s)" % (QT_VERSION_STR, PYQT_VERSION_STR)
     
    		# Appel constructeur de l'objet h
    		QApplication.__init__(self, argv)
     
    		# Widget principale
    		self.__mainWid=QMainWindow()
    		self.__mainWid.setCentralWidget(QWidget(self.__mainWid))
    		self.__mainWid.statusBar()
     
    		# Titre
    		self.__mainWid.setWindowTitle(QString.fromUtf8("V곩fication Qt (v%s)" % QT_VERSION_STR))
     
    		# Un espace de rangement
    		layoutG=QVBoxLayout(self.__mainWid.centralWidget())
     
    		# Le bouton
    		btn=QPushButton(QString.fromUtf8("Appel bdd"))
    		self.connect(btn, SIGNAL("clicked()"), self.slotBdd)
    		layoutG.addWidget(btn)
     
    		# Pour quitter
    		quit=QPushButton(QString.fromUtf8("Quitter"))
    		self.connect(quit, SIGNAL("clicked()"), self.__mainWid, SLOT("close()"))
    		layoutG.addWidget(quit)
    	# __init__()
     
    	# Affichage et lancement application
    	def run(self):
    		self.__mainWid.show()
    		self.exec_()
    	# run()
     
    	# Slot qui affiche la fenetre BDD simulée
    	def slotBdd(self):
    		print "bdd"
                    self.subWin=MyDialog()
                    if self.subWin.exec_():
                        self.slotLoadBdd()
    	# slotBdd()
     
    	# Slot qui simule le chargement bdd - Son but ici est simplement de pomper la ram
    	def slotLoadBdd(self):
    		print "slotLoadBdd"
     
    		# Un gros tableau 
    		tab=[cGrosTruc(i) for i in range(10000)]
    		print len(tab)
    		print "chargement terminé"
    	# slotLoadBdd()
    # class QtAppli
     
    class MyDialog(QDialog):
        def __init__(self, parent=None):
            QDialog.__init__(self, parent)
            layout=QVBoxLayout(self)
     
            lab=QLabel(("<center><font size='+5'>Chargement Bdd</font></center>"))
            layout.addWidget(lab)
     
            btn=QPushButton("Charge...")
            btn.connect(btn, SIGNAL("clicked()"),self, SLOT("accept()"))
            layout.addWidget(btn)
            self.setLayout(layout)
     
     
    # Objet à la con qui bouffe de la mémoire
    class cGrosTruc:
    	def __init__(self, n):
    		print "cGrosTruc %d" % n
    		self.__n=n
    		self.tab=range(10000)
    	# __init__()
     
    	def __del__(self):
    		print "~cGrosTruc %d" % self.__n
     
    	# __del__()
    # class cGrosTruc
     
    Appli=QtAppli(sys.argv)
    Appli.run()

  8. #8
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    PS: Exemple fonctionnel uniquement sous Windows avec sa façon de m.... de gérer la mémoire et les processus. Sous Linux, zéro souci.
    Windows ou non, il est toujours important de lancer les traitements lourds dans une thread différente que celle qui gère la GUI. Sinon, c'est le freeze assuré, puisque les événements doivent attendre la fin du gros traitement pour pouvoir être gérés.

Discussions similaires

  1. Réponses: 2
    Dernier message: 14/10/2009, 22h48
  2. Interaction croisée entre deux fenêtres
    Par womannosky dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 24/01/2008, 11h13
  3. Transmission d'informations entre deux fenêtres ouvertes
    Par lnplnp dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 02/02/2007, 12h04
  4. Passage d'information entre deux fenêtres
    Par kuuya dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 23/08/2005, 14h35
  5. Passer une variable entre deux fenêtres
    Par DeezerD dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 17/08/2005, 09h52

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