# Programmation Tcl/Vos premiers pas en Tk

* Introduction
* Vos premiers pas en Tcl
* Vos premiers pas en Tk
* Notation particulière de Tcl
* Introduction
* Vos premiers pas en Tcl
* Vos premiers pas en Tk
* Notation particulière de Tcl
[ Modifier le sommaire ]
Tk - la bibliothèque graphique de Tcl
Auteur de la version initiale : Arnaud LAPREVOTE

## Tk

Tk est le compagnon graphique de tcl. Il contient en particulier les widgets suivants :
* bouton
* label
* entrée de texte
* menu
* photo
* fenêtre de sélection de fichiers, de couleurs, ...
Ce qui n'est pas directement dans Tk peut être trouvé directement dans des extensions supplémentaires, telles que Blt (widget de tracé de courbes), les Bwidgets, HTML. On commence aussi à trouver des méga-widgets directement écrits en tk (combobox, notepad, arbre).

## Premier pas en Tk

Pour créer une interface, on :
* définit chaque widget et ses attributs,
* dispose les widgets dans la fenêtre (pack ou grid),
* définit les commandes associées aux actions.
Tk gère intégralement la boucle de suivi des événements. On ne s'en préoccupe pas.
Ou encore plus simplement :

## Création d'un widget

Le nom d'un widget est toujours : .xxxx.yyyy.zzzz Dans ce cas on crée le widget zzzz qui se trouve dans le widget yyyy qui se trouve dans le widget xxxx. xxxx pourrait parfaitement être une nouvelle fenêtre ou un widget.
Lors de la création d'un widget, des paramètres sont initialisés. Par exemple, dans :
Lors de la création du label .exemple_label, on vient initialiser son paramètre -text à la valeur "Je dis juste bonjour !".
Il est constamment possible de changer un paramètre d'un widget avec la commande configure.
Les valeurs initialisables pour un widget varient d'un widget à l'autre. Cependant il y a un certain nombre de paramètres qui sont systématiquement initialisables pour les widgets. La liste de ces options par défaut est disponible :

## Modification des paramètres d'un widget existant

Reprenons notre petit exemple : 2 solutions existent ici. Si vous tapez directement dans wish, alors tapez :
Si vous souhaitez plutôt utiliser un programme dans un fichier, alors tapez :
Comme vous pouvez le constater configure a permis de changer la valeur d'un paramètre d'un widget. Si vous souhaitez connaître la liste de tous les paramètres d'un widget, il vous suffit de taper : .nom_du_widget configure
Si vous souhaitez connaître la valeur d'un paramètre donné, vous pouvez utiliser la commande cget :
Remarque Dis tonton Arnaud, tu nous as bassiné avec le fait qu'une ligne tcl consistait toujours en :
Or je constate ici que l'on a :
Il y a tromperie sur la marchandise, remboursez !!!!!
Réponse de tonton Arnaud Que nenni !!! Lorsque l'on crée un widget, une commande portant le nom du widget est créée et configure est un argument de cette commande comme un autre.

## Mort d'un widget

Tout à une fin. On peut détruire un widget avec la commande :

## Les options standard

| Option | Signification |
| -background | couleur de fond du widget. Ou un mot (red, ...) ou une chaîne définissant rgb avec des valeurs hexa (noir : "#00000", blanc : "#ffffff", rouge : "#ff0000") |
| -activebackground | couleur de fond lorsque le curseur est sur le widget |
| -foreground | la couleur du texte quand le curseur n'est pas sur le widget |
| -activeforeground | la couleur du texte quand le curseur est sur le widget |
| -text | le texte du bouton |
| -textvariable | une variable dont le contenu s'affichera dans le widget |
| -image | une image précédemment chargée |
| -bitmap | un bitmap qui s'affichera dans le widget |
| -padx | espace de garde à droite et à gauche du widget |
| -pady | espace de garde au-dessus et en dessous du widget |
| -anchor | l'élément interne au widget (texte ou graphique) sera collé à la partie haute (nord => n) basse (sud => s) droite (est => e) ou gauche (ouest => w) ou au centre (center). |
| -width | largeur en caractères du widget |
| -height | hauteur en caractères du widget |
| -justify | right, left, center. Si le widget contient un texte sur plusieurs lignes, la justification choisie sera appliquée. |

## À vous Cognaq-Jay

Créez une application contenant un label, un bouton "Quitter", un bouton "Changer". Au départ, sur le label on lit :
Quand on clique sur le bouton "Changer", le label devient "Vous venez de cliquer sur le bouton Changer", si on re-clique, on revient à "Je dis juste bonjour" et ainsi de suite. Si on clique sur le bouton "Quitter", l'application se termine.
Le bouton "Quitter" a un fond rouge, quand le curseur passe dessus se fond devient rose (#ff8080).
Dans une seconde version, vous garderez cette même application, ajouterez les boutons flat, groove, sunken, ridge. Lors de l'appui sur ces boutons, la propriété -relief du label changera et prendra la valeur flat, groove, sunken, ridge.
Je paye personnellement un coca-cola (virtuel) à celui qui réussira à faire ce dernier exercice avec une boucle sur la liste [list flat groove sunken ridge].

## Placement des widgets

3 modes de placement existent :
* pack
* grid
* place

### Le packer :

Les widgets sont placés en colonnes ou lignes. Les colonnes ou les lignes sont créées les unes après les autres. Pour obtenir des placements par groupe de widget on utilise des frames qui vont contenir des widgets en horizontal ou en vertical.

### Le grider :

Les widgets sont placés sur une grille (comme dans un tableau HTML pour ceux qui connaissent). La taille des colonnes est calculée automatiquement.
Le placer permet de placer les widgets en donnant directement leurs coordonnées. Je ne l'ai jamais utilisé.

## Le packer dans le détail - les options de pack

On peut utiliser la commande pack avec les options :
On choisit l'orientation (horizontal ou vertical) ainsi que l'endroit (de gauche à droite, de droite à gauche, de haut en bas, de bas en haut) dans lequel les widget sont placés.
Défini si les widgets packés doivent remplir complètement l'espace disponible ou non en horizontal (x) ou en vertical (y). Par défaut l'option est none.
Lors du redimensionnement de la fenêtre, les widgets packés avec cette commande suivront l'expansion de la taille de la fenêtre.
Le nombre de pixels à droite et à gauche du widget courant sera ajouté en horizontal (padx) ou en vertical (pady) autour du widget.
Le nombre de pixels nécessaire est ajouté à l'intérieur du widget à droite et à gauche (-ipady) ou au dessus et en dessous (-ipadx).
On peut faire "oublier" un widget au packer en utilisant l'option forget. Enfin, pour pouvoir bouger un widget dans tous les sens, il faut qu'il soit défini avant un autre.

## Exercice

Votre mission si vous l'acceptez consiste à obtenir successivement les looks suivant avec le packer et 2 labels :
img/tk_ex1.gif
img/tk_ex2.gif
img/tk_ex3.gif
img/tk_ex4.gif
img/tk_ex5.gif
img/tk_ex6.gif
Pour ce faire, vous allez lancer wish et vous allez créer les 2 widgets suivants en ligne de commande :
Afin que votre cerveau ne s’autodétruise pas au bout de 15 minutes, voici quelques conseils :
* les 5 premiers écrans s'obtiennent uniquement en faisant bouger l'un ou l'autre des widgets avec la commande pack,
* le dernier écran s'obtient en configurant l'option -anchor du label 2.
Bien sûr, si votre mission échoue, nous nierons tout lien avec vous et peut-être même vous livrerons au côté obscure de la force dans la salle d'à côté.

## Les frames

Les frames sont comme le sel en cuisine, il en faut des pincées, cela reste invisible, mais sans cela le plat est infect.
Imaginons que nous souhaitions avoir l'interface suivante :
Le code correspondant est le suivant :
Comme vous le voyez, les frames permettent de regrouper des ensembles de widget par ligne horizontale et verticale. Grâce à .frame1.frame2 on a groupé les widgets .frame1.frame2.lab1 et .frame1.frame2.ent1 horizontalement, puis dans .frame1 on a ajouté .ent2 et on les a empilés verticalement. Enfin on a placé .but1 et .frame2 cote à cote.
En jouant judicieusement sur les options -expand et -fill, on arrive à obtenir un comportement sophistiqué de l'application lors des redimensionnements.

## Le gridder

Dans certains cas, le packer est vraiment assez inadapté et oblige à utiliser un nombre incroyable de frame. Des extensions à tcl sont alors apparus qui intégraient des algorithmes de placement basés sur une grille de cases.
Une grille est définie et l'on choisi le ou les cases sur lesquels un widget (ou un ensemble de widget dans une frame) vont être placés. Le gridder est plus verbeux que le packer à l'écriture parce qu'il faut une ligne pour chaque widget. Par contre, il est trivial de générer le code automatiquement.
Les options de grid (au placement ou lors d'un configure) sont les suivantes :
Identification de la case où le widget sera placé
Le widget est placé sur une ou plusieurs colonnes, sur une ou plusieurs lignes.
Strictement identique à ces valeurs dans le packer.
La manière dont le widget est "collé" aux bords. Si l'on souhaite qu'en cas de redimensionnement le widget voit sa taille augmenté, on utilise -sticky ew ou ns ou ewns.
Il est possible de fixer les attributs d'une colonne ou d'une ligne grâce à
Assez étrangement, les numéros de lignes et de colonnes commencent à 1 et non pas à 0.

## Événements disponibles

On peut associer à chaque événement d'un widget des actions. Cela se fait avec la commande bind.
La syntaxe de bind est :
La syntaxe des événements est un peu particulière. En première approximation, c'est : <type_d_evenement-evenement>
type_d_evenement peut être :
Type d'événement :
| ButtonPress <=> Button | Expose | Map |
| ButtonRelease | FocusIn | Motion |
| Circulate | FocusOut | Property |
| Colormap | Gravity | Reparent |
| Configure | KeyPress <=> Key | Unmap |
| Destroy | KeyRelease | Visibility |
| Enter | Leave | Activate |
| Deactivate | | |
Button et Key sont d'utilisation très courantes. Il m'est arrivé d'utiliser aussi Enter et Leave.
evenement peut être :
* 1 : le bouton 1 de la souris,
* 2 : le bouton 2 de la souris,
* 3 : le bouton 3 de la souris,
* le nom d'une touche : abcdefghij...left, right, up, down, Control_L, Control_R, Insert, Delete, ....
En ce qui concerne les touches, attention, il peut y avoir des différences de dénomination entre OS. Utilisez l'exemple plus haut pour connaître le code.
Enfin devant tout cela (type_d_evenement-evenement), il peut y avoir un modificateur. Par exemple, on peut demander à ce que la touche Ctrl soit appuyé, Alt ou Shift, ou vouloir un double clique, ou que le bouton 1 de la souris soit activé. Les autres événements me paraissent moins utiles. La liste est la suivante :
Modificateur :
| Control | Mod2 <=> M2 |
| Shift | Mod3 <=> M3 |
| Lock | Mod4 <=> M4 |
| Button1 <=> B1 | Mod5 <=> M5 |
| Button2 <=> B2 | Meta <=> M |
| Button3 <=> B3 | Alt |
| Button4 <=> B4 | Double |
| Button5 <=> B5 | Triple |
| Mod1 <=> M1 | |
Enfin, dans le script associé au binding, on peut récupérer diverses informations sur l'événement grâce à des chaines du type %x :
* %b : numéro du bouton pour les événements ButtonPress et ButtonRelease,
* %k : le code de touche pour l'événement KeyPress ou KeyRelease,
* %K : le code de touche sous forme de chaîne pour les 2 événements décrits plus haut,
* %X : coordonnée horizontal de l'événement dans le widget courant (pour ButtonPress, ButtonRelease, KeyPress, KeyRelease),
* %Y : coordonnée vertical de l'événement dans le widget courant (pour ButtonPress, ButtonRelease, KeyPress, KeyRelease).
La page de man de bind donne toutes les indications.

## Liste des widgets disponibles

* button, canvas, checkbutton
* entry, frame, label
* listbox, menu, menubutton
* message, radiobutton, scale
* scrollbar, text, toplevel
* tk_getOpenFile, tk_getSaveFile, tk_chooseColor
* tk_dialog, tk_optionMenu, ...

## Les boutons

Pour avoir toutes les options, n'hésitez pas à vous reporter à la page de man. Les options me semblant crucialement utiles sont :
Liste des options pour le widget button :
| Option | Signification |
| -background | couleur de fond du bouton |
| -activebackground | couleur de fond lorsque le curseur est sur le bouton |
| -text | le texte du bouton |
| -image | une image précédemment chargée s'affiche dans le bouton |
| -command | commande(s) exécutées lors de l'appui sur le bouton |

## Les labels

Les labels sont les widgets contenant un texte non modifiable interactivement. Évidemment le texte est modifiable via programmation.
Liste des options pour le widget :
| Option | Signification |
| -text | le texte du bouton |
| -textvariable | une variable dont le contenu s'affichera dans le widget |
Le widget "message" est un label multiligne.

## Les entrées de texte

Liste des options pour le widget :
| Option | Signification |
| -textvariable | une variable dont le contenu s'affichera dans le widget |
| -width | le nombre de caractères par défaut du widget en largeur |

## Les cases à cocher

Une case à cocher permet de savoir si une option est ou non sélectionnée. Cela s'utilise de la manière suivante :
Les options -onvalue -offvalue permettent de forcer une valeur pour la variable selon que la case est ou non cochée.

## Les boutons radio

Les boutons radio sont des cases à cocher dont une seule peut-être active à la fois.

## Les menus

Pour créer un menu avec des listes déroulantes, on utilise simplement une frame (-relief raised), avec à l'intérieur des widget "menubutton". À chaque menubutton on associe un widget fils menu. Ce menu est composé d'entrée de type command (avec option -label, -command), de type radiobutton (option -label, -command, -variable et -value) de type checkbutton (-label -command -variable -onvalue -offvalue) ou de type cascade (option -label -cascade) enchaînant vers un autre menu.
L'option -accelerator permet d'associer une touche d'accélération pour activer l'entrée de menu correspondante.
Il existe aussi des menus d'options : tk_optionMenu
Ainsi que des menus pop-up.

## Les images

Comme nous l'avons vu précédemment, il est possible d'associer un bitmap (noir et blanc) ou une photo à un widget (bouton, label).
La procédure est simple :
* on crée l'image de type photo (couleur) ou bitmap (pixel noir ou blanc),
* on associe la photo créée au widget.
Prenons le cas de la photo :
Il ne reste plus qu'à associer la photo à un label
Il est évidemment possible de changer de photo par exemple :
Vous pouvez vous reporter au man de la commande image pour voir les commandes disponibles.

## Interface graphique contre grosse fatigue

Nous allons refaire un joyeux exercice, mais cette fois-ci vous allez pouvoir utiliser un builder graphique d'application. Il en existe 3 pour tk.
Le plus ancien est SpecTcl (http://wuarchive.wustl.edu/languages/tcl/SpecTcl/). À sa création par les laboratoires de Sun, s'était un produit commercial. Ils l'ont rapidement mis dans le domaine public et abandonné le développement. Le résultat est un excellent produit stable et facile à utiliser mais qui n'évolue plus (et c'est bien dommage). C'est ce que j'ai utilisé au départ.
L'inconvénient de specTcl est qu'il faut très régulièrement retoucher le code généré. À partir du moment où on touche le code généré par un builder, on ne peut plus du tout continuer à utiliser l'interface graphique et cela perd de son intérêt.
Nouveau : le développement de specTcl a repris. Il se trouve maintenant sur sourceforge.net.
tkBuilder que l'on peut trouver sur http://scp.on.ca/sawpit, est un outil moins graphique que specTcl, mais très pratique pour le programmeur. C'est un excellent support graphique à la programmation. Grâce à la possibilité de rajouter du code manuel un peu partout, il est très facile de ne pas avoir à modifier le code.
Enfin, Visual Tcl est un logiciel constamment prometteur, certainement le plus ambitieux des 3 (le plus proche de VB ?). On le trouve sur :
Malheureusement, à chaque fois que je l'utilise, il me claque entre les mains. Je vous propose malgré tout de voir si la dernière version (1.6alpha) tient ou non la route.
Je vous propose de charger l'un des 3 et de faire les exercices suivants avec une interface de construction. À charge pour vous de lire le manuel.

## Vous n'allez pas rigoler

Votre but est de créer l'application suivante :
On donne en haut le nom d'un fichier gif ou jpeg et quand on appuie sur le bouton Ouvrir cette image est affichée dans le label central. Bien sûr, on peut répéter l'opération d'ouverture à chaque fois.
On peut choisir le nom de fichier ou en le tapant dans l'entrée de texte du haut ou en cliquant sur le bouton à côté de l'entrée de texte qui permet d'appeler le widget tk_getOpenFile. On utilise cette fonction de la manière suivante :
tk_getOpenFile est un widget standard de tcl ayant un manuel.
J'ai utilisé tkBuilder pour créer cette application : img/tkbuilder.gif mais n'hésitez pas à utiliser un autre builder.

## Bibliographie

"Graphical Applications with Tcl&Tk" de Eric F. Johnson, M&T Books, ISBN 1-55851-471-6 : le livre avec lequel j'ai appris le tcl/tk. Pas complet, mais très pédagogique et agréable à lire.