Tkinter : Dessiner et gérer des déplacements, des collisions

(actualisé le ) par wlaidet

Dessiner avec Tkinter

++++

Faire chuter une balle :

Nous allons programmer une balle qui tombe :

1) On dessine une balle dans le canvas

Un rappel des dessins dans Tkinter :

Voici le code :

from tkinter import *

#On cree une fenetre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Creation  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()

#On cree une balle:
balle1 = canvas.create_oval(10,10,30,30,fill='red')

#On lance la boucle principale:
tk.mainloop()

Télécharger

2) On fait tomber la balle la balle

On définit un mouvement au départ :

  • dx sera utilisée pour le déplacement horizontal
  • dy sera utilisée pour le déplacement vertical

L’idée ici est de créer une fonction récursive (qui se rappelle automatiquement) et qui ajoute dy (égal à 5) à l’ordonnée de la balle.

Le canvas possède une fonction : move(). Il faut lui indiquer l’objet que l’on veut déplacer et de combien (abscisses, ordonnées).

canvas.move(balle1,dx,dy)

Une explication en vidéo :

Ce qui donne :

from tkinter import *
 
def deplacement():
    global dx, dy
    #On deplace la balle :
    canvas.move(balle1,dx,dy)
    #On repete cette fonction
    tk.after(20,deplacement)
 
#Deplacement de la balle au départ:
dx=0
dy=5
 
#On cree une fenetre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)
 
#Creation  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()
 
#On cree une balle:
balle1 = canvas.create_oval(10,10,30,30,fill='red')
 
deplacement()
 
#On lance la boucle principale:
tk.mainloop()

Télécharger

A toi de jouer :

  • Change le vitesse de la balle
  • Change le déplacement de la balle (en haut, à gauche, en diagonal...)

3) On fait rebondir la balle sur le sol :

Dans la fonction deplacement() on teste si l’ordonnée (basse) de la balle dépasse la hauteur du canvas (400 pixels) :

if canvas.coords(balle1)[3]>400:

Si tel est le cas, on oppose le décalage vertical dy :

dy=-1*dy

Une explication en vidéo :

Ce qui donne :

from tkinter import *

def deplacement():
    global dx, dy
    if canvas.coords(balle1)[3]>400:
        dy=-1*dy
    #On deplace la balle :
    canvas.move(balle1,dx,dy)
    #On repete cette fonction
    tk.after(20,deplacement)

#Coordonnees de la balle:
Pos_X=60
Pos_Y=10
dx=0
dy=5

#On cree une fenetre et un canvas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Creation  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()

#On cree une balle:
balle1 = canvas.create_oval(Pos_X,Pos_Y,Pos_X+20,Pos_Y+20,fill='red')

deplacement()

#On lance la boucle principale:
tk.mainloop()

Télécharger

A toi de jouer :

  • Fais rebondir la balle en haut
  • Déplace en oblique la balle
  • Fais rebondir la balle sur tous les bords
  • Seras-tu capable d’ajouter une deuxième balle qui bouge ?

++++

Utiliser le clavier avec Tkinter :

Déplacement à chaque touche :

raquette et clavier avec Tkinter

Voici une raquette qui peut se déplacer avec la touche "droite" du clavier :

from tkinter import *

#Une fonction pour le deplacement vers la droite :
def droite(event):
    canvas.move(raquette,10,0)
   
#On cree une fenêtre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)
 
#Création  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()
 
#On cree une raquette:
raquette = canvas.create_rectangle(200,380,300,390,fill='red')

#On associe la touche droite du clavier a la fonction droite():
canvas.bind_all('<Right>', droite)
 
#On lance la boucle principale:
tk.mainloop()

Télécharger

Explications en vidéo :

A toi de jouer :

  • Ajoute les touches "Haut", "Gauche" et "Bas"
  • Seras-tu capable d’empêcher la raquette de sortir de l’écran ?

Déplacer la balle en continu :

Balle à déplacer en continu avec les flèches

Voici une fonction pour déplacer la balle en continu vers la droite :

from tkinter import *
 
def deplacement():
    global dx, dy
    if canvas.coords(balle1)[3]>400:
        dy=-1*dy
    #On deplace la balle :
    canvas.move(balle1,dx,dy)
    #On repete cette fonction
    tk.after(20,deplacement)
 
#Une fonction pour modifier le deplacement vers la droite :
def droite(event):
    global dx, dy
    dx=5
    dy=0
 
#Coordonnées de la balleau départ:
Pos_X=60
Pos_Y=10
#Déplacement de la balle au départ:
dx=0
dy=0
 
#On cree une fenêtre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)
 
#Création  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()
 
#On cree une balle:
balle1 = canvas.create_oval(Pos_X,Pos_Y,Pos_X+20,Pos_Y+20,fill='red')
 
canvas.bind_all('<Right>', droite)
 
deplacement()
 
#On lance la boucle principale:
tk.mainloop()

Télécharger

A toi de jouer :

  • Utilises toutes les flèches du clavier
  • Fais rebondir la balle sur les bords
  • Seras-tu capable de faire en sorte que la balle traverse l’écran ?

++++

Gérer des images avec Tkinter

Tkinter possède une fonction : PhotoImage(file = ’chemin_vers_image’).
Elle ne gère que les .gif et les .ppm.

Remarques importantes :

  • Si vous voulez utiliser d’autres formats d’images, il vaut mieux installer et utiliser PIL.
    Pour installer PIL sous Pyzo avec Python3, tapez dans un shell : conda install pillow
  • Il est possible que vous ayez des erreurs du type : "pyimage2 doesn’t exist". Si votre chemin et votre code sont corrects, fermer le shell de Pyzo et ouvrez un nouveau shell.

Un exemple avec une image de fond :

Image de fond
Un exemple avec une image de fond

Téléchargez l’image GIF suivante :

Copiez/Collez le code suivant, les commentaires décrivent les lignes de codes :

from tkinter import *
 
root = Tk()

#chemin d'acces a l'image:
imgfile = '/media/cours/Option_Codage/programmes/lapin.gif'
# Utilisation d'un dictionnaire pour conserver une reference:
gifsdict={}

#Creation de l'image:
img = PhotoImage(file = imgfile)
gifsdict[imgfile] = img

#Creation du canvas:
canvas=Canvas(root,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Un exemple de reduction de l'image:
img_2 = img.subsample(2, 2)

#On cree le Widget image dans le canvas:
#NW=Nord West, le coin haut guche de l'image sera positionne a (10,10):
W_image=canvas.create_image(10,10,anchor=NW,image=img_2)

#On affiche les coordonnees de l'image
#Il n'y a qu'un seul point:
print(canvas.coords(W_image))

root.mainloop()

Télécharger

Faire bouger une image :

L’image précédente peut bouger comme un rectangle ou un cercle dans le canvas avec la fonction move().

Voici un exemple avec le lapin qui tombe :

from tkinter import *
 
def deplacement():
    canvas.move(W_image,0,5)
   
    #On repete cette fonction
    root.after(40,deplacement)
 
root = Tk()

#chemin d'acces a l'image:
imgfile = '/media/cours/Option_Codage/programmes/lapin.gif'
# Utilisation d'un dictionnaire pour conserver une reference:
gifsdict={}

#Creation de l'image:
img = PhotoImage(file = imgfile)
gifsdict[imgfile] = img

#Creation du canvas:
canvas=Canvas(root,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Un exemple de reduction de l'image:
img_2 = img.subsample(2, 2)

#On cree le Widget image dans le canvas:
#NW=Nord West, le coin haut guche de l'image sera positionne a (10,10):
W_image=canvas.create_image(10,10,anchor=NW,image=img_2)

deplacement()

root.mainloop()

Télécharger

++++

Gérer les collisions

La detection des collisions entre objets peut être très pénible sous Tkinter. Il y a plusieurs méthodes :

  • On étudie les coordonnées de chaque objet (il est préférable qu’un des deux objets soit fixe ou ne se déplace que sur une ligne)
  • On utilise la méthode : find_overlapping(x1,y1,x2,y2). Elle renvoie les objets en chevauchement avec le rectangle (x1,y1,x2,y2).
  • On détermine la distance entre les objets grâce aux coordonnées de leur centre et le théorème de Pythagore.

Méthode1 : Avec les coordonnées

Si un de vos deux objets ne se déplace que très peu (de gauche à droite par exemple) ou qu’il est fixe, vous pouvez simplement étudier les positions des deux objets grâce à leurs coordonnées.

Une vidéo pour comprendre :

Le code correspondant :

from tkinter import *
 
def deplacement():
    global dx, dy
    if (canvas.coords(balle1)[3]>400) or (canvas.coords(balle1)[1]<0):
        dy=-1*dy
    if (canvas.coords(balle1)[0]<0) or (canvas.coords(balle1)[2]>500):
        dx=-1*dx
   
    #Test de la collision avec la raquette par coordonnees:
    if (canvas.coords(balle1)[3]>canvas.coords(raquette)[1]) and (canvas.coords(balle1)[0]<canvas.coords(raquette)[2]) and (canvas.coords(balle1)[2]>canvas.coords(raquette)[0]):
        dy=-1*dy
   
    #On deplace la balle :
    canvas.move(1,dx,dy)
   
    #On repete cette fonction
    tk.after(20,deplacement)
   
#Une fonction pour le deplacement vers la droite :
def droite(event):
    canvas.move(raquette,10,0)
   
#Une fonction pour le deplacement vers la gauche :
def gauche(event):
    canvas.move(raquette,-10,0)
 
#Vitesse de deplacement de la balle:
dx=5
dy=5

#On cree une fenetre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Creation  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()
 
#On cree une balle:
balle1 = canvas.create_oval(20,20,40,40,fill='red')

#On cree une raquette:
raquette = canvas.create_rectangle(200,380,400,390,fill='red')
 
#On associe les fleches du clavier aux fonctions droite() et gauche():
canvas.bind_all('<Right>', droite)
canvas.bind_all('<Left>', gauche)
 
#On lance le mouvement:
deplacement()
 
#On lance la boucle principale:
tk.mainloop()

Télécharger

Méthode 2 : Test de chevauchement

On utilise la méthode : find_overlapping(x1,y1,x2,y2). Elle renvoie les index des objets du canvas en chevauchement avec le rectangle défini par (x1,y1,x2,y2).

Cette méthode est pratique lorsque deux objets ont des déplacements dans tous les sens.

Une vidéo pour expliquer :

Le code correspondant :

from tkinter import *
 
def deplacement():
    global dx, dy
    if (canvas.coords(balle1)[3]>400) or (canvas.coords(balle1)[1]<0):
        dy=-1*dy
    if (canvas.coords(balle1)[0]<0) or (canvas.coords(balle1)[2]>500):
        dx=-1*dx
   
    #Test de la collision avec la raquette :
    if len(canvas.find_overlapping(canvas.coords(raquette)[0],canvas.coords(raquette)[1],canvas.coords(raquette)[2],canvas.coords(raquette)[3]))>1:
        dy=-1*dy
   
    #On deplace la balle :
    canvas.move(1,dx,dy)
   
    #On repete cette fonction
    tk.after(20,deplacement)
   
#Une fonction pour le deplacement vers la droite :
def droite(event):
    canvas.move(raquette,10,0)
   
#Une fonction pour le deplacement vers la gauche :
def gauche(event):
    canvas.move(raquette,-10,0)
 
#Vitesse de deplacement de la balle:
dx=5
dy=5

#On cree une fenetre et un canevas:
tk = Tk()
canvas = Canvas(tk,width = 500, height = 400 , bd=0, bg="white")
canvas.pack(padx=10,pady=10)

#Creation  d'un bouton "Quitter":
Bouton_Quitter=Button(tk, text ='Quitter', command = tk.destroy)
#On ajoute l'affichage du bouton dans la fenêtre tk:
Bouton_Quitter.pack()
 
#On cree une balle:
balle1 = canvas.create_oval(20,20,40,40,fill='red')

#On cree une raquette:
raquette = canvas.create_rectangle(200,380,400,390,fill='red')
 
#On associe les fleches du clavier aux fonctions droite() et gauche():
canvas.bind_all('<Right>', droite)
canvas.bind_all('<Left>', gauche)

#On lance le mouvement:
deplacement()
 
#On lance la boucle principale:
tk.mainloop()

Télécharger

++++

Vers un premier jeu

++++

Coordonnées de la souris