Interface Graphique : Gestion des images

Les interfaces graphiques servent à rendre les programmes plus conviviaux. Elles sont pratiques à utiliser mais elles demandent un peu de temps pour les concevoir.
Un programme fonctionne de manière différente. Il n’exécute plus successivement les instructions mais attend un événement - pression d’une touche du clavier, clic de souris - pour exécuter une fonction. C’est comme si le programme avait une multitude de points d’entrée.

Il existe plusieurs modules permettant d’exploiter les interfaces graphiques. Le plus simple est le module tkinter présent lors de l’installation du langage Python. Ce module est simple mais limité. Le module wxPython est plus complet mais un peu plus compliqué dans son utilisation. Toutefois, le fonctionnement des interfaces graphiques sous un module ou un autre est identique. C’est pourquoi ce chapitre n’en présentera qu’un seul, le module tkinter. Pour d’autres modules, les noms de classes changent mais la logique reste la même : il s’agit d’associer des événements à des parties du programme Python.

Les interfaces graphiques évoluent sans doute plus vite que les autres modules, des composantes de plus en plus complexes apparaissent régulièrement. Un module comme wxPython change de version plusieurs fois par an. Il est possible de trouver sur Internet des liens qui donnent des exemples de programme. Une excellente source de documentation sont les forums de discussion qui sont un lieu où des programmeurs échangent questions et réponses. Un message d’erreur entré sur un moteur de recherche Internet permet souvent de tomber sur des échanges de ce type, sur des problèmes résolus par d’autres.

Introduction

Un programme muni d’une interface graphique fonctionne différemment d’un programme classique. Un programme classique est une succession presque linéaire d’instructions.

Il y a un début ou point d’entrée du programme et aucun événement extérieur ne vient troubler son déroulement Avec une interface graphique, le point d’entrée du programme est masqué : il est pris en compte automatiquement. Du point de vue du programmeur, le programme a plusieurs points d’entrée : une simple fenêtre avec deux boutons propose deux façons de commencer et il faut prévoir une action associée à chaque bouton. La conception d’une interface graphique se déroule généralement selon deux étapes. La première consiste à dessiner l’interface, c’est-à-dire choisir une position pour les objets de la fenêtre (bouton, zone de saisie, liste déroulante, ...). La seconde étape définit le fonctionnement de la fenêtre, c’est-à-dire associe à chaque objet des fonctions qui seront exécutées si un tel événement se réalise (pression d’un bouton, pression d’une touche, ...).

Pour le moment, nous allons supposer que ces deux étapes sont scindées même si elles sont parfois entremêlées lorsqu’un événement implique la modification de l’apparence de la fenêtre. Nous allons décrire des objets que propose le module tkinter. Ensuite on présente la manière de les disposer dans une fenêtre et on décrit les événements et le moyen de les relier à des fonctions du programme. Enfin nous allons voir comment gérer des images c’est-à-dire comment dessiner sur la fenêtre, placer des images et les déplacer.

Nous allons voir dans ce chapitre comment dessiner sur la fenêtre, placer des images et les déplacer.

Le widget Canvas

Le module tkinter dispose d’un widget nommé Canvas destiné à dessiner sur la fenêtre. Si on veut créer un canvas nommé C, l’appel se fait comme pour tous les widgets de la manière suivante :

C = Canvas(emplacement, options)

Où emplacement est le nom de la fenêtre sur laquelle il est déposé et options indique les différentes propriétés du Canvas comme :

OptionsEffet
bg Précise la couleur du fond du Canvas.
width
height
Précisent respectivement la largeur et la hauteur du Canvas (en pixels).
Attention

Il faut penser comme tous les widgets, une fois le Canvas créé, il faut encore le placer sur la fenêtre avec la méthode place, grid ou pack présentée au paragraphe précédent.

Dessiner sur le canvas

Sur un Canvas ici noté C, on peut dessiner différents types d’objets que l’on appelle item. Nous n’allons pas aborder qu’une infime partie des possibilités offertes par Canvas.

Une ligne

L’instruction : ligne = C.create_line(x1,y1,x2,y2,options),
dessine un segment reliant le point de coordonnées (x1 ; y1) (inclus) au point de coordonnées (x2 ; y2) (exclu). On peut préciser des options :

OptionsEffet
width Epaisseur de la ligne en pixels.
fillCouleur de la ligne.

Exemple 9


from tkinter import *
fen = Tk()		# crée la fenêtre nommée fen
fen.geometry("200x100")
fen.title("Segment")
C = Canvas(fen, width = 200, height = 100, bg = 'black')
C.place(x=0,y=0)
ligne = C.create_line(0,0,180,80,width = 2,fill = 'red')
fen.mainloop()

On obtient :

exemple9

Rectangle

L’instruction : rectangle = C.create_rectangle(x1,y1,x2,y2,options)
dessine un rectangle dont deux sommets opposés (le premier en haut à gauche et le second à droite en bas) ont pour coordonnées (x1 ; y1) et (x2 ; y2).

On peut également préciser des options :

OptionsEffet
width Epaisseur du trait en pixels.
outlineCouleur du trait.
fillCouleur de l’intérieur du rectangle

Exemple 10


from tkinter import *
fen = Tk()		# crée la fenêtre nommée fen
fen.geometry("200x100")
fen.title("Segment") 
C = Canvas(fen, width = 200, height = 100, bg = 'black')
C.place(x=0,y=0)
rectanle = C.create_rectangle(70,10,110,80,width = 3,
     outline = 'red', fill = 'yellow')
fen.mainloop()

On obtient :

exemple10

Une ellipse ou un cercle

dessine une ellipse inscrite dans un rectangle (imaginaire) dont deux sommets opposés (le premier en haut à gauche et le second à droite en bas) ont pour coordonnées (x1 ; y1) et (x2 ; y2).

create_oval possède les mêmes options que create_rectangle.

Remarque

Pour dessiner un cercle les coordonnées (x1 ; y1) et (x2 ; y2) doivent vérifier la condition : x2 – x1 = y2 – y1.

Exemple 11


from tkinter import *
fen = Tk()		# crée la fenêtre nommée fen
fen.geometry("200x100") 
fen.title("Disque") 
C = Canvas(fen, width = 200, height = 100, bg = 'white')
C.place(x=0,y=0)
cercle = C.create_oval(70,10,150,90,width = 3,outline = 'red', fill = 'blue')
fen.mainloop()

On obtient :

exemple11

Afficher un texte sur un Canvas

L’instruction : texte = C.create_text(x, y, options)
permet d'afficher le texte à partir du point de coordonnées (x ; y) du Canvas C créé.

On peut également préciser des options :

OptionsEffet
anchor Précise la position d’attache du texte par rapport au point de coordonnées (x ; y).
Par défaut, le texte est centré autour du point indiqué (‘center’). Il peut aussi prendre les valeurs ‘n’, ‘e’, ‘s’, ‘w’, ‘nw’, ‘ne’, ‘sw’ ou ‘se’
fillCouleur du texte.
fontPolice de caractères utilisée (comme pour le widget Label)
textUne chaîne de caractères contenant le texte à afficher.

Exemple 12


from tkinter import *
import random

fenetre=Tk()
fenetre.title("Bonne année")
fenetre.geometry("500x300")
Fond=Canvas(fenetre,width=500,height=300,bg="black")
Fond.place(x=0,y=0)
#Lignes
Fond.create_line(0,50,250,10,fill='green')
Fond.create_line(250,10,500,50,fill='green')
Fond.create_line(250,20,500,80,fill='green')
Fond.create_line(0,80,250,20,fill='green')
#Boules 
Fond.create_oval(400,200,450,250,fill= 'cyan')
Fond.create_oval(100,80,160,140,fill= 'purple')
Fond.create_oval(200,100,240,140,fill= 'white')
Fond.create_oval(220,220,250,250,fill= 'yellow')

Fond.create_text(250,150,text="Bonne année 2015 !",font=("Arial",30), fill='red')

fenetre.mainloop()

On obtient :

exemple12

Placer des images

Il existe différents formats. Le module tkinter travaille très bien avec les images au format GIF (non animées). Ce sont des images en 256 couleurs.

Placer une image n’est pas plus complexe qu’afficher un texte, cela se passe en deux étapes :

  • Etape 1 : On charge l’image dans une variable globale (ici fichierimg) à l’aide de la commande

    Fichierimg = PhotoImage(file ="image.gif")

  • Etape 2 : place l’image au point de coordonnées x, y sur le Canvas C avec la méthode

    Img = C.create_image(x, y, options)

Le paramètre options permet de renseigner les propriétés. Par exemple

OptionsEffet
imageIndique l’image à afficher (fichierimg dans l’exemple que nous avons présenté).
anchor Comme pour objet text, indique la position du point de référence (x ; y) par rapport au reste de l’image.
Par défaut, le texte est centré autour du point indiqué). Il peut prendre les valeurs ‘n’, ‘e’, ‘s’, ‘w’, ‘nw’, ‘ne’, ‘sw’, ‘se’ ou ‘center’ (Par défaut).

Remarque

Comme pour les fichiers, vous pouvez indiquer un chemin relatif pour l’emplacement de l’image si celle-ci ne se trouve pas au même endroit que votre programme.

Exemple 13

L'objectif de cet exemple est de créer un fichier texte fichierISN.txt et placer des images à la place des caractères.


from tkinter import *

#Ouverture d'un fichier nommé fichierISN.txt pour y écrire du texte

fichier = open("fichierISN.txt","w")
liste = [[' ' for j in range(20)] for i in range(10)]
liste[0][0]='C'
for i in range(1,9):
    liste[i][i+10] = 'N'
for i in range(2,6):
    liste[i][i] = 'S'
for i in range(10):
    for j in range(20):
        if j ==10 or j == 19 :
            liste[i][j] = 'N'
        elif i>1 and j == 0 :
            liste[i][j] = 'I'
        elif (i == 0 or i == 9) and (j==3 or j == 5 or j == 7) :
            liste[i][j] = 'S'
        elif (i == 1 or i == 8) and (j == 2 or j == 8) :
            liste[i][j] = 'S'
        elif (i == 6 or i == 7) and j == i+1 :
            liste[i][j] = 'S'
for i in range(10):
    chaine = ''
    for j in range(20):
        chaine = chaine+''+str(liste[i][j])
    print(chaine,file=fichier)
fichier.close()

#Transformation des caractères du fichier texte fichierISN en images

fenetre=Tk()
fenetre.title("Plateforme !")
fenetre.geometry("880x500")
Fond=Canvas(fenetre,width=880,height=500,bg="#BBBBF9")
Fond.place(x=0,y=0)


F_Acier=PhotoImage(file="acier.gif")
F_Brique=PhotoImage(file="brique.gif")
F_coc=PhotoImage(file="coc.gif")

x, y = 10, 30
fichier = open("fichierISN.txt", 'r')
for ligne in fichier :
    for i in range(len(ligne)) :
        case = ligne[i]
        if case == 'S' :
            Fond.create_image(x,y,image=F_Acier,anchor="nw")
        if case == 'I':
            Fond.create_image(x,y,image=F_Brique,anchor="nw")
        if case == 'N':
            Fond.create_image(x,y,image=F_Brique,anchor="nw")
        if case == 'C':
            Fond.create_image(x,y,image=F_coc,anchor="nw")
        x = x + 40
    x = 10
    y = y + 40
fichier.close()

fenetre.mainloop()

On obtient :

exemple13

Modifier les items en cours de programme

A présent que nous savons placer des images et autres items sur la fenêtre, il est important de pouvoir les déplacer ou les modifier au fur et à mesure du jeu.

Changer les propriétés

Diverses méthodes sur le widget Canvas permettent de modifier les propriétés des items placés :

MéthodesEffet
C.delete(item)Efface l’item 'item' du Canvas C.
C.delete(ALL)Efface tout ce qui se trouve sur le Canvas C.
C.coords(item,x0, y0)
C.coords(item,x1, y1, x2, y2))
Modifie les coordonnées de l’item 'item' (2 ou 4 selon l’objet).
C.itemconfig(item, options)Permet de modifier une ou plusieurs options de l’item 'item'.
C.itemcget(item, prop)Renvoie la valeur de la propriété prop de l’item 'item'.

Si on souhaite modifier plusieurs options d’un coup, on sépare les propriétés par une virgule, par exemple :


....
C.itemconfig(T , text = 'Gagné ', fill = 'red')
...

transforme le contenu du texte T placé sur le Canvas C en "Gagné" écrit en rouge.

Exemple 14


from tkinter import *
fenetre=Tk()
fenetre.title("Vous avez gagné !")
Fond=Canvas(fenetre,bg="white", width=200, height = 200)
Fond.grid()
T = Fond.create_text(100,100,text="Gagné")
Fond.itemconfig(T,text = 'Perdu')
fenetre.mainloop()

Déplacement d'une image

En s'inspirant de l'exercice Sur le télécran, on peut créer des boutons qui permettent de commander le déplacement d'une coccinelle dans les quatre directions.

Selon la direction du déplacement, l’image de la coccinelle sera modifiée :

Exemple 15


fenetre=Tk()
fenetre.title("FaireBougerImage")
F_cocH = PhotoImage(file="cocH.gif")
F_cocB = PhotoImage(file="cocB.gif")
F_cocG = PhotoImage(file="cocG.gif")
F_cocD = PhotoImage(file="cocD.gif")

def haut():
    global y, img
    Fond.create_image(x, y-10, image = F_cocH)
    y = max (0,y-10)

def bas():
    global y
    Fond.create_image(x, y+10, image = F_cocB)
    y = min (400,y+10)

def gauche():
    global x
    Fond.create_image(x-10, y, image = F_cocG)
    x = max (0,x-10)

def droite():
    global x
    Fond.create_image(x+10, y, image = F_cocD)
    x = min (400,x+10)


Fond=Canvas(fenetre,width=400,height=400,bg="white")
Fond.grid(column=0,row=0, rowspan = 8)

Button(fenetre,text="HAUT",command=haut).grid(column=1,row=0)
Button(fenetre,text="BAS",command=bas).grid(column=1,row=1)
Button(fenetre,text="GAUCHE",command=gauche).grid(column=1,row=2)
Button(fenetre,text="DROITE",command=droite).grid(column=1,row=3)

x, y = 200, 200
img = Fond.create_image(200,200,image=F_cocH,anchor='nw')
fenetre.mainloop()

Remarque

Vous avez remarqué que ce programme a un défaut puisque le déplacement des images laisse des traces.

Pour corriger ce défaut on pourrait utiliser les méthodes

C.itemconfig(item, options) et C.coords(item,x, y)

Profondeurs des objets

Vous l’aurez sûrement remarqué, les objets images se superposent dans l’ordre dans lequel ils sont créés, c’est-à-dire que si vous créez d’abord un personnage, puis le fond, vous ne verrez pas votre héros puisqu’il sera derrière le décor.

On peut en cours de programme modifier cet ordre grâce à deux méthodes détaillées ici :

MéthodesEffet
C.tag_raise(obj)Place l’objet obj en premier plan du Canvas C.
C.tag_lower(obj)Place l’objet obj en arrière plan du Canvas C.