Le python landais

Interfaces graphiques.

Cette page est très largement inspirée de l'ouvrage de G. Swinnen, Apprendre à programmer avec Python 3.

Méthode de travail :

Nous allons ici mettre en place, à l'aide d'exemples, de très nombreuses techniques, pas toujours simples à comprendre, et encore moins à reproduire ensuite.

Pour en tirer profit, et pouvoir les ré-exploiter dans les projets, il faudra impérativement :
  • faire tourner les programmes pour voir ce qu'ils font,
  • étudier minutieusement le code, ligne à ligne, pour comprendre comment obtenir ce résultat,
  • conserver des programmes types, pour chaque objet ou méthode, dans le répertoire "Syntaxe_Python",
  • s'entraîner à faire les exercices sans copier-coller du cours, et en regardant de préférence vos programmes "Syntaxe Python", plutôt que les exemples du cours.
Chercher à gagner du temps sur cette partie de cours risquerait de compromettre le projet final :
tout le temps "perdu" ici, sera regagné 100 fois sur le projet !



Plan du chapitre :


1. Créer une fenêtre.
2. Meubler une fenêtre.
..... a. Widget Bouton.
..... b. Widget Label.
..... c. Widget Canevas.
3. Gestion des événements : event et binding.
4. Déplacer un item du canevas : les méthodes coords et move.
..... a. La méthode coords.
..... b. La méthode move.
5. Animation automatique : les fonctions récursives.




1) Créer une fenêtre.

Jusqu'à présent tous nos échanges avec Python avaient lieu en mode texte, à travers la console(l'interpréteur python ou encore le shell).  Nous souhaiterions à présent pouvoir créer des fenêtres graphiques qui nous permettront d'échanger avec Python via la souris ou le clavier. Ces fenêtres sont, en quelque sorte, des mini logiciels.
La librairie tkinter contient tous les outils dont nous aurons besoin pour créer une fenêtre.

Testez le script suivant :
#################### 1) Importation ####################
# Importation du module tkinter :
from tkinter import *


####################    2) Main     ####################
# Création de la fenêtre principale : objet Tk() stocké dans une variable fen :
fen = Tk()

# Lancement du gestionnaire d'événements
fen.mainloop() 

Désormais nos programmes ne sont plus destinés à être exécutés de la première à la dernière ligne mais à comporter tous les blocs d'instructions nécessaires pour répondre aux actions de l'utilisateur au clavier, à la souris, au joystick ...
Ainsi, dans un jeu vidéo, nous souhaitons, par exemple, que Mario Bros tourne à droite lorsque le joueur pousse son joystick vers la droite.

Nos programmes doivent désormais être pilotés par les événements qui se produisent (au clavier, à la souris, au joystick ...). Pour cela, nous devons céder la main à un réceptionnaire d'événements, capable de capter les actions de l'utilisateur et de les transmettre au programme pour les traiter.
C'est le rôle de l'instruction fen.mainloop() : il s'agit d'une boucle infinie qui va tourner jusqu'à la fermeture de la fenêtre, pour capter et transmettre toutes les actions de l'utilisateur.

pilotés_par_evts




2) Meubler une fenêtre.

La fenêtre du 1) est une « coquille vide », qui ne permet pas de faire quoi que ce soit. Pour la rendre plus intéressante, nous allons y placer des widgets (window gadgets : gadgets de fenêtre), qui vont permettre à l'utilisateur de réaliser différentes choses.

2)a) Widget Bouton.

Testez le script suivant :
#################### 1) Importation ####################
# Importation du module tkinter :
from tkinter import *


####################    2) Main     ####################
# Création de la fenêtre principale : objet Tk() stocké dans une variable fen :
fen = Tk()

# Création d'un widget Button (bouton Quitter)
bouton1 = Button(fen, text = 'Quitter', command = fen.destroy)

# Positionnement du widget dans la fenêtre avec la méthode pack() :
bouton1.pack()

# Lancement du gestionnaire d'événements
fen.mainloop() 

Remarques :
1. Examinons l'instruction : bouton1 = Button(fen, text = 'Quitter', command = fen.destroy)
Elle signifie : créer un widget « bouton poussoir », dans la fenêtre « fen », portant le texte « Quitter », et dont le résultat lorsqu'on l'actionne sera la commande « fen.destroy ».


2. On notera que de nombreuses actions sont désormais réalisées selon la syntaxe suivante :
objet.methode()
En programmation, les objets sont souvent dotés de méthodes permettant de les modifier ou d'agir sur eux : ce sont des fonctions qui s'appliquent à un objet particulier.
Ainsi, les objets fenêtre Tk() peuvent être détruits via la méthode destroy :
fen.destroy()


3. Une fois créé, le widget doit être positionné dans la fenêtre : c'est le rôle de la méthode pack de l'objet bouton1 :
bouton1.pack()



2)b) Widget Label.

Il s'agit d'un texte à positionner dans la fenêtre. Testez ainsi le code suivant :
#################### 1) Importation ####################
# Importation du module tkinter :
from tkinter import *


####################    2) Main     ####################
# Création de la fenêtre principale : objet Tk() stocké dans une variable fen :
fen = Tk()

# Création d'un widget Label (texte 'Bonjour tout le monde !')
label1 = Label(fen, text = 'Bonjour tout le monde !', fg = 'red')
# Positionnement du widget avec la méthode pack()
label1.pack()

# Lancement du gestionnaire d'événements
fen.mainloop() 
fg : foreground (premier plan) : permet de choisir la couleur du texte.

--> Exercices 1 et 2.




2)c) Widget Canevas.

Un canevas est une surface rectangulaire dont on spécifie la taille en pixels, et sur laquelle on peut dessiner ou placer des images. Créer le programme suivant :
#################### 1) Importation ####################
# Importation de la bibliothèque tkinter
from tkinter import *


####################    2) Main     ####################
# Création d'une fenêtre graphique stockée dans la variable fen :
fen=Tk()

# Création du widget Canvas :
can=Canvas(fen, bg='bisque', heigh=400, width=600)
# Positionnement du Canvas dans la fenêtre:
can.pack()

# On cède la main au réceptionnaire d’événements :
fen.mainloop()
bg  : background : arrière-plan : permet de choisir la couleur du fond ,
height  : hauteur en pixels du canevas souhaité,
width  : largeur en pixels du canevas souhaité.

A noter :

Le canevas est un widget. Les éléments que nous allons placer dedans(cercles, droites, textes...), s'appellent des items.

Dessinons maintenant :

--> Exercices 3, 4, 5.





3) Gestion des événements : event et binding.

################## 1) Importation de tkinter ##################
from tkinter import *


################# 2) Définition des fonctions #################
# La fonction position ci-dessous va modifier notre Label chaine 
# pour indiquer la position du clic.

# event.x : récupére l'abscisse de l'événement clic gauche
# event.y : récupère l'ordonnée de l'événement clic gauche

def position(event):
    chaine.configure(text = "Clic détecté en X =" + str(event.x) + ", Y =" + str(event.y))
    

############################ 3) MAIN ############################
# Création de la fenêtre :
fen = Tk()

# Création et positionnement du canevas :
can = Canvas(fen, width =600, height =500, bg="light yellow")
can.pack()

# Création et positionnement d'un label :
chaine = Label(fen)
chaine.pack()

# La méthode bind va lier l'événement clic gauche au canevas,
# déclenchant alors la fonction position.
can.bind("<Button-1>", position)

# On cède la main au réceptionnaire d'événements :
fen.mainloop()
Remarques :

--> Exercices 6, 7.





4) Déplacer un item du canevas : les méthodes coords et move.

Nous aimerions maintenant pouvoir déplacer un objet dans le canevas : un Mario Bros quand le joueur appuie sur flèche droite, par exemple.

4)a) La méthode coords.

La méthode "coords" du canevas permet de déplacer :
can.coords(objet_à_déplacer, new_x1, new_y1, new_x2, new_y2)

Exemple :
Nous allons ici lier l'événement "appui sur la flèche droite" à la fenêtre, le gestionnaire de l'événement étant alors la fonction avance. Testons le programme suivant :
######### 1) Importation ##############################
from tkinter import *


######### 2) Fonctions ################################
def avance(event) :
    global x
    x = x + 50
    can.coords(bal, x, 200, x + 50, 250)    # la méthode coords du canevas permet de déplacer
                                            # un objet à préciser (ici bal),
                                            # vers une nouvelle position, ici :    
                                            # la boite de points extrèmes : (x;200) et (x+50;250)  

######## 3) Main ######################################

# Création de la fenêtre et du canevas :
fen = Tk()
can = Canvas(fen, width = 600, height = 450, bg = 'ivory')
can.pack()

# Création de l'abscisse "x" de la balle et de la balle "bal" :
x = 0
bal = can.create_oval(x, 200, x + 50, 250, fill='blue')

# Mise en place du binding :
fen.bind("<Right>", avance)               # la fonction avance sera appelée à chaque fois
                                          # que l'on appuiera sur la touche "flèche droite".

# On cède la main au réceptionnaire d'événements :
fen.mainloop()

Remarquons ici qu'afin de pouvoir modifier l'abscisse "x" de la balle à l'intérieur de la fonction "avance", nous avons dû déclarer, dans la fonction "avance", qu'elle utiliserait la variable "x" comme une variable globale.


4)b) La méthode move.

Dans certains cas, nous pourrons préférer indiquer le déplacement à effectuer (+ 50 pixels en x, par exemple), plutôt que la nouvelle position où aller : on pourra alors utiliser la méthode move :
can.move(NomItem, dx, dy)
Déplace l'item concerné en ajoutant dx à son abscisse x, et dy à son ordonnées y.

--> Exercices 8, 9.





5) Animation automatique : les fonctions récursives.

Commençons par copier et exécuter le programme suivant :
######### 1) Importation ##############################
from tkinter import *


######### 2) Fonctions ################################
def moove() :
    global x
    x = x + 50
    can.coords(bal, x, 200, x + 50, 250)    
    fen.after(500, moove)                   # appeler à nouveau la fonction moove 500 ms plus tard


######## 3) Main ######################################

# Création de la fenêtre et du canevas :
fen = Tk()
can = Canvas(fen, width = 600, height = 450, bg = 'ivory')
can.pack()

# Création de l'abscisse "x" de la balle et de la balle "bal" :
x = 0
bal = can.create_oval(x, 200, x + 50, 250, fill='blue')

# On lance la fonction d'animation :
moove()

# On cède la main au réceptionnaire d'événements :
fen.mainloop()

La méthode "after" peut s'appliquer à un widget quelconque.
Nous l'appliquons ici à la fenêtre "fen" qui déclenche alors l'appel d'une fonction après un certain temps à préciser (ici 500 ms) :
fen.after(500, moove)

Dans le programme ci-dessus, alors que nous sommes déjà dans la fonction "moove", la fonction qui est appelée par la méthode "after" est la fonction "moove" elle-même : la fonction moove se ré-appelle elle-même !

La fonction sera donc relancée 500 ms plus tard, re-fera bouger la balle, et se ré-appellera 500 ms plus tard, et ainsi de suite ...
Nous lançons ainsi une boucle infinie : toutes les 500 ms, la fonction moove sera relancée.
En informatique, on appelle cela une fonction récursive : une fonction s'appelle elle-même.

--> Exercices 10, 11.



--> Mini projet 2.