GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Networking en Python

0. Introduction


TkInter est une bibliothèque Python utilisée pour la mise en place d'interfaces graphiques sous Python. C'est une bibliothèque qui existe nativement dans le langage et qui ne nécessite pas d'autre chose à faire que de l'importer pour l'utiliser. Elle est relativement ancienne, pas toujours très jolie mais a le mérite d'exister en proposant une syntaxe relativement simple et immédiate avec des fonctionnalités étendues.

Comme dans les lignes qui suivent il n'est pas prévu de retranscrire une documentation complète, je vous laisse consulter la notice de tkInter disponible sur les pages du New Mexico Tech Computer Center qui, bien que plus tenue à jour depuis des années n'en reste pas moins une excellente source d'informations.

Chaque widget graphique dispose d'éléments communs tels que :

Pour définir une police de caractères il faut importer le module tkFont
from tkinter import Tk, font
root = Tk() # instancier un objet de tkinter pour ouvrir la classe tkInter
print(font.names()) # affiche les polices disponibles sur le système
print(font.families()) # affiche les polices de caractères utilisables sur l'environnement de votre machine
et ensuite définir des options :

La bibliothèque tkInter fonctionne par événements, comme n'importe laquelle des interfaces modernes dignes de ce nom. Une boucle principale se chargera d'attendre les événements et réagira en fonction de ceux-ci. Le fenêtre principale est vierge avec une taille par défaut et sans titre ni icone. Tous les éléments d'interface qui seront disposés sur cette fenêtre disposent d'options (police, couleur, texte...) qui sont déclarés en tant que paramètre à la création de l'objet (text="texte à afficher", width=50, ...). Durant l'exécution du programme, les valeurs des objets peuvent être lues (méthode .cget()) ou bien redéfinies (méthode .config()).


import tkinter as tk

mainwin = tk.Tk() # instancie un objet tkinter (l'objet de base est une fenêtre invisible)

mainwin.title("Première fenêtre") # affecte un titre à la fenêtre
mainwin.geometry("800x600") # redimensionne la fenêtre pour le premier affichage
mainwin.minsize(640, 480) # définit la taille minimal de la fenêtre
mainwin.maxsize(1024, 768) # définit la taille maximale de la fenêtre
mainwin.resizable(width=False, height=True) # autorise le redimensionnement horizontal uniquement

mainwin.mainloop() # entre dans la boucle des événements, affiche la fenêtre et attend les événements

Les objets disposent de propriétés permettant notamment des récupérer des dimensions.


# Récupère les dimensions de l'écran
screen_width = mainwin.winfo_screenwidth()
screen_height = mainwin.winfo_screenheight()

# Centrage de la fenêtre
geo = "{}x{}+{}+{}".format("800", "600", screen_width, screen_height)
mainwin.geometry(geo)

1. Premiers widgets


TkInter dispose d'un grand nombre de widgets (window gadgets) qui vont permettre de réaliser des menus, des listes, des labels, des zones de saisie...tout ce qui donne un aspect moderne à une interface graphique. Les widgets sont créés globalement tous avec le même type d'appel à savoir nom_variable = nom_widget(widget_parent, paramètres...). Une fois le label créé, il n'est pas nativement affiché. Il faut forcer l'affichage en spécifiant un gestionnaire de géométrie. Sous ce terme barbare sont regroupés les façons qu'à tkinter de placer les widgets dans l'interface. Il existe un mode statique/absolu où les widgets sont positionnés au pixel près mais ne peuvent pas s'adapter lors du redimensionnement, ou bien un agencement dynamique qui prendra la forme de boîtes horizontales ou verticales, de grilles ou de package d'un widget par ligne. Nous les verrons au fur et à mesure.

Les widgets créés ne sont pas figés dans le marbre. Il existe la méthode .configure(params...) ou son alias .config(option) qui permet de modifier ou d'ajouter des attributs à un widget lorsqu'il existe déjà.

Pour lire la valeur d'un attribut d'un widget, il faut utiliser la méthode .cget("attribut"). Le nom de l'attribut est à écrire entre guillemets.

Les options génériques principales des widgets sont les suivantes :

bg ou background La couleur de fond du widget. Cette couleur est généralement gris clair
bd ou borderwidth La largeur de la bordure entourant le widget. Deux pixels généralement par défaut.
fg ou foreground La couleur de premier plan de rendu du widget. Par défaut la couleur noire est utilisée.

Les Labels

Le label est l'élément le plus simple de l'interface et dont le rôle se contente à afficher de l'information au format texte. Pour le faire afficher, il doit être mis dans les mains d'un gestionnaire de positionnement. Nous choisissons le plus simple ici car l'objectif est de faire afficher les widgets, pas d'en faire une interface utilisateur utilisable. Le gestionnaire utilisé est pack() qui permet de positionner les widgets les uns en dessous des autres automatiquement centrés.

Les champs de saisie

Pour saisir une donnée sur une seule ligne, le widget est Entry. Par défaut, sa dimension est de 20 caractères de largeur affichés, mais sans limite réelle du champ de saisie. Les options spécifiques (liste non exhaustive) :

exportselection (bool) Par défaut, la sélection faite dans un champ de saisie est stockée dans le presse papier automatiquement. Pour désactiver ce comportement, mettez exportselection=0 dans les options du champ.
show Si vous voulez afficher un caractère générique à la place de celui saisi (comme un champ de mot de passe par exemple), spécifiez l'option show='*'.
width La taille en caractères du champ de saisie. Par défaut de 20 caractères.

Les boutons

Permet la création d'un simple bouton sur l'interface. Ses options sont les suivantes :

bitmap L'image au format bitmap à afficher à la place du texte.
command La fonction devant être appelée lors de l'appui sur le bouton command=fonction. La référence à la fonction n'inclut pas les parenthèses.
height La hauteur en lignes de caractères ou en pixels selon l'objet affiché par le bouton.
image L'image à afficher à la place du texte par défaut.
text Le texte qui sera affiché dans le bouton.
width La largeur du bouton en nombre de caractères si l'affichage contient du texte ou en pixel si le bouton affiche une image.

Les images

Tkinter nous offre 3 méthodes générales pour afficher des images :

from PIL import ImageTk, Image

img = ImageTk.PhotoImage(Image.open('image.png'))

import tkinter as tk

mainwin = tk.Tk()

# Crée un label avec un texte
hello = tk.Label(mainwin, text="Hello World")
hello.pack()

# Création d'un label et affectation d'un attribut ultérieur
hello2 = tk.Label(mainwin)
hello2.configure(text="Hello encore")
hello2.pack()

# Affiche sur la console la valeur de l'attribut text du widget label hello2
print(hello2.cget("text"))

mainwin.mainloop()

Widgets avancés

Parmi les widget considérés comme avancés on retrouve les cases à cocher, les radio boutons, les listes, les boites de messages, les sliders, les spinbox... En voici quelques éléments utiles.

Le Checkbutton / Le Radiobutton

Un checkbutton est une simple case à cocher qui permet de valider ou non une option. S'il y a plusieurs cases à cocher sur l'interface, celles-ci sont toutes mutuellement indépendantes par défaut. Chaque checkbox dispose d'une variable distincte des autres.
Un radiobutton est l'opposé du checkbutton en ce sens qu'il ne peut exister que dans un groupe d'options, chacune étant mutuellement exclusive de toutes les autres. Tous les boutons radio partagent la même variable.

command Lorsque l'état du widget change, la fonction assosicée est appelée automatiquement.
image L'image à utiliser à la place du texte.
justify Spécifie comment le texte doit être aligné dans le widget : "left", "center", "right".
text Texte à afficher dans le widget.
value Chaine de caractères ou valeur entière qui servira à déterminer la valeur prise par le contrôle.
variable La variable qui suit l'état du bouton. Pour un checkbutton, par défaut 1 si checked et 0 si unchecked. Pour un radiobutton, stocke la valeur du champ value qui peut être une chaine ou un entier.
Les widgets disposent de méthodes particulières dans leur traitement.
deselect() Annule l'état de sélection du bouton.
invoke() Appelez cette méthode pour obtenir les mêmes actions que si c'était l'utilisateur qui avait cliqué sur le bouton.
select() Sélectionne le bouton.
toggle() Uniquement pour le checkbutton, toggle() alterne entre l'état coché et décoché.
import tkinter as tk

def printvar():
   print(var1.get())


app = tk.Tk()

# Variable qui va contenir l'état du checkbox
var1 = tk.IntVar()
# L'option command référence la fonction appelée à chaque click sur le bouton
# Cela met à jour la valeur de la variable var1
chk = tk.Checkbutton(app, text='Alors heureux ?', command=printvar, variable=var1)
chk.pack()

app.mainloop()

Le Listbox

Le listbox est un widget qui permet d'afficher une liste d'éléments et autorise l'utilisateur à en choisir un ou plusieurs parmi ceux présentés.

selectbackground Couleur de fond du texte sélectionné.
selectmode Nombre d'éléments pouvant être sélectionnés dans la liste : BROWSE, SINGLE, MULTIPLE, EXTENDED.
xscrollcommand / yscrollcommand Autorise l'utilisateur à scroller horizontalement / verticalement.
Les méthodes les plus usuelles sont les suivantes :
activate(index) Sélectionne la liste spécifiée par l'index.
curselection() Renvoie un tuple contenant les numéros de ligne du ou des éléments sélectionnés en partant de 0, ou un tuple vide si aucune sélection n'est faite.
delete(first, last=None) Supprime les lignes dont les indices sont entre [first, last]. Si le second argument est omis, seule la ligne avec l'indice first est supprimée.
get(first, last=None) Retourne un tuple contenant le texte de toutes les lignes qui sont entre les index [first, last]. Si le second argument est omis, la ligne avec l'indice le plus proche de first est renvoyé.
index(i) Si possible, positionne la ligne d'index i en haut du widget.
insert(index, *elements) Insère une ou plusieurs nouvelles lignes dans le listbox avant la ligne spécifiée par l'index. Utiliser END en tant que premier argument si vous voulez ajouter les nouvelles lignes à la fin du listbox.
import tkinter as tk

def retrieve():
   print(lb.curselection())


app = tk.Tk()

lbl = tk.Label(app, text="liste de courses")
lbl.pack()

lb = tk.Listbox(app)
lb.insert(1, "pain")
lb.insert(2, "viande")
lb.insert(3, "fromage")
lb.insert(4, "oeufs")
lb.pack()

btn = tk.Button(app, text="Récupérer la sélection", command=retrieve)
btn.pack()

app.mainloop()

import tkinter as tk
from tkinter import messagebox

app = tk.Tk()

chk = tkinter.Checkbutton(app, text="alors heureux ?", offvalue=0, onvalue=1)
chk.pack()

radio1 = tk.Radiobutton(app, text="Je suis un homme", value=1)
radio2 = tk.Radiobutton(app, text="Je suis une femme", value=2)
radio3 = tk.Radiobutton(app, text="Je suis les deux", value=2)
radio1.pack()
radio2.pack()
radio3.pack()

scale = tk.Scale(app, from_=10, to=100, tickinterval=25)
scale.pack()

spin = tk.Spinbox(app, from_=1, to=10)
spin.pack()

lb = tk.Listbox(app)
lb.insert(1, "Windows")
lb.insert(2, "Linux")
lb.insert(3, "Mac OS")
lb.pack()

# Boite de message pouvant prendre plusieurs modes
# showerror()
# showinfo()
# showwarning()
# askquestion()
# askokcancel()
# askyesno()
# askretrycancel()
mb = messagebox.showerror("Erreur", "Un problème est survenu")

app.mainloop()