Appeler un 2ème fenêtre, qui retourne une valeur
Date de publication : 29/10/2010 , Date de mise à jour : 29/10/2010
    ce tutoriel vous montre comment Appeler une 2ème fenêtre, qui retourne une valeur
   
		I. Introduction
			
			    
    
    
    
		
    II. Présentation générale
      
      
      
      
      
      
      
    
    III. Lancement d'une 2ème fenêtre à partir d'une 1ère fenêtre
      
      
      
    
    IV. Lancer une fenêtre 'modale'
      
      
      
      
      
    
    V. Transmission de l'information d'arrêt de la 2ème fenêtre, et de la donnée
      
      
      
      
      
      
      
      
      
    
    VI. Fermeture de la 2ème fenêtre
      
      
      
			
      
    
    VI. Positionnement des Widgets sur la fenêtre
      
      
      
      VI-A. Fenêtre QWidget: 
        
        
      
      VI-B. Fenêtre QMainWindow: 
        
        
        
      
    
    VIII. Code complet
      
      
      
      
    
  
		I. Introduction
			
			    Le code en bas de page permet de résoudre plusieurs problèmes: 
    
      - comment une fenêtre déjà ouverte peut en appeler une autre
 
      - comment cette 2ème fenêtre appelée peut être 'modale'
 
      - comment cette 2ème fenêtre peut se fermer
 
      - comment cette 2ème fenêtre peut retourner une donnée à la 1ère fenêtre lors de sa fermeture
 
    
    Et, accessoirement: 
    
      - comment positionner des widgets dans une fenêtre principale (QMainWindow)
 
    
		
    II. Présentation générale
      
      Les 2 fenêtres sont déclarées comme des classes. 
      
        - La 1ère fenêtre est une instance de la classe 'Principale'
 
        - La 2ème fenêtre est une instance de la classe 'Quelclient'
 
      
      On lance le programme: la 1ère fenêtre s'affiche. Elle porte une ligne de saisie et en dessous un bouton. 
      On appuie sur le bouton: la 2ème fenêtre s'affiche. Comme elle est 'modale', on ne peut plus accéder à la 1ère fenêtre tant que la seconde est ouverte. 
      La 2ème fenêtre contient aussi une ligne de saisie et un bouton. On tape un nom dans la ligne de saisie, et on appuie sur le bouton. La 2ème fenêtre se ferme, et. le nom qu'on a tapé dans la 2ème fenêtre est maintenant affiché dans la 1ère: cela prouve bien qu'à l'arrêt de la 2ème fenêtre, la donnée saisie a bien été transmise à la 1ère fenêtre. 
      Voilà comment ça marche: 
    
    III. Lancement d'une 2ème fenêtre à partir d'une 1ère fenêtre
      
      Le lancement de la 2ème fenêtre est très simple: on crée l'instance de classe, et on demande l'affichage: 
      self.quelclient = Quelclient()
self.quelclient.show()
  | 
 
    
    IV. Lancer une fenêtre 'modale'
      
      Si on veut que la 2ème fenêtre soit modale, il suffit d'ajouter avant le 'show()': 
      self.quelclient.setWindowModality(QtCore.Qt.ApplicationModal)
  | 
 
      Sans cette ligne, la fenêtre est 'non modale' par défaut au lancement. Si nécessaire, il est cependant possible de revenir au mode non modal avec la ligne suivante: 
      self.quelclient.setWindowModality(QtCore.Qt.NonModal)
  | 
 
    
    V. Transmission de l'information d'arrêt de la 2ème fenêtre, et de la donnée
      
      On a après résolu 2 problèmes différents, mais complémentaires: 
      
        - comment renvoyer une valeur de la 2ème fenêtre à la 1ère?
 
        - comment la 1ère fenêtre sait-elle que cette valeur est à sa disposition?
 
      
      On utilise pour cela un échange par signal qui, sur PyQt4 est la manière privilégiée pour faire communiquer 2 widgets: 
      
        - juste avant le lancement de la 2ème fenêtre, la 1ère fenêtre se prépare à recevoir un signal en provenance de la 2ème fenêtre, et à lancer dans ce cas la méthode clientchoisi:
 
      
      self.connect(self.quelclient, SIGNAL("fermeturequelclient(PyQt_PyObject)"), self.clientchoisi)
  | 
 
      
        - à la fermeture de la 2ème fenêtre, on émet un signal avec en argument la donnée à transmettre:
 
      
      self.emit(SIGNAL("fermeturequelclient(PyQt_PyObject)"), unicode(self.lineEdit.text()))
  | 
 
      Le nom du signal lui-même est crée dans ce programme (=n'existe pas dans la bibliothèque PyQt4), et l'argument PyQt_PyObject permet de passer n'importe quel objet Python, ici un simple texte en unicode, mais ça pourrait être, par exemple, une liste ou un dictionnaire. La donnée transmise est passé dans le 'x' de la méthode clientchoisi(self, x). 
    
    VI. Fermeture de la 2ème fenêtre
      
      Attention, tel que programmé, il faut arrêter la 2ème fenêtre avec le bouton. Si on l'arrête d'une autre manière (avec la croix en haut de la fenêtre p. ex.), le signal ne sera pas émis, et donc la donnée non transmise. Cela peut être souhaitable, mais pas forcément. Dans le cas où on veut que le signal soit émis dans tous les cas, il faut remplacer la méthode ok_m par: 
      def ok_m(self):
    self.close()
 
def closeEvent(self, event):
    self.emit(SIGNAL("fermeturequelclient(PyQt_PyObject)"), unicode(self.lineEdit.text()))
  | 
 
			
				ici, la méthode closeEvent surcharge la méthode déjà existante de la classe de la fenêtre (ici QWidget) et est lancé dans tous les cas d'arrêt de la fenêtre. 
On pourrait d'ailleurs lancer une fenêtre-message du genre "Etes-vous sûr?" avant de fermer effectivement la fenêtre, ou d'annuler la fermeture, comme suit: 
			
def closeEvent(self, event):
    reply = QtGui.QMessageBox.question(self, 
        u"Demande de fermeture",
        u"Etes-vous sûr de vouloir fermer la fenêtre?", 
        QtGui.QMessageBox.Yes, 
        QtGui.QMessageBox.No)
    if reply == QtGui.QMessageBox.Yes:
        event.accept()
    else:
        event.ignore()
  | 
 
      Avec un tel code, on pourrait aussi retourner un message différent selon le mode de fermeture. Il suffirait d'une variable 'drapeau' initialisée à False (ex: finnormale=False) qui passerait à True en passant dans la méthode ok_m, et qui serait testé dans closeEvent. 
    
    VI. Positionnement des Widgets sur la fenêtre
      
      Dernier point, ce code permet de voir que le positionnement des widgets sur la fenêtre avec QGridLayout est légèrement différent entre une fenêtre complète de type QMainWindow (c'est à dire avec possibilité de menus, de barre d'outil, de barre d'état, .), et une fenêtre courante de type QWidget. 
      On voit bien la différence entre les 2 types de fenêtre: 
      VI-A. Fenêtre QWidget: 
        
        posit = QtGui.QGridLayout()
posit.addWidget(self.lineEdit, 0, 0)
posit.addWidget(self.bouton, 1, 0)
self.setLayout(posit)
  | 
 
      
      VI-B. Fenêtre QMainWindow: 
        
        self.setCentralWidget(QtGui.QFrame())
...
posit = QtGui.QGridLayout()
posit.addWidget(self.lineEdit, 0, 0)
posit.addWidget(self.bouton, 1, 0)
self.centralWidget().setLayout(posit)
  | 
 
        En effet, pour la fenêtre de type QMainWindow, il existe déjà un layout, pour positionner le menu et les autres éléments, et il n'est pas possible de l'utiliser. Il faut donc ajouter un 'fond' QFrame', qui sera appelé 'centralWidget', ajouter les widgets ayant ce fond comme parent, et positionner les widgets sur ce fond. 
      
    
    VIII. Code complet
      
      Voilà le code complet: un simple copier-coller devrait vous permettre d'exécuter chez vous, mais il vous faut bien sûr PyQt4 en plus de Python. 
      Ce code a été écrit sous Python 2.7. Sa version Python 3.x ne devrait pas être très différente. Il est de plus multiplateforme (au moins Windows-Linux). 
      
 
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import SIGNAL
 
class Quelclient(QtGui.QWidget):
 
    def __init__(self, parent=None):
        super(Quelclient, self).__init__(parent)
        self.setWindowTitle(u"Quel client")
 
        
        self.lineEdit = QtGui.QLineEdit(self)
        
        self.bouton = QtGui.QPushButton(u"Ok", self)
        self.bouton.clicked.connect(self.ok_m)
        
        posit = QtGui.QGridLayout()
        posit.addWidget(self.lineEdit, 0, 0)
        posit.addWidget(self.bouton, 1, 0)
        self.setLayout(posit)
 
    def ok_m(self):
        
        self.emit(SIGNAL("fermeturequelclient(PyQt_PyObject)"), unicode(self.lineEdit.text())) 
        
        self.close()
 
class Principal(QtGui.QMainWindow):
 
    def __init__(self, parent=None):
        """Initialise la fenêtre"""
        super(Principal, self).__init__(parent)
        self.setWindowTitle(u"Code test")
 
        
        self.setCentralWidget(QtGui.QFrame())
        
        self.lineEdit = QtGui.QLineEdit(self.centralWidget())
        
        self.bouton = QtGui.QPushButton(u"Sélectionnez un client !", self.centralWidget())
        self.bouton.clicked.connect(self.quelclient_m)
        
        posit = QtGui.QGridLayout()
        posit.addWidget(self.lineEdit, 0, 0)
        posit.addWidget(self.bouton, 1, 0)
        self.centralWidget().setLayout(posit)
 
    def quelclient_m(self):
        """Lance la 2ème fenêtre"""
        self.quelclient = Quelclient()
        
        self.connect(self.quelclient, SIGNAL("fermeturequelclient(PyQt_PyObject)"), self.clientchoisi) 
        
        self.quelclient.setWindowModality(QtCore.Qt.ApplicationModal)
        
        self.quelclient.show()
 
    def clientchoisi(self, x):
        """affiche le résultat x transmis par le signal à l'arrêt de la 2ème fenêtre"""
        self.lineEdit.setText(x)
 
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('plastique'))
    main = Principal()
    main.show()
    sys.exit(app.exec_())
  | 
 
    
  


 
		
Copyright © 2010 
    .
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.