Tkinter les bases

Tkinter les bases

  • télécharger le dossier zip: tuto_tkinter.zip
  • le placer dans vos Documents
  • Extraire tout
  • Ouvrir le fichier main.py avec un IDE Python de la suite Winpython

Import des modules

Le module tkinter apporte l’environnement graphique.

Le module PIL permet la gestion des images.

Démarrer votre fichier par:

from tkinter import *
from PIL import Image, ImageTk

Créer une fenêtre Tkinter

La fenêtre est un objet de la classe Tk. Pour l’exemple qui suit, on lui donne le nom de fen1.

On créé un objet fenêtre à partir de l’instruction: fen1 = Tk()

La dernière ligne permet d’afficher la fenêtre à l’écran. Il faudra auparavent ajouter les objets esclaves.

if __name__ == '__main__':
    fen1 = Tk()
    # ajouter des objets esclaves à la fenêtre
    # puis finir par:
    fen1.mainloop()

Objets esclaves

Ces objets sont à ajouter à fen1, une fois celle-ci créé. Donc après fen1 = Tk()

Canvas

C’est la partie de la fenêtre dans laquelle on dessine.

On commence par créer un objet canvas appelé ici can1: c’est l’instruction can1 = Canvas(fen1, ...arguments...). Les arguments vont permettre de choisir par exemple la largeur en pixel du canvas (width) et sa hauteur (height).

Puis on positionne can1 avec la méthode pack. Sans cette deuxième ligne can1.pack(side=LEFT, padx=5, pady=5), l’objet esclave est créé mais non attaché à la fenêtre (et donc n’apparait pas).

if __name__ == '__main__':
    ...
    can1 = Canvas(fen1, bg='dark grey', height=400, width=600)
    can1.pack(side=LEFT, padx=5, pady=5)

des boutons

Les boutons vont permettre d’appeler des fonctions lorsque l’on clique dessus.

Par exemple, le bouton 1, bou1 va appeler une fonction de fen1 héritée lors de sa création à partir de la classe Tk. C’est la fonction quit.

Et le bouton 1, bou2, va appeler la fonction replay qu’il vous faudra programmer. Que voudrez vous qu’il se passe lorsque l’on clique sur ce bouton…? Effacer le canvas, dessiner une grille,…?

if __name__ == '__main__':
    ...
    # un bouton
    bou1 = Button(fen1, text='Quitter', command=fen1.quit)
    bou1.pack(side=BOTTOM)
    # un autre bouton
    bou2 = Button(fen1, text='(Re)Jouer', command=replay)
    bou2.pack()

Remarques: en python, il est possible de mettre en argument d’une fonction une autre fonction. C’est ce qui est réalisé ici, avec les fonctions fen1.quit et replay. On appelle ces fonctions sans mettre de parenthèses: on écrit replay et non replay().

Etiquette: Label

On peut écrire un texte dont le contenu sera evolutif, comme un panneau d’affichage dont le contenu est variable:

if __name__ == '__main__':
    ...
    text_affichage = StringVar()
    txt1 = Label(fen1, textvariable=text_affichage, justify=LEFT, font='TkFixedFont')
    txt1.pack()
    text_affichage.set("Cliquer sur \n le bouton \n pour demarrer")

Plus tard, dans le programme, il suffira d’écrire text_affichage.set("nouveau \n message") pour afficher un nouveau message, à la même place.

textBox

Equivalent de input mais pour l’environnement tkinter. Il faudra definir le champs de saisie (objet Text), et le bouton pour validation:

if __name__ == '__main__':
    ...
    # Une textBox
    textBox = Text(fen1, height=1, width=15)
    textBox.pack()
    # Bouton pour valider la saisie
    bou3 = Button(fen1, text='Valider', command=readtext)
    bou3.pack()

Il faudra alors programmer la fonction de rappel readtext:

def readtext():
    result=textBox.get("1.0","end")
    text_affichage.set(result)

Image dans le canvas

Plusieurs étapes sont necessaires pour:

  • ouvrir une image du dossier
  • redimensionner l’image
  • créer un objet image à partir du module tkinter: image_bomb
  • dessiner l’image dans le canvas à la position 100 * 100
if __name__ == '__main__':
    ...
    # une image de dimension 50 pixels * 50 pixels 
    image = Image.open("images/bomb.png")
    image = image.resize((50, 50), Image.ANTIALIAS)
    image_bomb = ImageTk.PhotoImage(image)
    bomb = can1.create_image(100, 100, anchor="nw", image=image_bomb)

Plus loin dans le programme, il suffira d’écrire bomb = can1.create_image(200, 200, anchor="nw", image=image_bomb) pour dessiner l’image à la position 200 * 200 par exemple.

Dessiner dans le canvas

Dessiner un carré
On utilise la méthode: .create_rectangle(X0, Y0, X1, Y1)
X, Y sont les coordonnées du coin supérieur gauche du carré.
Il peut y avoir des paramètres optionnels.

Exemple: pour dessiner un carré de côté r dans le canvas:

can1.create_rectangle(X, Y, X + r, Y + r, outline='black', fill=couleur)

Dessiner un cercle:
On utilise la méthode: .create_oval(x-r, y-r, x+r, y+r)

Dessiner une ligne:
On utilise la méthode: .create_line(x0, y0, x1, y1)

Exemple: le jeu du pendu

Evenements

Gestion d’un clic de souris dans le canvas

Pour ajouter un écouteur de clic dans le canvas, il faut ajouter dans le bloc principal (après avoir créé l’objet fen1 et can1:

if __name__ == '__main__':
  ...
  can1.bind('<Button-1>', clic)
  ...

Ici, la fonction qui sera appelée lorsque l’on clique avec le bouton gauche de la souris dans le canvas est la fonctionclic qu’il faudra programmer.

Lors de l’appel de cette fonction, il ne faut pas ajouter d’agument ni de parenthèses.

On peut par exemple vouloir afficher les coordonnées de la souris lors du clic:

def clic(event):
    """ Gestion de l'événement clic gauche sur la zone graphique """
    # MODE JEU
    # (X,Y) : position du pointeur de la souris
    X = event.x
    Y = event.y
    text_affichage.set("{}, {}".format(X,Y))

Remarque: la fonction de rappel clic a un paramètre obligatoire, event lors de sa création.

Cliquer sur une image

Pour ajouter un ecouteur de clic sur une image, on procède d’une manière presque identique:

Après avoir placé l’image dans le canvas, on lui associe une fonction de rappel:

if __name__ == '__main__':
    ...
    # Pierre
    image = Image.open("images/pierre.png")
    image = image.resize((image.size[0] // 2, image.size[1] // 2), Image.ANTIALIAS)
    photo_pierre = ImageTk.PhotoImage(image)

    pierre = can1.create_image(0, 0, anchor="nw", image=photo_pierre)
    can1.tag_bind(pierre, "<ButtonRelease>", play_pierre)
    ...

Ici, la fonction de rappel sera play_pierre. Le clic sur cette image pourra peut-être afficher un message:

def play_pierre(event):
    texte = "Joueur joue: \n PIERRE"
    text_affichage.set(texte)

Gestion des touches du clavier

De nombreux jeux utilisent les touches du clavier pour commandes. L’écoute du clavier se fait avec fen1.bind("<Key>", nom_de_la_fonction_de_rappel):

if __name__ == '__main__':
    ...
    fen1.bind("<Key>", clavier)
    ...

On peut alors programmer une fonction de rappel clavier qui affiche le caractère appuyé:

def clavier(event):
    t = event.keysym
    text_affichage.set("Touche pressée : {}".format(t))

Exemples

Exemple 1: dessiner une grille

Souvent, le jeu utilise un quadrillage sur tout le canvas.

On peut programmer une fonction carre qui dessine un carré de côté r, puis appeler cette fonction lors du dessin initial, dans une autre fonction.

def carre(r, X, Y, couleur):
    """
    dessine le contour d'une case colorée
    """
    can1.create_rectangle(X, Y, X + r, Y + r, outline='black', fill=couleur)

La fonction qui dessine le fond quadrillé utilisera 2 boucles bornées imbriquées. Si chaque carré a pour côté H: L’idée est d’utiliser les dimensions du canvas pour avoir un nombre entier de carrés de côtés H:

    for j in range(int(can1['height']) // H):
        for i in range(int(can1['width']) // H):
            carre(H,...

famille de jeux: le morpion, puissance 4, echecs, demineur

Exemple 2: dessiner selon le texte saisi

On pourrait dessiner avec un ordre: la fonction de rappel readtext pourrait évaluer les caractères saisis, et, s’il s’agit de:

  • tete: dessiner la tête du personnage
  • corps: dessiner le corps
  • bras: dessiner ses bras
  • jambes: dessiner ses jambes

famille de jeux: jeu de devinette, jeu de rôle, jeu du pendu

Exemple 3: ajouter une image lors d’un clic

Avec la fonction clic, on peut créer une nouvelle image dans le canvas avec:

def clic(event):
    """ Gestion de l'événement clic gauche sur la zone graphique """
    # MODE JEU
    # (X,Y) : position du pointeur de la souris
    X = event.x
    Y = event.y
    bomb = can1.create_image(X, Y, anchor="nw", image=image_bomb)

Le mieux, serait d’aligner l’image sur la grille de fond lorsque l’on clique dans l’une des cases du quadrillage.

Si les carrés ont pour côté H: Il faudra que X et Y soient multiples de H.

Utiliser la division entière:

X = (X // H) * H

et Y = (Y // H) * H

famille de jeux: demineur, puissance 4, morpion