Tkinter - Afficher une image sans bug : le guide complet

Noël Besnard .

2 mai 2026

Dans PyCharm, le code Python importe le module tkinter. L'éditeur montre une suggestion pour l'importation.

Afficher une image dans une interface Python semble simple, jusqu’au moment où le format n’est pas reconnu, que l’image disparaît sans raison ou qu’elle devient floue dès qu’on la redimensionne. Le sujet autour de tkinter image touche en réalité trois choses très concrètes: les formats gérés par Tkinter, la manière d’attacher l’image à un widget, et la préparation du fichier avant affichage. Je vais aller droit au but: ce qui marche nativement, quand Pillow devient nécessaire, et les pièges qui font perdre du temps même aux développeurs expérimentés.

Les points à retenir avant d’intégrer une image à votre interface

  • Tkinter gère nativement surtout les formats PGM, PPM, GIF et PNG via PhotoImage.
  • Pour JPEG, BMP, TIFF ou WebP, j’utilise presque toujours Pillow puis ImageTk.PhotoImage.
  • Le piège numéro un reste la référence Python perdue, qui vide le widget à l’écran.
  • Pour redimensionner proprement, il vaut mieux préparer l’image avec ImageOps.contain ou cover que forcer l’affichage à l’aveugle.
  • Le canevas est le bon choix quand il faut placer, superposer ou remplacer une image dynamiquement.

Ce que Tkinter gère nativement avec les images

La base est plus étroite qu’on ne le croit. Tkinter sait afficher des images, mais il ne remplace pas une vraie bibliothèque de traitement d’images. En pratique, son objet central est PhotoImage, qui couvre surtout les PNG, GIF, PGM et PPM dans les versions récentes; BitmapImage reste réservé aux bitmaps XBM, donc à des cas très spécifiques. Si vous avez besoin de JPEG, BMP, TIFF ou WebP, je passe presque toujours par Pillow.

Besoin Outil Quand je le choisis Limite principale
Afficher un PNG ou un GIF simple PhotoImage Icônes, logos, visuels légers Formats supportés assez limités
Afficher un bitmap XBM BitmapImage Cas historiques ou très ciblés Usage rare et spécialisé
Ouvrir, convertir, redimensionner, recadrer Pillow + ImageTk.PhotoImage Quand l’image doit être préparée avant affichage Dépendance externe supplémentaire
Placer l’image librement dans une zone Canvas.create_image() Vues composites, superposition, mise à jour dynamique Gestion plus manuelle de la position et de l’état

La clé, ici, c’est de ne pas demander à Tkinter ce qu’il ne fait pas bien. Si votre besoin se limite à quelques vignettes légères, le natif suffit souvent. Dès qu’il faut traiter une image, la préparer pour un cadre précis ou accepter plusieurs formats, Pillow devient le choix propre. Et une fois la source choisie, la vraie difficulté devient l’affichage stable dans l’interface.

Schéma d'une interface graphique Tkinter montrant une fenêtre avec un cadre pour les miniatures, un cadre d'édition contenant l'affichage d'une image et un champ de texte, ainsi qu'une zone pour les boutons.

Afficher une image correctement dans un widget

Quand j’intègre une image dans une étiquette, un bouton ou un canevas, le détail qui change tout n’est pas le widget lui-même mais la durée de vie de l’objet Python. Tkinter utilise l’image tant que la référence existe côté Python; si l’objet est libéré, le widget reste vide. C’est la raison pour laquelle on voit tant de captures d’écran avec une boîte blanche à la place du visuel.

from tkinter import Tk, Label
from PIL import Image, ImageTk

root = Tk()

original = Image.open("photo.jpg")
photo = ImageTk.PhotoImage(original)

label = Label(root, image=photo)
label.image = photo  # référence conservée
label.pack()

root.mainloop()

Je préfère stocker l’image dans un attribut du widget, de la fenêtre ou d’un objet métier. C’est plus lisible qu’une variable globale et beaucoup plus robuste dès qu’on a plusieurs vues. Le même principe s’applique au canevas, avec create_image() et un ancrage explicite comme anchor="nw" pour partir du coin supérieur gauche.

from tkinter import Tk, Canvas
from PIL import Image, ImageTk

root = Tk()

img = Image.open("banner.png")
photo = ImageTk.PhotoImage(img)

canvas = Canvas(root, width=800, height=500)
canvas.pack()
canvas.create_image(0, 0, anchor="nw", image=photo)
canvas.photo = photo  # référence conservée

root.mainloop()

Si vous remplacez l’image dynamiquement, conservez aussi la nouvelle référence. Une fois ce mécanisme en place, la vraie question devient la taille: une image correcte mais trop grande reste lourde, et une image trop petite devient vite floue.

Redimensionner et recadrer sans déformer

Je conseille de préparer la taille avant conversion en image Tk, pas après. Cette étape évite les déformations, limite l’usage mémoire et permet de choisir entre un rendu qui tient dans un cadre et un rendu qui le remplit complètement. Pillow offre plusieurs stratégies, et elles ne servent pas au même besoin.

Méthode Effet Quand je l’utilise Attention
resize() Taille exacte Icônes, formats imposés, cas où le ratio peut changer Peut déformer l’image
thumbnail() Réduction maximale en place Aperçus rapides, caches, lots d’images Modifie l’objet original
ImageOps.contain() Rentre dans un cadre sans crop Vignettes où je veux garder toute l’image visible Peut laisser des marges
ImageOps.cover() Remplit le cadre en gardant le ratio Bannières ou fonds visuels Coupe les bords de l’image
ImageOps.fit() Recadre au ratio cible Portraits, avatars, cartes Il faut régler le centrage
from PIL import Image, ImageTk, ImageOps

img = Image.open("hero.webp")
img = ImageOps.contain(img, (640, 360), method=Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img)

Pour une réduction nette, j’opte souvent pour LANCZOS; c’est un peu plus coûteux, mais le rendu reste plus propre sur les images photographiques. Si la vitesse compte davantage que la finesse, BICUBIC suffit souvent. Le bon filtre dépend donc du volume d’images, de leur taille d’origine et du niveau d’exigence visuelle. Quand la taille est réglée, il devient possible d’aller plus loin que l’affichage simple, notamment en transformant l’image avant de la montrer.

Modifier une image avant de l’afficher

Pour une interface un peu vivante, je garde toujours une copie de la source et je dérive les variantes à partir d’elle. C’est la meilleure façon d’éviter les effets cumulés, comme une rotation appliquée deux fois parce qu’on a réutilisé l’image déjà transformée. Dans Tkinter, c’est aussi la bonne porte d’entrée pour un bouton “tourner”, un aperçu en niveaux de gris ou un changement d’illustration selon l’état de l’application.

Partir d’une source immuable

Je conseille de ne jamais écraser l’image originale si vous prévoyez des interactions. Une base intacte permet de revenir en arrière proprement, de tester plusieurs filtres et d’éviter que la qualité se dégrade au fil des transformations successives.

from tkinter import Tk, Label, Button
from PIL import Image, ImageTk

root = Tk()

base = Image.open("camera.png")

label = Label(root)
label.pack()

def rotate_image():
    global current_photo
    rotated = base.rotate(90, expand=True)
    current_photo = ImageTk.PhotoImage(rotated)
    label.configure(image=current_photo)
    label.image = current_photo

Button(root, text="Tourner", command=rotate_image).pack()

root.mainloop()

Le paramètre expand=True évite de rogner l’image après rotation. Je l’utilise presque systématiquement quand je veux une rotation visuellement propre, surtout si le fichier n’est pas carré. Si vous avez besoin de transparence ou de composition sur un fond coloré, je passe aussi volontiers en RGBA avant d’assembler les calques.

Lire aussi : Python - isnumeric() vs isdigit() vs isdecimal() - Le guide

Mettre à jour l’interface sans la bloquer

Pour une animation ou un changement périodique d’image, il faut éviter les boucles bloquantes. Tkinter réagit bien quand on utilise after() pour programmer la mise à jour suivante. C’est une méthode simple, mais elle change tout pour un affichage fluide.

from tkinter import Tk, Label
from PIL import Image, ImageTk

root = Tk()

frames = []
for path in ["frame1.png", "frame2.png", "frame3.png"]:
    frames.append(ImageTk.PhotoImage(Image.open(path)))

label = Label(root)
label.pack()

index = 0

def animate():
    global index
    frame = frames[index]
    label.configure(image=frame)
    label.image = frame
    index = (index + 1) % len(frames)
    root.after(80, animate)

animate()
root.mainloop()

Pour une séquence courte, c’est très efficace. Pour une animation plus lourde, je précharge les frames une seule fois et je surveille la mémoire, parce que le confort visuel peut vite coûter cher si l’on redimensionne ou reconvertit en permanence. Une fois ces transformations maîtrisées, les problèmes restants viennent moins de l’image elle-même que du contexte d’exécution.

Les pièges qui provoquent un écran vide

Les bugs liés aux images ont presque tous la même signature: l’interface compile, la fenêtre s’ouvre, puis le visuel manque ou s’affiche mal. Quand je vois cela, je vérifie d’abord le cycle de vie de l’objet image, puis le format du fichier, ensuite le chemin d’accès. Dans la majorité des cas, le problème est là, pas dans Tkinter lui-même.

Symptôme Cause probable Correctif
Widget vide ou boîte blanche Référence Python perdue Stocker l’objet dans un attribut durable, par exemple widget.image
Erreur au chargement Format non supporté ou chemin incorrect Vérifier le fichier, utiliser Pillow, sécuriser le chemin
Image étirée Redimensionnement mal choisi Préférer contain(), cover() ou fit() selon le besoin
Transparence étrange Mode couleur inadapté Travailler en RGBA avant l’affichage
Application lente Images trop grandes ou conversions répétées Redimensionner une seule fois, puis mettre en cache les variantes

Dans une application distribuée, je sécurise aussi les chemins avec pathlib.Path(__file__).resolve() ou une fonction équivalente, parce qu’un chemin qui marche dans l’IDE peut casser dès qu’on lance le programme depuis un autre dossier ou qu’on l’embarque dans un exécutable. Une fois ces points nettoyés, Tkinter devient beaucoup plus prévisible, ce qui amène naturellement à la bonne stratégie globale.

Le compromis que je recommande pour un projet Tkinter en 2026

Pour un projet simple ou intermédiaire, ma règle est nette: Tkinter pour l’interface, Pillow pour le traitement. Cette séparation garde le code lisible et évite d’exiger du widget qu’il fasse un travail qu’il n’a jamais été conçu pour faire. En 2026, c’est encore le meilleur compromis pour un outil bureau sobre, rapide à maintenir et peu dépendant.

  • Je charge l’image originale une seule fois.
  • Je crée les variantes redimensionnées ou filtrées au moment du chargement.
  • Je stocke les objets PhotoImage dans des attributs persistants.
  • Je préfère un canevas pour l’affichage libre, un label pour un visuel fixe, et un bouton seulement pour une petite icône.
  • Je passe à un autre outil graphique si l’application devient très riche en médias, en effets ou en animation continue.

Avec cette approche, l’affichage d’image dans Tkinter reste prévisible, léger et suffisamment souple pour la plupart des tableaux de bord, outils internes et petites interfaces métiers. Le vrai gain n’est pas seulement visuel: c’est surtout de garder un code qui ne se bat pas contre le cycle de vie des objets, les formats de fichiers et le redimensionnement.

Questions fréquentes

Tkinter, via PhotoImage, prend en charge nativement les formats PGM, PPM, GIF et PNG. Pour les autres formats comme JPEG, BMP, TIFF ou WebP, il est recommandé d'utiliser la bibliothèque Pillow pour la conversion et la gestion.
C'est un piège courant ! L'image disparaît car la référence Python à l'objet PhotoImage est perdue, et le garbage collector la supprime. Pour éviter cela, stockez l'objet PhotoImage dans un attribut persistant de votre widget ou de votre fenêtre (ex: `label.image = photo`).
Utilisez Pillow avant de convertir l'image en PhotoImage. Des méthodes comme `ImageOps.contain()` ou `ImageOps.cover()` permettent de redimensionner en conservant le ratio, évitant ainsi les déformations. `resize()` peut déformer si le ratio n'est pas maintenu.
Le Canvas est idéal lorsque vous avez besoin de placer, superposer, déplacer ou remplacer des images dynamiquement et avec plus de liberté. Un Label est suffisant pour afficher une image fixe ou une icône simple sans interaction complexe.
Oui, et c'est recommandé ! Chargez l'image originale une seule fois avec Pillow, puis créez des variantes (tournées, filtrées, redimensionnées) à partir de cette source immuable. Cela évite les effets cumulés et maintient la qualité de l'image.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

tkinter image afficher image tkinter pillow redimensionner image tkinter recadrer image tkinter gérer formats image tkinter
Autor Noël Besnard
Noël Besnard
Je suis Noël Besnard, un analyste de l'industrie passionné par les domaines de la technologie, notamment le web, l'intelligence artificielle, les réseaux et la sécurité. Avec plus de dix ans d'expérience dans l'analyse des tendances du marché technologique, j'ai acquis une expertise approfondie qui me permet d'explorer les innovations et les défis auxquels notre monde numérique est confronté. Mon approche consiste à simplifier des données complexes et à fournir une analyse objective, ce qui me permet de rendre les sujets techniques accessibles à tous. Je m'engage à offrir des informations précises et à jour, en vérifiant rigoureusement les faits pour garantir la fiabilité de chaque article que je publie. Mon objectif est d'aider les lecteurs à naviguer dans cet univers en constante évolution, en leur fournissant les outils nécessaires pour comprendre les enjeux technologiques contemporains.

Commentaires (0)

Ajouter un commentaire