Aller au contenu

Sortir de la console

Nous avons vu comment construire une interface graphique donnant les différentes combinaisons possibles des composantes RGB. Le travail suivant permet en autonomie la création puis la gestion d’interfaces graphiques (fenêtre, boutons, labels,...) qui donne une véritable Interface Homme-Machine (IHM).

Nous allons ainsi créer une interface qui permet de construire des cercles et des carrés de différentes couleurs dans une zone de dessin. Dans une seconde partie nous créerons des animations pour dynamiser tout cela.

Tout cela dans un seul objectif: créer des jeux!

Notre interface graphique

Analyse de notre interface

fenetre_a_realiser

Exemple d'interface graphique

Dans cette interface vous trouverez:

  • une zone de commandes contenant les diverses commandes (boutons, échelles, entrée,...)
  • une zone de dessin, le canvas.

Achevée, elle permettra de construire des cercles ou des carrés dans le canvas, définis dans leur forme et couleur par les valeurs des différents boutons de commandes...

Vous trouverez ci-dessous, une partie du code qui permet sa construction et que vous devrez compléter tout au long de ce travail:

Code à copier
🐍 Script Python
from tkinter import *
from random import randint
#---------------------   ZONE FONCTIONS     -------------------------------------#

#--------------------------------------------------------------------------------#
#                CONSTRUCTION DES OBJETS DANS LA FENETRE                         #
#--------------------------------------------------------------------------------#
fen = Tk()
fen.title( "Les objets graphiques")
fen.geometry('1000x600+100+100')
fen.resizable(False, False)
#widthxheight+bordecran
#--------------------------------------------------------------------------------#
ZoneDessin = Frame(fen, borderwidth = 2, relief = RIDGE)
ZoneDessin.grid(row = 0, column = 0)
#--------------------------------------------------------------------------------#
Can = Canvas(ZoneDessin, bg = 'grey', width = 800, height = 580)
Can.grid()
#--------------------------------------------------------------------------------#
ZoneOptions = LabelFrame(fen, borderwidth = 2, text = 'Options', labelanchor = 'n',
                        relief = RIDGE,
                        width = 200, height = 580)
ZoneOptions.grid(row = 0, column = 1)
ZoneOptions.grid_propagate(0)
#--------------------------------------------------------------------------------#
BouDebut = Button (ZoneOptions, text = 'Construire', command = fen.destroy)
BouDebut.grid(row = 0, pady = 20,columnspan = 2)
#--------------------------------------------------------------------------------#
EchelleRayon = Scale(ZoneOptions, orient = 'horizontal', from_=0,
    to = 100, resolution = 1, tickinterval = 10, length = 190,
    label = 'Dimension du cercle ou du carre')
EchelleRayon.grid(row = 1,columnspan = 2)
#---------------------------------------------------------------------------------#
#                INSERER DESSOUS LES AUTRES CONSTRUCTEURS                         #
#---------------------------------------------------------------------------------#


#----------    NE RIEN METTRE EN DESSOUS DE CETTE LIGNE    -----------------------#
fen.mainloop()

Exo

Copier ce code et enregistrez-le dans un nouveau fichier que vous nommerez interface_graphique.py

Analyse du code

Pour construire une interface graphique, on fait appel à des constructeurs: Frame, Scale, Button en sont des exemples. Chaque constructeur est défini dans un élément parent et possède des attributs obligatoires ou optionnels qui lui sont propres.

Info

Le premier constructeur appelé est Tk() qui construit la fenêtre, appelée fen ici. Ce constructeur possède les attributs title, geometry ou encore resizableet sûrement bien d'autres.

Remarquez que le constructeur Scale pour faire des curseurs contient beaucoup plus d'attributs...

En résumé...

Et si vous voulez connaître tous les attributs du constructeur Button, il suffit de se documenter:

classbutton

Pour se documenter

Vous trouverez ici ou de quoi vous documentez sur les différents widgets que vous pouvez utiliser.

Le gestionnaire de positionnement

Il ne suffit pas de créer les éléments graphiques ; il faut aussi les placer dans leur conteneur. D’ailleurs, les différents widgets n’apparaîtront pas s’ils ne sont pas placés !

Il existe plusieurs façons de positionner des objets mais nous n'utiliserons que le gestionnaire grid, qui permet de placer les objets dans une grille virtuelle, préalablement réfléchie...

Positionnement relatif

Les objets (widgets) sont positionnés dans leur parent respectif et direct. Le canvas Can est positionné dans la ZoneDessin et le bouton Boudebut dans la ZoneOptions!

Il est donc important de bien concevoir son projet avant de le réaliser!

Enrichissons notre interface!

Nous allons compléter le code proposé ci-dessus afin d'enrichir l'interface par d'autres widgets et voir comment on peut dessiner dans le canvas (zone dédié au dessin...).

Commande pour dessiner dans le canvas...

Un canvas est une zone destinée à contenir des dessins ou autres figures complexes (et même des images...). Par exemple pour créer un cercle rouge dans le canvas Can , il faut utiliser le constructeur :

🐍 Script Python
x = 400
y = 300
r = 50 
Can.create_oval(x - r, y - r, x + r, y + r, fill = 'red')

Exo

  1. Insérer ce code dans la zone d'insertion et vous devriez voir apparaître un cercle rouge dans le canvas.
  2. Créer un autre cercle de couleur verte dont les coordonnées du centre sont 225 et 458 de rayon 200

Pour dessiner un rectangle rose de largeur l, de hauteur h centré au point de coordonnées (x,y), il suffit d'utiliser le constructeur:

🐍 Script Python
Can.create_rectangle(x - l/2, y - h/2, x + l/2, y + h/2, fill = 'pink')

Exo

Créer dans le canvas, un rectangle bleu centré en (500, 140) de largeur 100 et de hauteur 60. N'oubliez pas de créer les variables x, y et l,h!

Il existe d'autres constructeurs pour insérer du texte par exemple (create_text()) ou des polygones plus complexes (create_polygon()) mais intéressons nous à la façon d'intégrer une image!

Exo

  1. Récupérer l'image pacman.png(ici).
  2. Supprimer les cercles et les rectangles puis insérer le code suivant pour afficher l'image au milieu du canvas:
    🐍 Script Python
    img = PhotoImage(file = "pacman.png")
    Can.create_image(400, 290, image = img)
    

Associer la valeur des widgets à la construction d'une figure élémentaire...

Dans cette partie, on souhaite construire des cercles ou des rectangles dont les dimensions sont données par la valeur des quelques widgets présents...

Par exemple, récupérer la valeur donnée par le curseur et construire le cercle dont le rayon est cette valeur. On utilise pour cela une variable de contrôle.

Info

Une variable de contrôle est une variable gobale associée à un widget et sa valeur peut être récupérée partout dans le programme.

Le code suivant permet de définir une variable de contrôle de type Int.

Exo

  1. Ajouter le code suivant juste avant le constructeur EchelleRayon:
    🐍 Script Python
    valeur_curseur = IntVar(ZoneOptions)
    valeur_curseur.set(5)
    
  2. Ajouter au constructeur EchelleRayon l'attribut variable = valeur_curseur.

Nous allons maintenant associer au bouton la commande qui permet de dessiner un cercle dans le canvas. Pour cela, nous allons définir la fonction cercle qui sera appelée à l'appui sur le bouton.

Exo

  1. Ajouter dans la zone dédiée aux fonctions, le code suivant:
    🐍 Script Python
    def cercle():
        x, y = randint(0,800),randint(0,580)
        r = valeur_curseur.get()#permet de récupérer la valeur de la variable valeur_curseur et la stocker dans la variable r
        Can.create_oval(x - r, y - r, x + r, y + r, fill = 'red')
    
  2. Changer l'attibut command du bouton, en lui attibuant la valeur cercle.
  3. Modifier la valeur du curseur et tester la construction pour constater que le rayon du cercle change.

Nous allons maintenant créer de nouveaux widgets pour :

  • choisir la couleur(menu déroulant dont le constructuer se nomme OptionMenu )
  • choisir la forme(menu déroulant dont le constructeur se nomme OptionMenu )
  • saisir la valeur du côté du carré (zone de saisir dont le constructeur se nomme Entry )
  • donner des indications (texte dont le constructeur se nomme Label )

Exo

Réaliser la construction des widgets ci-dessus ainsi que les variables de contrôles nécessaires. Je rappelle le lien vers l'aide (voir ici)

Et la souris mainenant!

Nouvel objectif: dessiner dans le canvas une forme (carré ou cercle) aux coordonnées données par le clic de souris dans le canvas.

Info

L'appel de la fonction cercle va maintenant être réalisé par un événement extérieur (du type clavier ou souris). Il faut donc l'indiquer à la fonction en lui ajoutant le paramètre event ou plus simplement evt

La méthode fen.mainloop() en fin de programme permet d'écouter les événements extérieurs...

Exo

  1. Changer la fonction cercle comme suit:
    🐍 Script Python
    def cercle(evt):
            x, y = evt.x, evt.y
            r = valeur_curseur.get()
            Can.create_oval(x - r, y - r, x + r, y + r, fill = 'red')
    
  2. Ajouter enfin le code suivant juste avant l'instruction fen.mainloop() :
    🐍 Script Python
    Can.bind("<Button-1>", cercle)
    

Vous comprendrez que:

  • les coordonnées du clic de souris sont récupérées par les instructions etv.x et evt.y .
  • la dernière instruction établit un lien entre le clic sur le bouton gauche de la souris (Button-1 ) et la fonction cercle sur le canvas.

Exo

Modifier les codes afin de construire un objet au clic de souris dans le canvas dont les formes, les couleurs et les dimensions sont données par les différents widgets.

Pilotage au clavier

Dans le paragraphe précédent, nous avons vu comment intéragir avec le clic de souris. Je vous propose ici de piloter un objet au clavier à l'aide des flèches directionnelles.

Exo

Récupérer le code ci-dessous et enregistrez le fichier sous le nom pilotage_cercle.py

Code à copier
🐍 Script Python
from tkinter import *
#######################      VARIABLES      ##########################################
x, y = 400, 400 #défini les coordonnées initiales de l'objet
r = 25# défini le rayon initial
couleur = 'red'
#########################        FONCTIONS     #######################################
def bouge_balle(evt):
    global x, y, r
    touche = evt.keysym
    if touche == "Left":
        x = x - 2
        can.coords(baballe, x - r, y - r, x + r, y + r)
    elif touche == "Right":
        x = x + 2
        can.coords(baballe, x - r, y - r, x + r, y + r)
    elif touche == "Up":
        pass
    elif touche == "Down":
        pass
######################          CONSTRUCTEURS      ###################################
fen = Tk()
fen.title( "Pilotage au clavier dans un canvas")
fen.geometry('800x800+100+100')
fen.resizable(False, False)
######################################################################################
can = Canvas(fen, bg = 'white', width = 800, height = 800)
can.grid()
######################################################################################
baballe = can.create_oval(x - r, y - r, x + r, y + r, fill = "red")
######################################################################################
fen.bind("<Key>", bouge_balle)
fen.mainloop()

Vous devriez être en mesure de piloter la balle rouge vers la gauche ou la droite. Quelques éléments qui méritent notre attention:

  • l'instruction fen.bind("<Key>", bouge_balle) établit une lien entre l'action au clavier et la fonction bouge_balle;
  • l'instruction touche = evt.keysym permet d'affecter à la variable touche la valeur de la touche enfoncée au clavier;
  • l'instruction can.coords(baballe,...) change les coordonnées de l'objet baballe(qui existe!) dans le canvas can.

Exo

Changer les instructions pass de la fonction bouge_balle pour piloter vers le haut et vers le bas la balle rouge.

Pour terminer ce travail, on se propose de faire évoluer la couleur la balle en appuyant sur les touches b pour la couleur bleu, r pour le rouge et n pour le noir.

Exo

Ajouter deux cas à la fonction bouge_balle pour modifier la couleur de la balle

Il faudra sans doute reconfigurer la balle...