"tkinter" — Interface Python pour *Tcl/Tk*
******************************************

**Code source :** Lib/tkinter/__init__.py

======================================================================

Le paquet "tkinter" (« interface Tk ») est l'interface Python standard
de la boîte à outils d'interface utilisateur graphique (GUI) *Tcl/Tk*.
*Tk* et "tkinter" sont disponibles sur la plupart des plates-formes
Unix, y compris macOS, ainsi que sur les systèmes Windows.

Exécuter "python -m tkinter" depuis la ligne de commande ouvre une
fenêtre de démonstration d'une interface *Tk* simple, vous indiquant
que "tkinter" est correctement installé sur votre système et indiquant
également quelle version de *Tcl/Tk* est installée ; vous pouvez donc
lire la documentation *Tcl/Tk* spécifique à cette version.

*Tkinter* prend en charge une gamme de versions *Tcl/Tk*, construites
avec ou sans prise en charge des fils d'exécution multiples. La
version binaire officielle de Python utilise *Tcl/Tk* 8.6 gérant les
fils d'exécution multiples. Voir le code source du module "_tkinter"
pour plus d'informations sur les versions prises en charge.

*Tkinter* n'est pas une simple enveloppe, mais ajoute une bonne partie
de sa propre logique pour rendre l'expérience plus *pythonique*. Cette
documentation se concentre sur ces ajouts et modifications, et se
réfère à la documentation officielle de *Tcl/Tk* pour les détails qui
restent inchangés.

Note:

  *Tcl/Tk* 8.5 (2007) a introduit un ensemble moderne de composants
  d'interface utilisateur thématiques ainsi qu'une nouvelle API pour
  les utiliser. Les anciennes et les nouvelles API sont toujours
  disponibles. La plupart des documents que vous trouverez en ligne
  utilisent toujours l'ancienne API et peuvent être terriblement
  obsolètes.

Voir aussi:

  * TkDocs
       Tutoriel complet sur la création d'interfaces utilisateur avec
       *Tkinter*. Explique les concepts clés et illustre les approches
       recommandées à l'aide de l'API moderne.

  * Référence *Tkinter* 8.5 : une interface graphique pour Python
       Documentation de référence pour *Tkinter* 8.5 détaillant les
       classes, méthodes et options disponibles (ressource en
       anglais).

  Ressources *Tcl*/Tk :

  * Commandes *Tk*
       Référence complète de chacune des commandes *Tcl/Tk* sous-
       jacentes utilisées par *Tkinter* (ressource en anglais).

  * Page d'accueil de Tcl/Tk
       Documentation supplémentaire et liens vers le développement
       principal de *Tcl/Tk*.

  Livres :

  * Modern Tkinter for Busy Python Developers
       par Mark Roseman. (ISBN 978-1999149567)

  * Python GUI programming with Tkinter
       By Alan D. Moore. (ISBN 978-1788835886)

  * Programming Python
       livre de Mark Lutz, qui couvre excellemment bien *Tkinter*
       (ISBN 978-0596158101).

  * Tcl and the Tk Toolkit (2nd edition)
       par John Ousterhout, inventeur de *Tcl/Tk*, et Ken Jones ; ne
       couvre pas *Tkinter*. (ISBN 978-0321336330)


Architecture
============

*Tcl/Tk* n'est pas une bibliothèque unique mais se compose plutôt de
quelques modules distincts, chacun avec des fonctionnalités distinctes
et sa propre documentation officielle. Les versions binaires de Python
sont également livrées avec un module complémentaire.

Tcl
   *Tcl* est un langage de programmation interprété dynamique, tout
   comme Python. Bien qu'il puisse être utilisé seul comme langage de
   programmation à usage général, il est le plus souvent intégré dans
   les applications C en tant que moteur de script ou interface avec
   la boîte à outils *Tk*. La bibliothèque *Tcl* a une interface C
   pour créer et gérer une ou plusieurs instances d'un interpréteur
   *Tcl*, exécuter des commandes et des scripts *Tcl* dans ces
   instances et ajouter des commandes personnalisées implémentées en
   *Tcl* ou C. Chaque interpréteur a une file d'attente d'événements
   et il y a facilités pour lui envoyer des événements et les traiter.
   Contrairement à Python, le modèle d'exécution de *Tcl* est conçu
   autour du multitâches coopératif, et *Tkinter* comble cette
   différence (voir Modèle de thread pour plus de détails).

Tk
   *Tk* est un paquet *Tcl* implémenté en C qui ajoute des commandes
   personnalisées pour créer et manipuler des widgets d'interface
   graphique. Chaque objet "Tk" embarque sa propre instance
   d'interpréteur *Tcl* avec *Tk* chargé dedans. Les widgets de *Tk*
   sont très personnalisables, mais au prix d'une apparence datée.
   *Tk* utilise la file d'attente d'événements de *Tcl* pour générer
   et traiter les événements de l'interface graphique.

Ttk
   *Themed Tk* (*Ttk*) est une nouvelle famille de widgets *Tk* qui
   offrent une bien meilleure apparence sur différentes plates-formes
   que la plupart des widgets *Tk* classiques. *Ttk* est distribué
   dans le cadre de *Tk*, à partir de la version 8.5 de *Tk*. Les
   liaisons Python sont fournies dans un module séparé, "tkinter.ttk".

En interne, *Tk* et Ttk utilisent les fonctionnalités du système
d'exploitation sous-jacent, c'est-à-dire Xlib sur Unix/X11, Cocoa sur
macOS, GDI sur Windows.

Lorsque votre application Python utilise une classe dans *Tkinter*,
par exemple pour créer un widget, le module "tkinter" assemble d'abord
une chaîne de commande *Tcl/Tk*. Il passe cette chaîne de commande
*Tcl* à un module binaire interne "_tkinter", qui appelle ensuite
l'interpréteur *Tcl* pour l'évaluer. L'interpréteur *Tcl* appelle
alors les paquets *Tk* et/ou Ttk, qui à leur tour font des appels à
Xlib, Cocoa ou GDI.


Modules *Tkinter*
=================

La prise en charge de *Tkinter* est répartie sur plusieurs modules. La
plupart des applications auront besoin du module principal "tkinter",
ainsi que du module "tkinter.ttk", qui fournit l'ensemble de widgets
thématiques modernes et l'API :

   from tkinter import *
   from tkinter import ttk

class tkinter.Tk(screenName=None, baseName=None, className='Tk', useTk=True, sync=False, use=None)

   Construit un widget *Tk* de niveau supérieur, qui est généralement
   la fenêtre principale d'une application, et initialise un
   interpréteur *Tcl* pour ce widget. Chaque instance a son propre
   interpréteur *Tcl* associé.

   La classe "Tk" est généralement instanciée en utilisant toutes les
   valeurs par défaut. Cependant, les arguments nommés suivants sont
   actuellement reconnus :

   *screenName*
      Lorsqu'il est donné (sous forme de chaîne), définit la variable
      d'environnement "DISPLAY". (X11 uniquement)

   *baseName*
      Nom du fichier de profil. Par défaut, *baseName* est dérivé du
      nom du programme ("sys.argv[0]").

   *className*
      Nom de la classe de widget. Utilisé comme fichier de profil et
      aussi comme nom avec lequel *Tcl* est invoqué (*argv0* dans
      *interp*).

   *useTk*
      S'il vaut "True", initialise le sous-système *Tk*. La fonction
      "tkinter.*Tcl*()" définit ceci sur "False".

   *sync*
      Si "True", exécute toutes les commandes du serveur X de manière
      synchrone, afin que les erreurs soient signalées immédiatement.
      Peut être utilisé pour le débogage. (X11 uniquement)

   *use*
      Spécifie l'*id* de la fenêtre dans laquelle intégrer
      l'application, au lieu de la créer en tant que fenêtre de niveau
      supérieur indépendante. *id* doit être spécifié de la même
      manière que la valeur de l'option -use pour les widgets de
      niveau supérieur (c'est-à-dire qu'il a une forme semblable à
      celle renvoyée par "winfo_id()").

      Notez que sur certaines plates-formes, cela ne fonctionne
      correctement que si *id* fait référence à un cadre *Tk* ou à un
      niveau supérieur dont l'option -container est activée.

   "Tk" reads and interprets profile files, named ".*className*.tcl"
   and ".*baseName*.tcl", into the Tcl interpreter and calls "exec()"
   on the contents of ".*className*.py" and ".*baseName*.py".  The
   path for the profile files is the "HOME" environment variable or,
   if that isn't defined, then "os.curdir".

   tk

      L'objet d'application *Tk* créé en instanciant "Tk". Cela donne
      accès à l'interpréteur *Tcl*. Chaque widget auquel est attachée
      la même instance de "Tk" a la même valeur pour son attribut
      "tk".

   master

      L'objet widget qui contient ce widget. Pour "Tk", le *master*
      est "None" car c'est la fenêtre principale. Les termes *master*
      et *parent* sont similaires et parfois utilisés de manière
      interchangeable comme noms d'arguments ; cependant, appeler
      "winfo_parent()" renvoie une chaîne du nom du widget alors que
      "master" renvoie l'objet. *parent*/*enfant* reflète la relation
      arborescente tandis que *maître*/*esclave* reflète la structure
      du conteneur.

   children

      Les descendants immédiats de ce widget en tant que "dict" avec
      les noms des widgets enfants comme clés et les objets d'instance
      enfants comme valeurs.

tkinter.Tcl(screenName=None, baseName=None, className='Tk', useTk=False)

   La fonction "Tcl()" est une fonction fabrique qui crée un objet
   similaire à celui créé par la classe "Tk", sauf qu'elle
   n'initialise pas le sous-système *Tk*.  Ceci est le plus souvent
   utile lorsque vous pilotez l'interpréteur *Tcl* dans un
   environnement où vous ne voulez pas créer des fenêtres de haut
   niveau supplémentaires, ou alors si c'est impossible (comme les
   systèmes Unix/Linux sans un serveur X).  Un objet créé par "Tcl()"
   peut avoir une fenêtre de haut niveau créée (et le sous-système
   *Tk* initialisé) en appelant sa méthode "loadtk()".

Parmi les modules qui savent gérer *Tk*, nous pouvons citer :

"tkinter"
   Module *Tkinter* principal

"tkinter.colorchooser"
   Boîte de dialogue permettant à l'utilisateur de choisir une
   couleur.

"tkinter.commondialog"
   Classe de base pour les boîtes de dialogue définies dans les autres
   modules listés ici.

"tkinter.filedialog"
   Boîtes de dialogue standard permettant à l'utilisateur de spécifier
   un fichier à ouvrir ou à enregistrer.

"tkinter.font"
   Utilitaires pour gérer les polices de caractères.

"tkinter.messagebox"
   Accès aux boîtes de dialogue *Tk* standard.

"tkinter.scrolledtext"
   Outil d'affichage de texte avec une barre de défilement verticale
   intégrée.

"tkinter.simpledialog"
   Boîtes de dialogue simples et fonctions utilitaires.

"tkinter.ttk"
   Ensemble de widgets thématiques introduit dans *Tk* 8.5, offrant
   des alternatives modernes à de nombreux widgets classiques du
   module principal "tkinter".

Modules supplémentaires :

"_tkinter"
   Module binaire qui contient l'interface de bas niveau vers
   *Tcl/Tk*. Il est automatiquement importé par le module principal
   "tkinter" et ne doit jamais être utilisé directement par les
   programmeurs d'applications. Il s'agit généralement d'une
   bibliothèque partagée (ou *DLL*), mais peut dans certains cas être
   lié statiquement à l'interpréteur Python.

"idlelib"
   Environnement de développement et d'apprentissage intégré de Python
   (*IDLE* pour *Integrated Development and Learning Environment*).
   Basé sur "tkinter".

"tkinter.constants"
   Constantes symboliques pouvant être utilisées à la place des
   chaînes lors de la transmission de divers paramètres aux appels
   *Tkinter*. Importé automatiquement par le module principal
   "tkinter".

"tkinter.dnd"
   Gestion du glisser-déposer pour "tkinter". Il s'agit d'une méthode
   expérimentale qui ne sera plus maintenue lorsqu'elle sera remplacée
   par *Tk DND*.

"tkinter.tix"
   (obsolète) Un ancien paquet *Tcl/Tk* tiers qui ajoute plusieurs
   nouveaux widgets. De meilleures alternatives pour la plupart de ces
   widgets peuvent être trouvées dans "tkinter.ttk".

"turtle"
   Tortue graphique dans une fenêtre *Tk*.


Guide de survie *Tkinter*
=========================

Cette section n'est pas conçue pour être un tutoriel exhaustif sur
*Tk* ou *Tkinter*. Pour cela, reportez-vous à l'une des ressources
externes mentionnées précédemment. Cette section fournit plutôt un
guide très rapide sur ce à quoi ressemble une application *Tkinter*,
identifie les concepts fondamentaux de *Tk* et explique comment
l'enveloppe *Tkinter* est structurée.

Le reste de cette section vous aidera à identifier les classes,
méthodes et options dont vous aurez besoin dans votre application
*Tkinter*, et où trouver une documentation plus détaillée à leur
sujet, y compris dans le manuel de référence officiel *Tcl/Tk*.


Un simple programme *Hello World*
---------------------------------

Nous commençons par disséquer une application *Hello World* dans
*Tkinter*. Ce n'est pas le plus petit que nous puissions écrire, mais
il en contient suffisamment pour illustrer certains concepts clés que
vous devez connaître.

   from tkinter import *
   from tkinter import ttk
   root = Tk()
   frm = ttk.Frame(root, padding=10)
   frm.grid()
   ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
   ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
   root.mainloop()

Après les importations, la ligne suivante crée une instance de la
classe "Tk", qui initialise *Tk* et crée son interpréteur *Tcl*
associé. Elle crée également une fenêtre de niveau supérieur, connue
sous le nom de fenêtre racine, qui sert de fenêtre principale de
l'application.

La ligne suivante crée un widget de cadre, qui dans ce cas contiendra
une étiquette et un bouton que nous créerons ensuite. Le cadre est
ajusté à l'intérieur de la fenêtre racine.

La ligne suivante crée un widget d'étiquette contenant une chaîne de
texte statique. La méthode "grid()" est utilisée pour spécifier la
disposition relative (position) de l'étiquette dans son widget cadre
contenant, similaire au fonctionnement des tableaux en HTML.

Un widget de bouton est alors créé et placé à droite de l'étiquette.
Lorsqu'il est pressé, il appelle la méthode "destroy()" de la fenêtre
racine.

Enfin, la méthode "mainloop()" affiche tout sur l'écran et répond aux
entrées de l'utilisateur jusqu'à ce que le programme se termine.


Concepts importants de *Tk*
---------------------------

Même ce programme simple illustre les concepts clés suivants de *Tk* :

widgets
   Une interface utilisateur *Tkinter* est composée de *widgets*
   individuels. Chaque widget est représenté comme un objet Python,
   instancié à partir de classes comme "ttk.Frame", "ttk.Label" et
   "ttk.Button".

hiérarchie des widgets
   Les widgets sont organisés dans une *hiérarchie*. L'étiquette et le
   bouton étaient contenus dans un cadre, qui à son tour était contenu
   dans la fenêtre racine. Lors de la création de chaque widget
   *enfant*, son widget *parent* est passé comme premier argument au
   constructeur du widget.

options de configuration
   Les widgets ont des *options de configuration*, qui modifient leur
   apparence et leur comportement, comme le texte à afficher dans une
   étiquette ou un bouton. Différentes classes de widgets auront
   différents ensembles d'options.

gestion de la géométrie
   Les widgets ne sont pas automatiquement ajoutés à l'interface
   utilisateur lorsqu'ils sont créés. Un *gestionnaire de géométrie*
   comme "grid" contrôle où ils sont placés dans l'interface
   utilisateur.

boucle d'événements
   *Tkinter* réagit aux entrées de l'utilisateur, aux modifications de
   votre programme et même rafraîchit l'affichage uniquement lors de
   l'exécution active d'une *boucle d'événements*. Si votre programme
   n'exécute pas la boucle d'événements, votre interface utilisateur
   n'est pas mise à jour.


Comprendre comment *Tkinter* enveloppe *Tcl/Tk*
-----------------------------------------------

When your application uses Tkinter's classes and methods, internally
Tkinter is assembling strings representing Tcl/Tk commands, and
executing those commands in the Tcl interpreter attached to your
application's "Tk" instance.

Qu'il s'agisse d'essayer de naviguer dans la documentation de
référence, d'essayer de trouver la bonne méthode ou option, d'adapter
du code existant ou de déboguer votre application *Tkinter*, il arrive
qu'il soit utile de comprendre à quoi ressemblent ces commandes
*Tcl/Tk* sous-jacentes.

Pour illustrer cette idée, voici l'équivalent *Tcl/Tk* de la partie
principale du script *Tkinter* ci-dessus.

   ttk::frame .frm -padding 10
   grid .frm
   grid [ttk::label .frm.lbl -text "Hello World!"] -column 0 -row 0
   grid [ttk::button .frm.btn -text "Quit" -command "destroy ."] -column 1 -row 0

La syntaxe de *Tcl* est similaire à celle de nombreux langages
*shell*, où le premier mot est la commande à exécuter, suivi des
arguments de cette commande, séparés par des espaces. Sans entrer dans
trop de détails, notez ce qui suit :

* Les commandes utilisées pour créer des widgets (comme "ttk::frame")
  correspondent aux classes de widgets dans *Tkinter*.

* Les options du widget *Tcl* (comme "-text") correspondent aux
  arguments de mots-clés dans *Tkinter*.

* Les widgets sont référencés par un *chemin* en *Tcl* (comme
  ".frm.btn"), alors que *Tkinter* n'utilise pas de noms mais des
  références d'objets.

* La place d'un widget dans la hiérarchie des widgets est encodée dans
  son nom de chemin (hiérarchique), qui utilise un "." (point) comme
  séparateur de chemin. Le chemin d'accès à la fenêtre racine est
  simplement "." (point). Dans *Tkinter*, la hiérarchie n'est pas
  définie par le nom de chemin mais en spécifiant le widget parent
  lors de la création de chaque widget enfant.

* Les opérations qui sont implémentées comme des *commandes* séparées
  dans *Tcl* (comme "grid" ou "destroy") sont représentées comme des
  *méthodes* sur les objets widget de *Tkinter*. Comme vous le verrez
  bientôt, à d'autres moments, *Tcl* utilise ce qui semble être des
  appels de méthode sur des objets widget, qui reflètent plus
  fidèlement ce qui serait utilisé dans *Tkinter*.


Comment puis-je… ? Quelle option… ?
-----------------------------------

Si vous ne savez pas comment faire quelque chose dans *Tkinter* et que
vous ne le trouvez pas immédiatement dans le didacticiel ou la
documentation de référence que vous utilisez, il existe quelques
stratégies qui peuvent être utiles.

Tout d'abord, rappelez-vous que les détails du fonctionnement des
widgets individuels peuvent varier selon les différentes versions de
*Tkinter* et de *Tcl/Tk*. Si vous recherchez de la documentation,
assurez-vous qu'elle correspond aux versions Python et *Tcl/Tk*
installées sur votre système.

Lorsque vous recherchez comment utiliser une API, il est utile de
connaître le nom exact de la classe, de l'option ou de la méthode que
vous utilisez. L'introspection, soit dans un shell Python interactif,
soit avec "print()", peut vous aider à identifier ce dont vous avez
besoin.

Pour savoir quelles options de configuration sont disponibles sur
n'importe quel widget, appelez sa méthode "configure()", qui renvoie
un dictionnaire contenant une variété d'informations sur l'objet, y
compris ses valeurs par défaut et actuelles. Utilisez "keys()" pour
obtenir uniquement les noms de chaque option.

   btn = ttk.Button(frm, ...)
   print(btn.configure().keys())

Comme la plupart des widgets ont de nombreuses options de
configuration en commun, il peut être utile de savoir lesquelles sont
spécifiques à une classe de widget particulière. Comparer la liste des
options à celle d'un widget plus simple, comme un cadre, est une façon
de faire.

   print(set(btn.configure().keys()) - set(frm.configure().keys()))

De même, vous pouvez trouver les méthodes disponibles pour un objet
widget en utilisant la fonction standard "dir()". Si vous l'essayez,
vous verrez qu'il existe plus de 200 méthodes de widget courantes
donc, encore une fois, identifier celles spécifiques à une classe de
widget est utile.

   print(dir(btn))
   print(set(dir(btn)) - set(dir(frm)))


Navigation dans le manuel de référence *Tcl/Tk*
-----------------------------------------------

Comme indiqué, le manuel de référence officiel des commandes Tk (pages
de manuel) est souvent la description la plus précise sur ce que font
des opérations spécifiques sur les widgets. Même lorsque vous
connaissez le nom de l'option ou de la méthode dont vous avez besoin,
il se peut que vous ayez encore quelques endroits à chercher.

Alors que toutes les opérations dans *Tkinter* sont implémentées en
tant qu'appels de méthode sur des objets widget, vous avez vu que de
nombreuses opérations *Tcl/Tk* apparaissent sous forme de commandes
qui prennent un chemin d'accès de widget comme premier paramètre,
suivi de paramètres facultatifs, par exemple :

   destroy .
   grid .frm.btn -column 0 -row 0

D'autres, cependant, ressemblent plus à des méthodes appelées sur un
objet widget (en fait, lorsque vous créez un widget en *Tcl/Tk*, il
crée une commande *Tcl* avec le nom du chemin du widget, le premier
paramètre de cette commande étant le nom d'une méthode à appeler).

   .frm.btn invoke
   .frm.lbl configure -text "Goodbye"

Dans la documentation de référence officielle *Tcl/Tk*, vous trouvez
la plupart des opérations qui ressemblent à des appels de méthode dans
la page de manuel d'un widget spécifique (par exemple, vous trouvez la
méthode "invoke()" dans la page de manuel sur le ttk::button), tandis
que les fonctions qui prennent un widget comme paramètre ont souvent
leur propre page de manuel (par exemple, grid).

Vous trouvez de nombreuses options et méthodes courantes dans les
pages de manuel des options ou ttk::widget, tandis que d'autres se
trouvent dans la page de manuel d'une classe de widget spécifique.

Vous constaterez également que de nombreuses méthodes *Tkinter* ont
des noms composés, par exemple "winfo_x()", "winfo_height()",
"winfo_viewable()". Vous trouverez de la documentation pour tout cela
dans la page de manuel winfo.

Note:

  de manière quelque peu déroutante, il existe également des méthodes
  sur tous les widgets *Tkinter* qui ne fonctionnent pas réellement
  sur le widget, mais fonctionnent à une portée globale,
  indépendamment de tout widget. Les méthodes d'accès au presse-
  papiers ou à la sonnerie du système en font partie (il se trouve
  qu'elles sont implémentées en tant que méthodes dans la classe de
  base "Widget" dont héritent tous les widgets *Tkinter*).


Fils d'exécution multiples
==========================

Python et *Tcl/Tk* gèrent les fils d'exécution multiples (*threads*)
de manière très différente, et "tkinter" essaie de combler cette
différence. Si vous utilisez des fils d'exécution multiples, vous
devrez sûrement en être conscient.

Un interpréteur Python peut être associé à de nombreux fils
d'exécution. En *Tcl*, plusieurs fils d'exécution peuvent être créés,
mais chaque fil d'exécution est associé à une instance d'interpréteur
*Tcl* distincte. Les fils d'exécution multiples peuvent également
créer plusieurs instances d'interpréteur, bien que chaque instance
d'interpréteur ne puisse être utilisée que par le seul fil d'exécution
qui l'a créée.

Chaque objet "Tk" créé par "tkinter" contient un interpréteur *Tcl*.
Il garde également une trace du fil d'exécution qui a créé cet
interpréteur. Les appels à "tkinter" peuvent être effectués à partir
de n'importe quel fil d'exécution Python. En interne, si un appel
provient d'un fil d'exécution autre que celui qui a créé l'objet "Tk",
un événement est posté dans la file d'attente d'événements de
l'interpréteur et, lorsqu'il est exécuté, le résultat est renvoyé au
fil d'exécution Python appelant.

Les applications *Tcl/Tk* sont normalement pilotées par les
événements, ce qui signifie qu'après l'initialisation, l'interpréteur
exécute une boucle d'événements (c'est-à-dire "Tk.mainloop()") et
répond aux événements. Comme il s'agit d'un seul fil d'exécution, les
gestionnaires d'événements doivent répondre rapidement, sinon ils
empêchent le traitement d'autres événements. Pour éviter cela, les
calculs de longue durée ne doivent pas s'exécuter dans un gestionnaire
d'événements, mais sont soit divisés en plus petits morceaux à l'aide
de temporisateurs, soit exécutés dans un autre fil d'exécution. Ceci
est différent de nombreux kits d'outils d'interface graphique où
l'interface graphique s'exécute dans un fil d'exécution complètement
séparé de tout le code d'application, y compris les gestionnaires
d'événements.

Si l'interpréteur *Tcl* n'exécute pas la boucle d'événements et ne
traite pas les événements, tout appel "tkinter" effectué à partir de
fils d'exécution autres que celui exécutant l'interpréteur *Tcl*
échoue.

Plusieurs cas particuliers existent :

* Les bibliothèques *Tcl/Tk* peuvent être construites de manière à ne
  pas gérer les fils d'exécution multiples. Dans ce cas, "tkinter"
  appelle la bibliothèque à partir du fil d'exécution Python
  d'origine, même s'il est différent du fil d'exécution qui a créé
  l'interpréteur *Tcl*. Un verrou global garantit qu'un seul appel se
  produit à la fois.

* Alors que "tkinter" vous permet de créer plus d'une instance d'un
  objet "Tk" (avec son propre interpréteur), tous les interpréteurs
  qui font partie du même fil d'exécution partagent une file d'attente
  d'événements commune, qui devient très vite moche. En pratique, ne
  créez pas plus d'une instance de "Tk" à la fois. Sinon, il est
  préférable de les créer dans des fils d'exécution séparés et de vous
  assurer que vous exécutez une instance *Tcl/Tk* compatible avec les
  fils d'exécution multiples.

* Le blocage des gestionnaires d'événements n'est pas le seul moyen
  d'empêcher l'interpréteur *Tcl* de bien gérer la boucle
  d'événements. Il est même possible d'exécuter plusieurs boucles
  d'événements imbriquées ou d'abandonner complètement la boucle
  d'événements. Si vous faites quelque chose de délicat en ce qui
  concerne les événements ou les fils d'exécution, soyez conscient de
  ces possibilités.

* Il existe quelques fonctions select "tkinter" qui ne fonctionnent
  actuellement que lorsqu'elles sont appelées depuis le fil
  d'exécution qui a créé l'interpréteur *Tcl*.


Guide pratique
==============


Définition des options
----------------------

Les options contrôlent des paramètres tels que la couleur et la
largeur de la bordure d'un objet graphique. Les options peuvent être
réglées de trois façons :

Lors de la création de l'objet, à l'aide d'arguments par mots-clés
      fred = Button(self, fg="red", bg="blue")

Après la création de l'objet, en manipulant le nom de l'option comme
une entrée de dictionnaire
      fred["fg"] = "red"
      fred["bg"] = "blue"

Utilisez la méthode "config()" pour mettre à jour plusieurs attributs
après la création de l'objet
      fred.config(fg="red", bg="blue")

Pour l'explication complète d'une option donnée et de son
comportement, voir les pages de manuel *Tk* de l'objet graphique en
question.

Notez que les pages de manuel listent « OPTIONS STANDARD » et «
OPTIONS SPÉCIFIQUES D'OBJETS GRAPHIQUES » pour chaque objet graphique.
La première est une liste d'options communes à de nombreux objets
graphiques, la seconde est une liste d'options propres à cet objet
graphique particulier.  Les options standard sont documentées sur la
page de manuel *options(3)*.

Aucune distinction n'est faite dans ce document entre les options
standard et les options spécifiques à un objet graphique.  Certaines
options ne s'appliquent pas à certains types d'objets graphiques. La
réaction d'un objet graphique donné à une option particulière dépend
de la classe de l'objet graphique ; les boutons possèdent une option
"command", pas les étiquettes.

Les options gérées par un objet graphique donné sont listées dans la
page de manuel de cet objet graphique, ou peuvent être interrogées à
l'exécution en appelant la méthode "config()" sans argument, ou en
appelant la méthode "keys()" sur cet objet graphique.  La valeur de
retour de ces appels est un dictionnaire dont la clé est le nom de
l'option sous forme de chaîne (par exemple, "'relief'") et dont les
valeurs sont des *5-uplets*.

Certaines options, comme "bg", sont des synonymes d'options communes
qui ont des noms longs ("bg" est une abréviation pour "background" «
arrière-plan »). Passer le nom abrégé d'une option à la méthode
"config()" renvoie un couple, pas un quintuplet. Le couple renvoyé
contient le nom abrégé et le nom *réel* de l'option, par exemple
"('bg','background')".

+---------+-----------------------------------+----------------+
| Index   | Signification                     | Exemple        |
|=========|===================================|================|
| 0       | nom des options                   | "'relief'"     |
+---------+-----------------------------------+----------------+
| 1       | nom de l'option pour la recherche | "'relief'"     |
|         | dans la base de données           |                |
+---------+-----------------------------------+----------------+
| 2       | classe de l'option pour la        | "'Relief'"     |
|         | recherche dans la base de données |                |
+---------+-----------------------------------+----------------+
| 3       | valeur par défaut                 | "'raised'"     |
+---------+-----------------------------------+----------------+
| 4       | valeur actuelle                   | "'groove'"     |
+---------+-----------------------------------+----------------+

Exemple :

   >>> print(fred.config())
   {'relief': ('relief', 'relief', 'Relief', 'raised', 'groove')}

Bien sûr, le dictionnaire affiché contient toutes les options
disponibles et leurs valeurs.  Ceci n'est donné qu'à titre d'exemple.


L'empaqueteur
-------------

L'empaqueteur est l'un des mécanismes de *Tk* pour la gestion de la
disposition des éléments sur l'écran.   Les gestionnaires de géométrie
sont utilisés pour spécifier le positionnement relatif du
positionnement des objets graphiques dans leur conteneur — leur
*constructeur* mutuel.  Contrairement au plus encombrant *placeur*
(qui est utilisé moins souvent, et nous n'en parlons pas ici),
l'empaqueteur prend les spécifications qualitatives de relation —
*above*, *to the left of*, *filling*, etc — et calcule tout pour
déterminer les coordonnées exactes du placement pour vous.

La taille d'un objet graphique *constructeur* est déterminée par la
taille des « objets graphiques hérités » à l'intérieur.  L'empaqueteur
est utilisé pour contrôler l'endroit où les objets graphiques hérités
apparaissent à l'intérieur du constructeur dans lequel ils sont
empaquetés.  Vous pouvez regrouper des objets graphiques dans des
cadres, et des cadres dans d'autres cadres, afin d'obtenir le type de
mise en page souhaité. De plus, l'arrangement est ajusté dynamiquement
pour s'adapter aux changements incrémentiels de la configuration, une
fois qu'elle est empaquetées.

Notez que les objets graphiques n'apparaissent pas tant que leur
disposition n'a pas été spécifiée avec un gestionnaire de géométrie.
C'est une erreur de débutant courante de ne pas tenir compte de la
spécification de la géométrie, puis d'être surpris lorsque l'objet
graphique est créé mais que rien n'apparaît.  Un objet graphique
n'apparaît qu'après que, par exemple, la méthode "pack()" de
l'empaqueteur lui ait été appliquée.

La méthode "pack()" peut être appelée avec des paires mot-
clé-option/valeur qui contrôlent où l'objet graphique doit apparaître
dans son conteneur et comment il doit se comporter lorsque la fenêtre
principale de l'application est redimensionnée.  En voici quelques
exemples :

   fred.pack()                     # defaults to side = "top"
   fred.pack(side="left")
   fred.pack(expand=1)


Options de l'empaqueteur
------------------------

Pour de plus amples informations sur l'empaqueteur et les options
qu'il peut prendre, voir les pages de manuel et la page 183 du livre
de John Ousterhout.

*anchor*
   Type d'ancrage.  Indique l'endroit où l'empaqueteur doit placer
   chaque enfant dans son espace.

*expand*
   Booléen, "0" ou "1".

*fill*
   Valeurs acceptées : "'x'", "'y'", "'both'", "'none'".

*ipadx* et *ipady*
   Une distance — désignant l'écart interne de chaque côté de l'objet
   graphique hérité.

*padx* et *pady*
   Une distance — désignant l'écart externe de chaque côté de l'objet
   graphique hérité.

*side*
   Valeurs acceptées : "'left'", "'right'", "'top'", "'bottom'".


Association des variables de l'objet graphique
----------------------------------------------

L'assignation d'une valeur à certains objets graphiques (comme les
objets graphique de saisie de texte) peut être liée directement aux
variables de votre application à l'aide d'options spéciales.  Ces
options sont "variable", "textvariable", "onvalue", "offvalue" et
"value".  Ce lien fonctionne dans les deux sens : si la variable
change pour une raison ou pour une autre, l'objet graphique auquel
elle est connectée est mis à jour pour refléter la nouvelle valeur.

Malheureusement, dans l'implémentation actuelle de "tkinter" il n'est
pas possible de passer une variable Python arbitraire à un objet
graphique via une option "variable" ou "textvariable".  Les seuls
types de variables pour lesquels cela fonctionne sont les variables
qui sont sous-classées à partir d'une classe appelée "Variable",
définie dans "tkinter".

Il existe de nombreuses sous-classes utiles de "Variable" déjà
définies : "StringVar", "IntVar", "DoubleVar" et "BooleanVar". Pour
lire la valeur courante d'une telle variable, appelez la méthode
"get()" dessus et, pour changer sa valeur, appelez la méthode "set()".
Si vous suivez ce protocole, l'objet graphique suivra toujours la
valeur de la variable, sans autre intervention de votre part.

Par exemple :

   import tkinter as tk

   class App(tk.Frame):
       def __init__(self, master):
           super().__init__(master)
           self.pack()

           self.entrythingy = tk.Entry()
           self.entrythingy.pack()

           # Create the application variable.
           self.contents = tk.StringVar()
           # Set it to some value.
           self.contents.set("this is a variable")
           # Tell the entry widget to watch this variable.
           self.entrythingy["textvariable"] = self.contents

           # Define a callback for when the user hits return.
           # It prints the current value of the variable.
           self.entrythingy.bind('<Key-Return>',
                                self.print_contents)

       def print_contents(self, event):
           print("Hi. The current entry content is:",
                 self.contents.get())

   root = tk.Tk()
   myapp = App(root)
   myapp.mainloop()


Le gestionnaire de fenêtres
---------------------------

Dans *Tk*, il y a une commande pratique, "wm", pour interagir avec le
gestionnaire de fenêtres.  Les options de la commande "wm" vous
permettent de contrôler les titres, le placement, les icônes en mode
*bitmap* et encore d'autres choses du même genre.  Dans "tkinter", ces
commandes ont été implémentées en tant que méthodes sur la classe
"Wm".  Les objets graphiques de haut niveau sont sous-classés à partir
de la classe "Wm", ils peuvent donc appeler directement les méthodes
de "Wm".

Pour accéder à la fenêtre du plus haut niveau qui contient un objet
graphique donné, vous pouvez souvent simplement vous référer au parent
de cet objet graphique.  Bien sûr, si l'objet graphique a été
empaqueté à l'intérieur d'un cadre, le parent ne représentera pas la
fenêtre de plus haut niveau.  Pour accéder à la fenêtre du plus haut
niveau qui contient un objet graphique arbitraire, vous pouvez appeler
la méthode "_root()". Cette méthode commence par un soulignement pour
indiquer que cette fonction fait partie de l'implémentation, et non
d'une interface avec la fonctionnalité *Tk*.

Voici quelques exemples d'utilisation courante :

   import tkinter as tk

   class App(tk.Frame):
       def __init__(self, master=None):
           super().__init__(master)
           self.pack()

   # create the application
   myapp = App()

   #
   # here are method calls to the window manager class
   #
   myapp.master.title("My Do-Nothing Application")
   myapp.master.maxsize(1000, 400)

   # start the program
   myapp.mainloop()


Types de données des options *Tk*
---------------------------------

*anchor*
   Les valeurs acceptées sont des points cardinaux : ""n"", ""ne"",
   ""e"", ""se"", ""s"", ""sw"", ""w"", ""nw"" et ""center"".

*bitmap*
   Il y a huit bitmaps intégrés nommés : ""error"", ""gray25"",
   ""gray50"", ""hourglass"", ""info"", ""questhead"", ""question"",
   ""warning"".  Pour spécifier un nom de fichier bitmap X, indiquez
   le chemin complet du fichier, précédé de "@", comme dans
   ""@/usr/contrib/bitmap/gumby.bit"".

*boolean*
   Vous pouvez lui donner les entiers 0 ou 1 ou les chaînes de
   caractères ""yes"" ou ""no"".

*callback*
   N'importe quelle fonction Python qui ne prend pas d'argument.  Par
   exemple :

      def print_it():
          print("hi there")
      fred["command"] = print_it

*color*
   Les couleurs peuvent être données sous forme de noms de couleurs
   *Xorg* dans le fichier *rgb.txt*, ou sous forme de chaînes
   représentant les valeurs RVB en 4 bits : ""#RGB"", 8 bits :
   ""#RRVVBB"", 12 bits : ""#RRRVVVBBB"", ou 16 bits :
   ""#RRRRVVVVBBBB"", où R,V,B représente ici tout chiffre hexadécimal
   valide. Voir page 160 du livre d'Ousterhout pour plus de détails.

*cursor*
   Les noms de curseurs *Xorg* standard que l'on trouve dans
   "cursorfont.h" peuvent être utilisés, sans le préfixe "XC_". Par
   exemple pour obtenir un curseur en forme de main ("XC_hand2"),
   utilisez la chaîne ""hand2"". Vous pouvez également spécifier votre
   propre bitmap et fichier masque. Voir page 179 du livre
   d'Ousterhout.

*distance*
   Les distances à l'écran peuvent être spécifiées en pixels ou en
   distances absolues. Les pixels sont donnés sous forme de nombres et
   les distances absolues sous forme de chaînes de caractères, le
   dernier caractère indiquant les unités : "c" pour les centimètres,
   "i" pour les pouces (*inches* en anglais), "m" pour les
   millimètres, "p" pour les points d'impression. Par exemple, 3,5
   pouces est noté ""3.5i"".

*font*
   *Tk* utilise un format de nom de police sous forme de liste, tel
   que "{courier 10 bold}". Les tailles de polices avec des nombres
   positifs sont mesurées en points ; les tailles avec des nombres
   négatifs sont mesurées en pixels.

*geometry*
   Il s'agit d'une chaîne de caractères de la forme "largeurxhauteur",
   où la largeur et la hauteur sont mesurées en pixels pour la plupart
   des objets graphiques (en caractères pour les objets graphiques
   affichant du texte). Par exemple : "fred["geometry"] = "200x100"".

*justify*
   Les valeurs acceptées sont les chaînes de caractères : ""left"",
   ""center"", ""right"" et ""fill"".

*region*
   c'est une chaîne de caractères avec quatre éléments séparés par des
   espaces, chacun d'eux étant une distance valide (voir ci-dessus).
   Par exemple : ""2 3 4 5"", "" 3i 2i 4.5i 2i"" et ""3c 2c 4c
   10.43c"" sont toutes des régions valides.

*relief*
   Détermine le style de bordure d'un objet graphique. Les valeurs
   valides sont : ""raised"", ""sunken"", ""flat"", ""groove"" et
   ""ridge"".

*scrollcommand*
   C'est presque toujours la méthode "set()" d'un objet graphique de
   défilement, mais peut être n'importe quelle méthode d'objet
   graphique qui prend un seul argument.

*wrap*
   Doit être ""none"", ""char"" ou ""word"".


Liaisons et événements
----------------------

La méthode "bind" de la commande d'objet graphique vous permet de
surveiller certains événements et d'avoir un déclencheur de fonction
de rappel lorsque ce type d'événement se produit.  La forme de la
méthode de liaison est la suivante :

   def bind(self, sequence, func, add=''):

où :

*sequence*
   est une chaîne de caractères qui indique le type d'événement cible.
   (Voir la page du manuel de *bind(3tk)* et la page 201 du livre de
   John Ousterhout *Tcl and the Tk Toolkit (2nd edition)* pour plus de
   détails).

*func*
   est une fonction Python, prenant un argument, à invoquer lorsque
   l'événement se produit. Une instance d’évènement sera passée en
   argument. (Les fonctions déployées de cette façon sont communément
   appelées *callbacks* ou « fonctions de rappel » en français).

*add*
   est facultative, soit "''" ou "'+'".  L'envoi d'une chaîne de
   caractères vide indique que cette liaison doit remplacer toute
   autre liaison à laquelle cet événement est associé.  L'envoi de
   ""+"" signifie que cette fonction doit être ajoutée à la liste des
   fonctions liées à ce type d'événement.

Par exemple :

   def turn_red(self, event):
       event.widget["activeforeground"] = "red"

   self.button.bind("<Enter>", self.turn_red)

Remarquez comment on accède au champ *objet graphique* de l'événement
dans la fonction de rappel "turn_red()".  Ce champ contient l'objet
graphique qui a capturé l'événement *Xorg*. Le tableau suivant
répertorie les autres champs d'événements auxquels vous pouvez
accéder, et comment ils sont nommés dans *Tk*, ce qui peut être utile
lorsque vous vous référez aux pages de manuel *Tk*.

+------+-----------------------+------+-----------------------+
| Tk   | Champ *évènement* de  | Tk   | Champ *évènement* de  |
|      | *Tkinter*             |      | *Tkinter*             |
|======|=======================|======|=======================|
| %f   | focus                 | %A   | char                  |
+------+-----------------------+------+-----------------------+
| %h   | hauteur               | %E   | send_event            |
+------+-----------------------+------+-----------------------+
| %k   | keycode               | %K   | keysym                |
+------+-----------------------+------+-----------------------+
| %s   | state                 | %N   | keysym_num            |
+------+-----------------------+------+-----------------------+
| %t   | time                  | %T   | type                  |
+------+-----------------------+------+-----------------------+
| %w   | width                 | %W   | widget                |
+------+-----------------------+------+-----------------------+
| %x   | x                     | %X   | x_root                |
+------+-----------------------+------+-----------------------+
| %y   | y                     | %Y   | y_root                |
+------+-----------------------+------+-----------------------+


Le paramètre index
------------------

Un certain nombre d'objets graphiques nécessitent le passage de
paramètres « indicés ».  Ils sont utilisés pour pointer vers un
endroit spécifique dans un objet graphique de type *Texte*, ou vers
des caractères particuliers dans un objet graphique de type *Entrée*,
ou vers des éléments de menu particuliers dans un objet graphique de
type *Menu*.

Index des objets graphique de type *Entrée* ("index", "view index",
etc.)
   Les objets graphiques de type *Entrée* ont des options qui se
   réfèrent à la position des caractères dans le texte affiché.  Vous
   pouvez utiliser ces fonctions "tkinter" pour accéder à ces points
   spéciaux dans les widgets textes :

Index des objets graphiques texte
   La notation de l'index des objets graphiques de type *Texte* est
   très riche et mieux décrite dans les pages du manuel *Tk*.

Index menu ("menu.invoke()", "menu.entryconfig()", etc.)
   Certaines options et méthodes pour manipuler les menus nécessitent
   des éléments de spécifiques. Chaque fois qu'un index de menu est
   nécessaire pour une option ou un paramètre, vous pouvez utiliser :

   * un entier qui fait référence à la position numérique de l'entrée
     dans l'objet graphique, comptée à partir du haut, en commençant
     par 0 ;

   * la chaîne de caractères ""active"", qui fait référence à la
     position du menu qui se trouve actuellement sous le curseur ;

   * la chaîne de caractères ""last"" qui fait référence au dernier
     élément du menu ;

   * un entier précédé de "@", comme dans "@6", où l'entier est
     interprété comme une coordonnée y de pixels dans le système de
     coordonnées du menu ;

   * la chaîne de caractères ""none"", qui n'indique aucune entrée du
     menu, le plus souvent utilisée avec "menu.activate()" pour
     désactiver toutes les entrées, et enfin,

   * une chaîne de texte dont le motif correspond à l'étiquette de
     l'entrée de menu, telle qu'elle est balayée du haut vers le bas
     du menu.  Notez que ce type d'index est considéré après tous les
     autres, ce qui signifie que les correspondances pour les éléments
     de menu étiquetés "last", "active" ou "none" peuvent être
     interprétés comme les littéraux ci-dessus, plutôt.


Images
------

Des images de différents formats peuvent être créées à travers la
sous-classe correspondante de "tkinter.Image" :

* "BitmapImage" pour les images au format *XBM*.

* "PhotoImage" pour les images aux formats *PGM*, *PPM*, *GIF* et
  *PNG*. Ce dernier est géré à partir de *Tk* 8.6.

L'un ou l'autre type d'image est créé par l'option "file" ou "data"
(d'autres options sont également disponibles).

L'objet image peut alors être utilisé partout où un objet graphique
sait gérer une option "image" (par ex. étiquettes, boutons, menus).
Dans ces cas, *Tk* ne conserve pas de référence à l'image. Lorsque la
dernière référence Python à l'objet image est supprimée, les données
de l'image sont également supprimées, et *Tk* affiche une boite vide à
l'endroit où l'image était utilisée.

Voir aussi:

  Le paquet Pillow ajoute la prise en charge de formats tels que
  *BMP*, *JPEG*, *TIFF* et *WebP*, entre autres.


Gestionnaires de fichiers
=========================

*Tk* vous permet d'enregistrer et de *désenregistrer* une fonction de
rappel qui est appelée depuis la boucle principale de *Tk* lorsque des
entrées-sorties sont possibles sur un descripteur de fichier. Un seul
gestionnaire peut être enregistré par descripteur de fichier. Exemple
de code :

   import tkinter
   widget = tkinter.Tk()
   mask = tkinter.READABLE | tkinter.WRITABLE
   widget.tk.createfilehandler(file, mask, callback)
   ...
   widget.tk.deletefilehandler(file)

Cette fonction n'est pas disponible sous Windows.

Dans la mesure où vous ne savez pas combien d'octets sont disponibles
en lecture, il ne faut pas utiliser les méthodes "BufferedIOBase" ou
"TextIOBase" "read()" ou "readline()", car elles requièrent d'indiquer
le nombre de *bytes* à lire. Pour les connecteurs, les méthodes
"recv()" ou "recvfrom()" fonctionnent bien ; pour les autres fichiers,
utilisez des lectures brutes ou "os.read(file.fileno(),
maxbytecount)".

Widget.tk.createfilehandler(file, mask, func)

   Enregistre la fonction de rappel du gestionnaire de fichiers
   *func*. L'argument "file" peut être soit un objet avec une méthode
   "fileno()" (comme un objet fichier ou connecteur), soit un
   descripteur de fichier de type entier. L'argument "mask" est une
   combinaison *OU* de l'une des trois constantes ci-dessous. La
   fonction de rappel s'utilise comme suit :

      callback(file, mask)

Widget.tk.deletefilehandler(file)

   Désenregistre un gestionnaire de fichiers.

_tkinter.READABLE
_tkinter.WRITABLE
_tkinter.EXCEPTION

   Constantes utilisées dans les arguments "mask".
