Cette partie vous montrera comment parvenir à attacher certains événements (dont l'appui sur les touches) à l'appel d'une fonction.
Nous allons par exemple diriger pacman à l'aide des fléches ou des touches plutôt qu'à l'aide de widgets-Button.
L'appui sur un Button est d'ailleurs un évènement événement intégré de base à la classe Button. Pour cela, il faut utiliser le paramètre command de cette classe. Voyons maintenant comment en créer de nouveau.
Commençons par redonner le code de notre animation pacman (avec toujours le même print de test que j'avais oublié de supprimer une fois les tests validés...).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
import random
vit_x = 10
vit_y = 0
vit_x2 = -10
vit_y2 = 0
animation_active = False
stop_animation = False
# ----------------------------------------------------------------
# Fonctions
# ----------------------------------------------------------------
def modification():
global vit_x
global vit_y
global animation_active
global stop_animation
animation_active = True
mod_angle = 0
liste_coord = .coords(pacman_1)
liste_items = .find_overlapping(liste_coord[0],liste_coord[1],liste_coord[2],liste_coord[3])
test_collision = False
if len(liste_items) > 1 :
for x in liste_items :
if x != pacman_1 :
test_collision = True
break
if test_collision == True :
itemconfig(pacman_1, fill="red")
.else:
itemconfig(pacman_1, fill="yellow")
.if liste_coord[2]>500 :
vit_x = -10
elif liste_coord[0]<0 :
vit_x = 10
elif liste_coord[1]<0 :
vit_y = 10
elif liste_coord[3]>600 :
vit_y = -10
if (vit_x <0 or vit_y>0) :
mod_angle = 180
if vit_y == 0 :
if (monCanvas.itemcget(pacman_1, 'start') == '15.0' or monCanvas.itemcget(pacman_1, 'start') == '195.0') :
itemconfig(pacman_1, start=30+mod_angle, extent=300)
.else:
itemconfig(pacman_1, start=15+mod_angle, extent=330)
.if vit_x ==0 :
if (monCanvas.itemcget(pacman_1, 'start') == '105.0' or monCanvas.itemcget(pacman_1, 'start') == '285.0') :
itemconfig(pacman_1, start=120+mod_angle, extent=300)
.else:
itemconfig(pacman_1, start=105+mod_angle, extent=330)
.move(pacman_1,vit_x,vit_y)
.modification2()
if stop_animation == False :
fen_princ.after(100, modification)
else:
stop_animation = False
animation_active = False
def init_couleurs():
config(bg="black")
.config(bg="black")
.config(bg="black")
.config(bg="black")
.config(bg="red")
.config(bg="red")
.def avance_x():
print(cget('bg'))
.init_couleurs()
config(bg="blue")
.global vit_x
global vit_y
global stop_animation
stop_animation = False
vit_x = 10
vit_y = 0
if animation_active == False :
modification()
def recule_x():
init_couleurs()
config(bg="blue")
.global vit_x
global vit_y
global stop_animation
stop_animation = False
vit_x = -10
vit_y = 0
if animation_active == False :
modification()
def monte_y():
init_couleurs()
config(bg="blue")
.global vit_x
global vit_y
global stop_animation
stop_animation = False
vit_x = 0
vit_y = -10
if animation_active == False :
modification()
def descend_y():
init_couleurs()
config(bg="blue")
.global vit_x
global vit_y
global stop_animation
stop_animation = False
vit_x = 0
vit_y = 10
if animation_active == False :
modification()
def point_depart():
init_couleurs()
config(bg="blue")
.global vit_x
global vit_y
vit_x = 0
vit_y = 0
arret()
coords(pacman_1,50,50,150,150)
.itemconfig(pacman_1, start=15,extent=330)
.coords(pacman_2,350,350,450,450)
.itemconfig(pacman_2, start=15,extent=330)
.def arret():
init_couleurs()
config(bg="blue")
.global stop_animation
stop_animation = True
def modification2():
global vit_x2
global vit_y2
if animation_active == True :
direction = random.randint(1,100)
if direction > 80 :
if direction > 95 :
vit_x2 = 10
vit_y2 = 0
elif direction > 90 :
vit_x2 = -10
vit_y2 = 0
elif direction > 85 :
vit_x2 = 0
vit_y2 = 10
else:
vit_x2 = 0
vit_y2 = -10
mod_angle = 0
liste_coord = .coords(pacman_2)
if liste_coord[2]>500 :
vit_x2 = -10
elif liste_coord[0]<0 :
vit_x2 =10
elif liste_coord[1]<0 :
vit_y2 = 10
elif liste_coord[3]>600 :
vit_y2 = -10
if (vit_x2 <0 or vit_y2>0) :
mod_angle = 180
if vit_y2 == 0 :
if (monCanvas.itemcget(pacman_2, 'start') == '15.0' or monCanvas.itemcget(pacman_2, 'start') == '195.0') :
itemconfig(pacman_2, start=30+mod_angle, extent=300)
.else:
itemconfig(pacman_2, start=15+mod_angle, extent=330)
.if vit_x2 ==0 :
if (monCanvas.itemcget(pacman_2, 'start') == '105.0' or monCanvas.itemcget(pacman_2, 'start') == '285.0') :
itemconfig(pacman_2, start=120+mod_angle, extent=300)
.else:
itemconfig(pacman_2, start=105+mod_angle, extent=330)
.move(pacman_2,vit_x2,vit_y2)
.# ----------------------------------------------------------------
# Corps du programme
# ----------------------------------------------------------------
fen_princ = Tk()
fen_princ.title("ESSAI AVEC CANVAS")
fen_princ.geometry("600x600")
Canvas(fen_princ, width=500, height=600, bg='ivory', bd=0, highlightthickness=0)
=grid(row=0,column=0, padx=10,pady=10)
.pacman_1 = .create_arc(50,50,150,150,fill="yellow",start=15,extent=330)
pacman_2 = .create_arc(350,350,450,450,fill="orange",start=15,extent=330)
Frame(fen_princ, bg='#777777')
=grid(row=0,column=1,ipadx=5)
.Button( , text="Droite", fg="yellow", bg="black", command=avance_x)
=Button( , text="Gauche", fg="yellow", bg="black", command=recule_x)
=Button( , text="Monte", fg="yellow", bg="black", command=monte_y)
=Button( , text="Descend", fg="yellow", bg="black", command=descend_y)
=Button( , text="STOP", fg="yellow", bg="red", command=arret)
=Button( , text="INIT", fg="yellow", bg="red", command=point_depart)
=pack(fill=X)
.pack(fill=X)
.pack(fill=X)
.pack(fill=X)
.pack(fill=X)
.pack(fill=X)
.fen_princ.mainloop()
Tout d'abord : qu'est-ce qu'un événement pour l'interface ? Il s'agit d'une modification de l'environnement extérieur : une touche de clavier sur laquelle on appuie, le déplacement de la souris, l'appui sur une touche de la souris ...
La traduction d'événement est event.
Lorsqu'on constate un événement / event, encore faut-il le gérer.
C'est le rôle du gestionnaire d'événement, qu'on traduit par event handler. C'est ce gestionnaire qui va donner l'ordre à la fonction désignée de s'activer si tel ou tel événement survient.
Pour créer cette liaison, il faut utiliser la méthode bind. Allons voir dans la documentation :
bind(sequence=None, func=None, add=None) [#]
Adds an event binding to this widget.Usually, the new binding replaces any existing binding for the same event sequence. By passing in “+” as the third argument, the new callback is added to the existing binding.
Maintenant que nous avons posé le vocabulaire, comment parvenir à faire avancer le pacman à gauche lorsq'on appuie sur la flèche "gauche" ?
Recherchons les infos dans le code et dans la documentation :
fen_princ.bind('<Left>', recule_x)
On notera l'absence des parenthèses pour la fonction : le but n'est pas de la déclencher mais uniquement d'y faire référence.
01° Placer cette ligne dans le corps du programme, sous la déclaration du Canvas. Par exemple, juste en dessous de la création de pacman_1. On pourrait le mettre plus loin mais comme c'est pour bouger le pacman, autant regrouper. Lancez et ...
Rien. Pas terrible comme ligne de code. Si on se renseigne un peu plus, on peut voir que la touche se nomme bien Left mais que l'événement "Appui sur Left" se code par Key-Left.
02° Ni une, ni deux : utilisons ceci :
fen_princ.bind('<Key-Left>', recule_x)
Alors ?
Normalement, toujours rien... Enfin, si. Nous avons progressé : nous déclenchons une erreur !
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\ho\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1550, in __call__
return self.func(*args)
TypeError: recule_x() takes 0 positional arguments but 1 was given
Que nous dit la console ? Que recule_x n'attend aucune argument et que pourtant il en a reçu un ... Mais pourtant, nous n'avons rien envoyé !
Si ?
Si.
Pourquoi ? C'est simple : nous déclenchons un événement et cet événement est porteur d'informations : par exemple, les coordonnées de la souris à ce moment.
Bref, la fonction qu'on donne en argument de bind ne doit pas être n'importe quelle fonction : ce doit être une fonction pouvant recevoir les données de l'événement. Et devinez comment s'appelle ce type de fonction : un gestionnaire d'événement.
Le paramètre de la fonction se nommera souvent event ou evt, bref événement. Vous pourriez noter dudule, ça fonctionnerait aussi.
03° Créer la fonction gest_recule(event) qui sera le gestionnaire chargé de gérer l'appui sur la flèche gauche. Changer ensuite la méthode bind pour la relier à ce gestionnaire.
def gest_recule(event) :
recule_x()
Donc le programme principal, on aura :
fen_princ.bind('<Key-Left>', gest_recule)
Normalement, cette fois, ça fonctionne.
04° A vous de gérer les autres mouvements. Quelques indices :
Vous devriez constater en plus que le button de la dernière action activée est devenu bleu puisqu'on gère cela dans les fonctions avance_x ...
Voilà pour l'introduction : souvenez vous que les événements et les gestionnaires d'événements ne sont pas juste des mots de vocabulaire pour faire sympa. Derrière, se cachent réellement des notions à gérer dans le code :
Bon, maintenant que vous avez bien compris la différence entre la touche (Left) et l'événement sur la touche (Key-...) , je vais pouvoir vous montrer que j'ai menti un peu plus haut (ce n'est pas la première que cela arrive, mais cela permet de simplifier ou de mettre une notion en avant).
En réalité, si fen_princ.bind('<Left>', recule_x)
ne fonctionnait pas, c'était bien à cause du fait que la fonction, pas à cause de Left.
05° Supprimer les key lors de la déclaration de vos méthodes bind. Relancer.
fen_princ.bind('<Left>', gest_recule)
fen_princ.bind('<Right>', gest_avance)
fen_princ.bind('<Up>', gest_monte)
fen_princ.bind('<Down>', gest_descend)
Vous constatez que cela fonctionne. En réalité, par défaut Left est compris comme Key-Left. Nous allons détailler les autres options qui ne sont pas par défaut dans les parties suivantes.
Le premier problème lorsqu'on cherche à créer une interaction, c'est d'abord de trouver le bon code de détection de l'événement. Vous trouverez ici une liste de certains événements "touches clavier", gérés par Tkinter.
Commençons par rajouter une gestionnaire d'événement qui va nous servir à comprendre ce qui se passe lorsqu'on a activé une touche :
06° Rajouter la déclaration d'une méthode bind et du gestionnaire dans la partie fonctions. Vérifier que cela fonctionne avec deux trois touches.
fen_princ.bind('<Any-KeyPress>', gest_affichage)
Cette déclaration d'événement veut dire : Si on appuie (KeyPress) sur n'importe quelle touche (Any). Nous verrons ensuite les différentes possibilités disponibles justemnt.
def gest_affichage(event) :
print("-- DEBUT INFO --")
print('type : \t', event.type)
print('keysym : \t', event.keysym)
print('keycode : \t', event.keycode)
print('keysym_num : \t', event.keysym_num)
print("Coordonnées pointeur souris")
print(event.x," ",event.y)
print("FIN INFO")
07° Appuyer sur la touche "a", puis "Verrouillage majuscule", puis "A".
Touche a :
-- DEBUT INFO --
type : 2
keysym : a
keycode : 65
keysym_num : 97
Coordonnées pointeur souris
411 -33
FIN INFO
Verrouillage MAJ :
-- DEBUT INFO --
type : 2
keysym : Caps_Lock
keycode : 20
keysym_num : 65509
Coordonnées pointeur souris
411 -33
FIN INFO
Touche A :
-- DEBUT INFO --
type : 2
keysym : A
keycode : 65
keysym_num : 65
Coordonnées pointeur souris
411 -33
FIN INFO
Commençons par remarquer que keysym contient la chaîne de caractère contenant la description de l'événement : a, Caps_Lock et A.
Nous voyons ensuite que A et a ont le même keycode : 65
Par contre, le keysym_num diffère : 65 pour A mais 97 pour a.
Cela ne vous rappelle rien ? Un gros indice pour ceux qui bloquent :
08° Ouvrir la fiche dans une autre fenêtre et chercher les codes ASCII de A et a.
On voit donc que :
Si on résume :
Pour les touches "lettres" :
Nom de la touche | keysym (caractère obtenu) | keycode | keysym_num |
---|---|---|---|
A + Caps Lock activé | A | 65 | 65 |
B + Caps Lock activé | B | 66 | 66 |
. . . | . . . | . . . | . . . |
Y + Caps Lock activé | Y | 89 | 89 |
Z + Caps Lock activé | Z | 90 | 90 |
Nom de la touche | keysym(caractère obtenu) | keycode | keysym_num |
A | a | 65 | 97 |
B | b | 66 | 98 |
. . . | . . . | . . . | . . . |
Y | y | 89 | 121 |
Z | z | 90 | 122 |
09° Vérifier que cela fonctionne avec les touches 0 à 9 (hors pavé numérique) :
Nom de la touche | keysym (caractère obtenu) | keycode | keysym_num |
---|---|---|---|
0 + Caps Lock activé | 0 | 48 | 48 |
1 + Caps Lock activé | 1 | 49 | 49 |
2 + Caps Lock activé | 2 | 50 | 50 |
3 + Caps Lock activé | 3 | 51 | 51 |
4 + Caps Lock activé | 4 | 52 | 52 |
5 + Caps Lock activé | 5 | 53 | 53 |
6 + Caps Lock activé | 6 | 54 | 54 |
7 + Caps Lock activé | 7 | 55 | 55 |
8 + Caps Lock activé | 8 | 56 | 56 |
9 + Caps Lock activé | 9 | 57 | 57 |
Nom de la touche | keysym (caractère obtenu) | keycode | keysym_num |
0 | agrave à | 48 | 224 |
1 | ampersand & | 49 | 38 |
2 | eacute é | 50 | 233 |
3 | quotedbl " | 51 | 34 |
4 | quoteright ' | 52 | 39 |
5 | parenleft ( | 53 | 53 |
6 | minus - | 54 | 45 |
7 | egrave è | 55 | 232 |
8 | underscore _ | 56 | 95 |
9 | ccedilla ç | 57 | 231 |
Attention : on notera que keysym est bien une chaîne de caractères contenant par exemple "agrave". C'est important d'y faire attention pendant les tests.
Si vous voulez utiliser d'autres touches, il vaut mieux vérifier les codes obtenus via tkinter avec la fonction qu'on a donné ici : le codage des autres touches est parfois étrange.
Ainsi si vous voulez lancer l'appel de gest_avance avec la touche &, il faut noter fen_princ.bind('<Key-ampersand>', gest_avance)
ou fen_princ.bind('<ampersand>', gest_avance)
.
Dans la plupart des applications graphiques, on gère surtout ces touches-ci. CTRL, TAB ...
En voici la liste dans le sens de lecture suivant sur un clavier Windows : haut à gauche vers bas à gauche vers bas à droite puis haut à droite.
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Tabulation | Tab | 9 | 65289 |
Verrouillage majuscule | Caps_Lock | 20 | 65509 |
Touche shift de gauche(maj) | Shift_L | 16 | 65505 |
Touche contrôle de gauche | Control_L | 17 | 65507 |
Touche Windows de gauche | Win_L | 91 | 65371 |
Touche Alt de gauche | Alt_L | 18 | 65513 |
Space | space | 32 | 32 |
Touche Alt Gr | Alt_R | 18 | 65514 |
Touche Windows de droite | Win_R | 92 | 65372 |
Touche Applications | App | 93 | 65373 |
Touche contrôle de droite | Control_R | 17 | 65508 |
Touche shift de droite(maj) | Shift_R | 16 | 65506 |
Touche Entrée | Return | 13 | 65293 |
Retour | BackSpace | 8 | 65288 |
Faites attention à space qui possède bien une minuscule, là où les autres touches ont des majuscules. Il doit y avoir une raison. Si je le découvre un jour, je tenterais d'éditer cette phrase !
10° Que remarquez-vous sur les keycodes des deux types ALT ou Controle ou Shift ? Ont-elles le même keysym_num ?
Et voici la fin avec la ligne de haut qui contient habituellement ESCAPE, les F, Impr.Ecran ... puis les blocs de droite qui comprend les flèches, les touches Insérer, Supprimer ...
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Flèche Gauche | Left | 37 | 65361 |
Flèche Haut | Up | 38 | 65362 |
Flèche Droite | Right | 39 | 65363 |
Flèche Bas | Down | 40 | 65364 |
Page précédente | Prior | 33 | 65365 |
Page suivante | Next | 34 | 65366 |
Insérer | Insert | 45 | 65379 |
Supprimer | Delete | 46 | 65535 |
Début de ligne | Home | 36 | 65360 |
Fin de ligne | End | 35 | 65367 |
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Echap. | Escape | 27 | 65307 |
F1 | F1 | 112 | 65470 |
F2 | F2 | 113 | 65471 |
F3 | F3 | 114 | 65472 |
F4 | F4 | 115 | 65473 |
F5 | F5 | 116 | 65474 |
F6 | F6 | 117 | 65475 |
F7 | F7 | 118 | 65476 |
F8 | F8 | 119 | 65477 |
F9 | F9 | 120 | 65478 |
F10 | F10 | 121 | 65479 |
F11 | F11 | 122 | 65480 |
F12 | F12 | 123 | 65481 |
Arrêt défilement | Scroll_Lock | 145 | 65300 |
Pause | Pause | 19 | 65299 |
Dernière partie d'énumération des touches avec le clavier numérique.
Il possède deux topologies différentes en fonction de l'activation ou non du Verrouillage Numérique.
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Verrouillage Numérique | Num_Lock | 144 | 65407 |
Voici la version avec verrouillage numérique activé :
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Division | slash | 111 | 47 |
Multiplication | asterisk | 106 | 42 |
Soustraction | minus | 109 | 45 |
Addition | plus | 107 | 43 |
Entrée | Return | 13 | 65293 |
Virgule/Point | period | 110 | 46 |
0 | 0 | 96 | 48 |
1 | 1 | 97 | 49 |
2 | 2 | 98 | 50 |
3 | 3 | 99 | 51 |
4 | 4 | 100 | 52 |
5 | 5 | 101 | 53 |
6 | 6 | 102 | 54 |
7 | 7 | 103 | 55 |
8 | 8 | 104 | 56 |
9 | 9 | 105 | 57 |
Attention : les chiffres, le +, le - ... ont ici le même keysym et keysym_num mais un code de touche keycode différent puisqu'il s'agit physiquement d'une autre touche ayant la même fonction. Si vous avez vraiment besoin de distinguer le 5 du clavier alpha ou numérique, vous savez maintenant comment faire.
Voici la version avec verrouillage numérique désactivé : aucun changement avec /, *, -, +, Entrée. Par contre, il y a du changement pour les chiffres et le point.
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Division | slash | 111 | 47 |
Multiplication | asterisk | 106 | 42 |
Soustraction | minus | 109 | 45 |
Addition | plus | 107 | 43 |
Entrée | Return | 13 | 65293 |
Nom de la touche | keysym (nom interne) | keycode | keysym_num |
---|---|---|---|
Point/Virgule | Delete | 46 | 65535 |
0 | Insert | 45 | 65379 |
1 | End | 35 | 65367 |
2 | Down | 40 | 65364 |
3 | Next | 34 | 65366 |
4 | Left | 37 | 65361 |
5 | Clear | 12 | 65291 |
6 | Right | 39 | 65363 |
7 | Home | 36 | 65360 |
8 | Up | 38 | 65362 |
9 | Prior | 33 | 65365 |
On voit donc que ces touches se comportent alors comme les touches situées à droite du clavier alpha.
Depuis le début, nous n'utilisons ici qu'un type unique d'événement : l'événement dont le type est 2. Il s'agit de l'événement "Appui sur une touche".
Il en existe bien d'autres.
Voici une liste des plus utiles dans les applications habituelles.
Type (event.type) | Nom à utiliser dans bind | Description |
---|---|---|
2 | KeyPress | L'utilisateur vient d'appuyer sur la touche. On peut également utiliser uniquement Key . Exemple : '<KeyPress-Left>' ou '<Key-Left>' ou encore '<Left>' | .
3 | KeyRelease | L'utilisateur vient de relacher une touche. Exemple : '<KeyRelease-Left>' . |
4 | Button | L'utilisateur vient d'appuyer sur l'un des boutons de la souris. Exemple : '<Button-1>' ou '<Button-2>' ou '<Button-3>' . Si event est le nom donné au paramètre "événement" du gestionnaire, on peut obtenir les coordonnées à l'aide de event.x ou event.y . Si on utilise bind('<Button-1>', gest_avance) , on activera .gest_avance si on appuie sur le Bouton 1 de la souris alors qu'on est au dessus du Canvas. Si vous voulez que cela marche sur toute la fenêtre, il faut utiliser fen_princ.bind('<Button-1>', gest_avance) . |
5 | ButtonRelease | L'utilisateur vient de relacher l'un des boutons de la souris. Exemple : '<ButtonRelease-1>' . A priviligier par rapport au type 4 : l'utilisateur peut sortir du widget pour désactiver son action en cas d'erreur, alors que l'exécution est automatique en cas de type 4 (Button). |
6 | Motion | L'utilisateur a bougé la souris : '<Motion>' . Si on utilise bind('<Motion>', gest_avance) , on lance l'appel de .gest_avance lorsque la souris bouge au dessus du widget . |
7 | Enter | L'utilisateur vient de faire rentrer la souris dans le Widget : '<Enter>' . Même remarque qu'au dessus, la seule différence étant qu'on ne relance pas l'appel si on bouge ensuite dans le widget. L'activation ne se fait que lors de la rentrée. bind('<Enter>', gest_avance) va lancer .gest_avance lorsqu'on fait rentrer le pointeur de la souris dans . |
8 | Leave | L'utilisateur vient de faire sortir la souris d'un Widget : '<Leave>' . Même remarque qu'au dessus, la seule différence étant qu'on ne relance pas l'appel si on bouge ensuite dans le widget. L'activation ne se fait que lors de la sortie. bind('<Leave>', gest_recule) va lancer .gest_recule lorsqu'on fait sortir le pointeur de la souris de . |
Il en existe beaucoup d'autres. Allez voir la documentation externe si vous désirez faire quelque chose qui n'est pas réalisable avec ceux-ci.
On notera que si vous voulez utiliser la saisie qu'un utilisateur a réalisé dans un Widget Entry, il faut utiliser par exemple bind('<Return>', gestionnaire)
. .
La source documentaire se trouve ici : Vers infohost : http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-types.html
En français, on trouve également ceci : Vers tkinter.fdex.eu : http://tkinter.fdex.eu/doc/event.html#types-d-evenements
Répondez aux questions ci-dessous en utilisant le tableau (ou une autre source d'informations). Piochez ce dont vous avez besoin.
11° Créer une gestion d'événement liée à la fenêtre faisant le lien entre l'appui sur la touche A et le fait d'avancer en x.
12° Créer une gestion d'événement liée à la fenêtre faisant stopper l'animation lorsqu'on lache le A.
13° Créer une gestion d'événement liée au Canvas donnant dans la console les coordonnées x,y du pointeur de la souris lorsqu'on appuie sur le bouton de votre choix de la souris.
Les coordonnées sont toujours les coordonnées propres au Widget. Ainsi, si on clique sur le Canvas, on obtient les coordonnées du point de vue du Canvas.
Pour convertir des coordonnées fenêtre en coordonnées Canvas, il faut utiliser les méthodes canvasx et canvasy. A vous de vous renseigner, sur http://effbot.org/tkinterbook/canvas.htm.
14° Créer une gestion d'événement liée au Canvas donnant dans la console les coordonnées x,y du pointeur de la souris lorsqu'on appuie sur le bouton de votre choix de la souris.
15° Modifier la fonction du gestionnaire pour activer avancer si x > 250 et reculer si x < 250.
16° Arrêter l'animation dès que le curseur sort du Canvas.
Nous allons voir qu'on peut aller plus loin avec la déclaration de l'événement.
On peut rajouter des informations devant l'événement pour le rendre plus complexe.
Ainsi :
On peut utiliser Alt ou Control ou ou Shift si on veut dire que la touche Alt (ou Control ou Shift) doit être enfoncée lors de l'événement.
Ainsi bind('<Alt-Button-1>', gest_monte)
veut dire qu'on active .gest_monte si on appuie ET qu'on maintient ALT enfoncé dans le même temps.
On peut utiliser Any pour généraliser l'événement.
Ainsi fen_princ.bind('<Any-KeyPress>', gest_monte)
veut dire qu'on active gest_monte si on appuie sur n'importe quelle touche.
On peut utiliser Lock pour imposer que le verouillage majuscule soit actif.
Ainsi fen_princ.bind('<Lock-KeyRelease-A>', gest_monte)
veut dire qu'on active gest_monte si on relache A alors que le verrouillage majuscule est actif.
17° Et si nous pouvions changer la vitesse de l'animation avec CTRL+ et CTRL- ? Rajouter les événements et le bon gestionnaire pour réaliser cela.
Par contre, il faut savoir que event.x
va toujours vous donner les coordonnées locales liées au widget sur lequel vous avez cliqué. Ainsi si vous cliquez en haut à gauche d'un carré contenu dans une fenêtre, les coordonnées x,y de l'événement seront (0,0) même si le widget n'est pas à cet endroit.
Si vous voulez les coordonnées du click dans le contexte de la fenêtre, il va falloir utiliser event.x_root
et event.y_rooot
.
Un exemple ci-dessous vous permettra d'un peu mieux voir la différence entre les deux.
On crée 1 Label contenant du texte et 3 Label contenant une image.
Lorsqu'on clique sur l'écran, on lance l'appel à la fonction activation qui va :
18° Tester le programme ci-dessous pour tenter de voir la différence entre les deux types de coordonnées. Cliquez par exemple au même endroit sur les trois carrés initiaux.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import *
from PIL import Image as Img
from PIL import ImageTk
# - - - - - - - - - - - - - -
# Définitions des fonctions utilisées
# - - - - - - - - - - - - - -
def activation(event) :
print ("Vous avez cliqué sur ", event.widget)
print ("Les coordonnées locales sont : ")
# On affiche les coordonnées de l'événement, contenues dans x et y
print(event.x)
print(event.y)
print ("Les coordonnées globales sont : ")
# On affiche les coordonnées de l'événement avec x_root et y_root
print(event.x_root)
print(event.y_root)
try:
event.widget
=nouvelle_temp = Img.new("RGB", (40,40), (255,255,0))
nouvelleTk_temp = ImageTk.PhotoImage(nouvelle_temp)
configure(image = nouvelleTk_temp)
.# la ligne suivante évite au ramasse-miette de détruire nouvelleTk_temp car
# cette variable n'est sinon plus pointée par une variable du corps du programme.
# Sans cette ligne, nouvelleTk_temp serait inexistante hors de la fonction
nouvelleTk_temp
.monEmplacement =except:
print("On clique directement sur la fenêtre")
# - - - - - - - - - - - - - -
# Création de la fenêtre et des objets associés la fenêtre
# - - - - - - - - - - - - - -
fen_princ = Tk()
# Création d'un Label nommé monAffichage
Label(fen_princ, text="Belles images variables en cliquant", width=70)
=pack()
.# Création des images de base
presentation = Img.new("RGB", (40,40), (0,0,255))
presentationTk = ImageTk.PhotoImage(presentation)
presentation2 = Img.new("RGB", (40,40), (0,0,255))
presentationTk2 = ImageTk.PhotoImage(presentation2)
presentation3 = Img.new("RGB", (40,40), (0,0,255))
presentationTk3 = ImageTk.PhotoImage(presentation3)
# Création des labels de type image associés aux presentationTk
Label(fen_princ, image=presentationTk)
=pack()
.Label(fen_princ, image=presentationTk2)
=pack()
.Label(fen_princ, image=presentationTk3)
=pack()
.# - - - - - - - - - - - - - -
# Bouclage de la fenêtre fen_princ
# - - - - - - - - - - - - - -
fen_princ.bind('<Button-1>', activation )
fen_princ.mainloop()
Vous pouvez tester le programme : en cliquant sur un élément, on le transforme en carré jaune. Même le texte. Il manque un filtre ou deux !
Allez également voir la console pour voir ce qu'elle affiche : vous allez voir la différence entre les coordonnées locales x et globales x_root.
...CORRECTION...
Si on clique en haut à gauche des carrés, on obtient toujours presque les mêmes valeurs x et y comprises entre 1 et 5 pixels : c'est normal, il donne les coordonnées par rapport au coin haut-gauche du widget sur lequel vous avez cliqué.
Pas moyen donc de déterminer le widget sur lequel on voit de cliquer avec ces coordonnées.
Avec x_root et y_root, on obtient les coordonnées par rapport au coin haut-gauche de votre écran. C'est mieux, mais si on bouge la fenêtre, ca change aussi les coordonnées de x_root et y_root du coup...
Par contre, le nom pourrait être utile pour distinguer le widget.
Voyons comment créer un filtre pour que le Labeltext ne se transforme pas en image mais simplement que la couleur du texte change. Pour cela, nous allons comparer l'adresse contenue dans event.widget
et l'adresse de l'objet contenu dans .
19° Modifier la fonction avec le code ci-dessous. Vérifier qu'on modifie maintenant le Labeltext comme indiqué.
try:
event.widget
=if widgetClique == monAffichage :
configure(fg = "red")
.else:
nouvelle_temp = Img.new("RGB", (40,40), (255,255,0))
nouvelleTk_temp = ImageTk.PhotoImage(nouvelle_temp)
configure(image = nouvelleTk_temp)
.# la ligne suivante évite au ramasse-miette de détruire nouvelleTk_temp car
# cette variable n'est sinon plus pointée par une variable du corps du programme.
# Sans cette ligne, nouvelleTk_temp serait inexistante hors de la fonction
nouvelleTk_temp
.monEmplacement =except:
print("On clique directement sur la fenêtre")
20° Tache finale : on veut pouvoir augmenter la taille des carrés de 10 pixels en largeur lorsqu'on clique un carré en appuyant sur CTRL et réduire de 10 lorsqu'on clique en appuyant ALT.
Il y a encore beaucoup à dire mais on rentre dans ce cas de plus en plus dans des détails. La documentation est votre amie. Vous avez maintenant assez de recul pour la comprendre un peu mieux. Avec tous les boutons, la souris et les event.x
et event.y
pour récupérer les coordonnées de la souris lors de l'événement, vous avez de quoi faire des interfaces très complexes.
Si vous voulez savoir sur quel widget vous venez de cliquer, aller voir l'activité Savoir où on clique.