blog: tkinter

This commit is contained in:
Pascal Engélibert 2024-10-30 22:17:19 +01:00
parent d7f401d3f7
commit 467ce418c0

305
content/blog/tkinter.fr.md Normal file
View file

@ -0,0 +1,305 @@
+++
title = "Tutoriel Tkinter Python"
date = 2024-10-30
description = "Initiation aux interfaces graphiques"
insert_anchor_links = "left"
[taxonomies]
tags = ["programmation", "Python"]
+++
**Prérequis** :
Il faut être un peu à l'aise avec le Python, au moins les variables, fonctions, manipulation des chaînes de caractères.
Si ce n'est pas le cas, [France-IOI](https://www.france-ioi.org/algo/chapters.php) propose une introduction ludique au Python.
Tkinter est une bibliothèque d'interface graphique, qui permet de créer des fenêtres sous Linux, Windows et Mac.
C'est une des plus simples à utiliser en Python.
Seulement elle est un peu vieille et trop peu intuitive, d'où ce tutoriel qui peut servir de mini-référence.
Pour aller plus loin : [TkDocs Tutorial](https://tkdocs.com/tutorial/index.html) (en anglais)
[Ce tutoriel est disponible en PDF.](/o/tuto/tkinter.pdf)
Le code source et les commandes utilisées pour générer le PDF sont disponibles [dans ce dépôt Git](https://git.txmn.tk/tuxmain/jsb-editor).
## Code de base
Créer une fenêtre vide :
```python
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.mainloop()
```
Ajouter des boutons dans la fenêtre :
```python
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
bt1 = ttk.Button(root, text="Bonjour")
bt1.grid(column=0, row=0)
bt2 = ttk.Button(root, text="Au revoir")
bt2.grid(column=1, row=0)
root.mainloop()
```
### Positionnement
On peut changer le nombre de colonnes ou de lignes occupées par un widget en ajoutant les paramètres `columnspan` ou `rowspan` en plus de `column` et `row`.
Pour coller un widget au bord de la grille, on peut ajouter `sticky` :
```python
bt2.grid(column=1, row=0, sticky=(tk.W, tk.N))
```
`sticky` prend une liste de 0 à 4 éléments parmi :
* `tk.E` : _East_ = droite
* `tk.N` : _North_ = haut
* `tk.S` : _South_ = bas
* `tk.W` : _West_ = gauche
### Redimensionner la fenêtre
Il faut indiquer quelles lignes et colonnes doivent changer de taille quand la fenêtre change de taille.
Dans cet exemple, ce sont la ligne 1 et la colonne 0.
```python
root.rowconfigure(1, weight=1)
root.columnconfigure(0, weight=1)
```
On peut appeler plusieurs fois ces fonctions pour choisir plusieurs lignes et colonnes, et changer les `weight` pour modifier les proportions.
## Widgets
Tous les widgets doivent être positionnés avec `grid`, de la même manière que ci-dessus.
Sans ça, ils ne seront pas affichés !
### Label
Juste du texte.
```python
label = ttk.Label(root, text="Je suis un texte. Wow.")
```
On peut aussi modifier le texte plus tard :
```python
label_text = tk.StringVar()
label["textvariable"] = label_text
label_text.set("Le nouveau texte. Tout ça pour ça !")
```
### Bouton
Un bouton qu'on peut cliquer.
```python
def onclick():
print("On a cliqué")
bt = ttk.Button(root, text="Cliquez-moi", command=onclick)
```
### Image
Un bouton ou un label peut afficher une image :
```python
img = tk.PhotoImage(file='image.png')
label["image"] = img
```
### Entrée de texte
Boîte dans laquelle on peut écrire une ligne de texte.
```python
text = tk.StringVar()
entry = ttk.Entry(root, textvariable=text)
```
Plus tard, on peut récupérer le texte :
```python
print("L'utilisateur a écrit :", text.get())
```
### Entrée de texte multiligne
Zone dans laquelle on peut écrire du texte en plusieurs lignes.
```python
text = tk.Text(root)
```
Il y a plusieurs méthodes pour interagir avec le texte :
```python
print(text.get("1.0", "end")) # obtenir tout le texte
print(text.get("2.0", "8.0")) # le texte de la ligne 2 à la ligne 8
text.replace("1.0", "end", "le nouveau texte") # remplacer du texte
text.insert("1.0", "le nouveau texte") # insérer du texte
text.insert("1.0 +42 chars", "le nouveau texte")
text.insert("end -3 lines", "le nouveau texte")
```
Pour mettre en forme certaines parties du texte, on peut utiliser des tags :
```python
text.tag_configure("fond_rouge", background="#ffaaaa")
text.tag_configure("insistance", foreground="#008800", underline=True)
text.insert("1.0", "ce texte sera sur fond rouge", "fond_rouge")
text.tag_add("insistance", "1.3", "1.8")
```
[Liste de tous les styles possibles.](https://tcl.tk/man/tcl8.6/TkCmd/text.htm#M43)
### Barre de défilement
Certains widgets (`Text`) peuvent défiler avec la molette, mais pour afficher la barre de défilement il faut un widget supplémentaire.
```python
scroll = ttk.Scrollbar(root, orient=tk.VERTICAL, command=mon_widget_qui_défile.yview)
scroll.grid(column=1, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
mon_widget_qui_défile.configure(yscrollcommand=scroll.set)
```
### Cadre
Un cadre qui peut avoir une bordure et contenir d'autres widgets.
```python
frame = ttk.Frame(root, borderwidth=5, relief="ridge", width=200, height=100)
```
Les options ne sont pas obligatoires.
Le cadre contient ses propres lignes et colonnes.
Pour placer des widgets dedans, il suffit de remplacer `root` par `frame` en les créant.
### Barre de menus
La barre de menus en haut de la fenêtre.
```python
root.option_add('*tearOff', False)
menubar = tk.Menu(root)
root['menu'] = menubar
menu_file = tk.Menu(menubar)
menu_edit = tk.Menu(menubar)
menubar.add_cascade(menu=menu_file, label='Fichier')
menubar.add_cascade(menu=menu_edit, label='Édition')
menu_file.add_command(label='Ouvrir', command=on_open)
```
### Canevas
Le canevas est une zone de dessin.
```python
canvas = tk.Canvas(root, background='white')
```
On peut y dessiner des formes :
```python
canvas.create_line(x1, y1, x2, y2)
canvas.create_line(x1, y1, x2, y2, fill="red", width=3, dash=6)
canvas.create_rectangle(x1, y1, x2, y2, fill="red", outline="blue")
canvas.create_oval(x1, y1, x2, y2, fill="red", outline="blue")
```
## Événements
Quand il se passe quelque chose sur un widget, un événement est généré.
On peut écouter les événements de certains types, c'est-à-dire lancer une fonction quand l'action se produit.
Par exemple, on affiche des informations quand une touche du clavier est relâchée dans la zone de texte :
```python
text = tk.Text(root)
def onrelease(event):
print("On a frappé le clavier !")
print(event)
text.bind("<KeyRelease>", onrelease)
```
On peut aussi utiliser les infos contenues dans l'événement, par exemple `event.keycode`.
### Liste des événements
* Souris
* `ButtonPress`: clic enfoncé d'un bouton de la souris
* `Button-1`: comme `ButtonPress` mais seulement pour le clic gauche
* `Button-2`: comme `ButtonPress` mais seulement pour le clic molette
* `Button-3`: comme `ButtonPress` mais seulement pour le clic droit
* `ButtonRelease`: clic relâché d'un bouton de la souris
* `Double-1`: double-clic gauche
* `Double-3`: double-clic droit
* `Enter`: le pointeur de la souris entre
* `Leave`: le pointeur de la souris sort
* `Motion`: le pointeur de la souris bouge dedans
* `B1-Motion`: comme `Motion` mais avec le clic gauche enfoncé
* `B2-Motion`: comme `Motion` mais avec le clic molette enfoncé
* `B3-Motion`: comme `Motion` mais avec le clic droit enfoncé
* Clavier
* `KeyPress`: appui d'une touche du clavier
* `KeyRelease`: relâche d'une touche du clavier
* Widget
* `Configure`: le widget est créé
* `Expose`: le widget est affiché ou réaffiché
* `FocusIn`: le widget reçoit le focus
* `FocusOut`: le widget pert le focus
## Dialogues
On peut ouvrir des fenêtres pour dire ou demander des choses.
### Fichiers
Demander où enregistrer un fichier, quel fichier ouvrir, choisir un dossier.
```python
from tkinter import filedialog
filetypes = [
("Texte", "*.txt"),
("Image PNG", "*.png"),
("Autre", "*.*")
]
chemin = tk.filedialog.asksaveasfilename(filetypes=filetypes)
print(chemin)
```
On peut remplacer `asksaveasfilename` par `askopenfilename` ou `askdirectory`.
### Couleur
Demander de choisir une couleur.
```python
from tkinter import colorchooser
color = colorchooser.askcolor(initialcolor='black')
print(color)
```
On peut aussi indiquer la couleur initiale avec sa notation hexadécimale `"#000000"`.