Aller au contenu

Programmation Tcl/Version imprimable

Un livre de Wikilivres.

Ceci est la version imprimable de Programmation Tcl.
  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.


Programmation Tcl

Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_Tcl

Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la Licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans Texte de dernière page de couverture. Une copie de cette licence est incluse dans l'annexe nommée « Licence de documentation libre GNU ».

Introduction

Il était une fois un programmeur qui avait passé des milliers d'heures à programmer en C, particulièrement des applications de calculs scientifiques (traitement vidéo). Avec les années, il avait acquis une grande facilité dans l'écriture des programmes C, et en même temps une certaine impatience.

Autant le C le satisfaisait pour le calcul scientifique, autant dès qu'il fallait traiter des fichiers ou des chaînes de caractères, il trouvait que c'était fastidieux et surtout générateur d'erreurs de manière inacceptable.

Comme tout le monde, il avait entendu parler des langages de script genre Perl, Tcl/tk, et autres (python). À l'occasion de l'installation de son PC sous Linux, il prit le temps de commencer à programmer en Tcl.

Et ce fut un choc. Plus de pointeurs, plus de gestion-mémoire, de l'idée au programme en un minimum de lignes. Tout cela trivial à apprendre et gratuit, qui peuvent être redistribué, fonctionnant sous Unix (tous les Unix) et sous Windows. Capable de faire des interfaces graphiques de manière très facile, de la programmation CGI en plaisantant. Facile à expliquer. Génial.

Ce programmeur, c'est moi. Et j'ai très envie de vous faire partager cette passion pour mon langage préféré. Tcl (Tool Command Language) est un langage de programmation initialement conçu en 1989 pour le système d'exploitation UNIX par le Dr John Ousterhout et son équipe à l'Université de Berkeley.

Tcl est un langage facile à apprendre, surtout pour un habitué du Langage C. Il permet de développer très rapidement des logiciels de petite taille.

La promotion de Tcl est maintenant assurée par la société ActiveState. Cependant, Tcl est un logiciel libre.

Tcl est surtout connu pour son extension graphique TK. Tcl/Tk permet de concevoir des applications fenêtrées indépendamment de la plateforme (Xwindows / Mac / Windows) à la manière du Langage Java.

Tcl étant un langage interprété, le développement d'un programme est rapide et peut s'effectuer par couche successive. Cette méthode de travail correspond à un cycle de développement en cascade.

Tcl facilite l'accès aux fonctions de son interpréteur, ce qui contribue à la puissance du langage et le place à mi-chemin entre un langage fonctionnel (LISP) et un langage interprété ordinaire (BASIC).


Vos premiers pas en Tcl

Comment j'ai divisé par 10 le nombre de lignes de code de mes programmes !


Auteur de la version initiale : Arnaud LAPREVOTE

Pourquoi un cours sur le tcl/tk ?

[modifier | modifier le wikicode]

Je me présente, je m'appelle Henri. Euh non, Arnaud LAPREVOTE.

Il était une fois un programmeur qui avait passé des milliers d'heures à programmer en C, particulièrement des applications de calculs scientifiques (traitement vidéo). Avec les années, il avait acquis une grande facilité dans l'écriture des programmes C, et en même temps une certaine impatience.

Autant le C le satisfaisait pour le calcul scientifique, autant dès qu'il fallait traiter des fichiers ou des chaînes de caractères, il trouvait que c'était fastidieux et surtout générateur d'erreurs de manière inacceptable.

Comme tout le monde, il avait entendu parler des langages de script genre Perl, tcl/tk, et autres (python). À l'occasion de l'installation de son PC sous Linux, il prit le temps de commencer à programmer en tcl.

Et ce fut un choc. Plus de pointeurs, plus de gestion-mémoire, de l'idée au programme en un minimum de lignes. Tout cela trivial à apprendre, gratuit, qui peut être redistribué, fonctionnant sous Unix (tous les Unix) et sous Windows. Capable de faire des interfaces graphiques de manière très facile, de la programmation CGI en plaisantant. Facile à expliquer. Génial.

Ce programmeur, c'est moi. Et j'ai très envie de vous faire partager cette passion pour mon langage préféré.

Et la concurrence ?

[modifier | modifier le wikicode]

Il y a de très nombreux concurrents au Tcl/Tk :

  • Perl,
  • Python,
  • Scheme (LISP),
  • dans une certaine mesure PHP,
  • Visual Basic,
  • Java.

Tous ces langages ont des avantages et des inconvénients. Mes critères de choix principaux sont :

  • open source (source disponible, gratuite et qui peut être redistribuée),
  • lisibilité (facilité à maintenir et déboguer),
  • multiplateforme (Unix, Linux, Windows),
  • grand nombre d'extensions.

Un dernier avantage est que le Tcl n'a pas une ambition infinie. Le Tcl ne veut pas TOUT faire. C'est juste un langage de "colle" pour faire tenir un ensemble d'application ensemble. Si vous voulez faire des choses très grosses ou très complexes ou très rapides, vous êtes priés de vous tourner vers le C, le C++ ou le java. À cette lumière, il ne reste que le python et le tcl, à la limite le Perl. La syntaxe du Perl me rend fou, donc je l'exclus. Je m'intéresse au Python.

Autres avantages

[modifier | modifier le wikicode]

Les points suivants méritent d'être soulignés :

  • fonctions réseau (socket) intégrées très élégamment au langage. Un serveur Web se fait en claquant des doigts,
  • facilité d'intégration du tcl dans une application existante,
  • très grande robustesse du langage,
  • facilité d'intégration de fonctions C dans le Tcl,
  • la compatibilité ascendante n'est pas une théorie, mais une réalité,
  • très forte cohérence due à une origine universitaire.

Inconvénients

[modifier | modifier le wikicode]

Tout n'est pas parfait en tcl.

  • le langage n'est pas en GPL => moins grande dynamique du langage que le python ou le Perl,
  • il n'est pas possible de définir de vraies structures en tcl (au sens C du terme). Cela peut nuire à la lisibilité des programmes et limite la taille de ce que l'on peut programmer. On peut se tourner vers les extensions objet du tcl pour avoir ces fonctions,
  • il manque un IDE libre avec un débogueur intégré pour faciliter la prise en main par les débutants.

Tcl fut créé en 1990 par John OUSTERHOUT à l'Université de Berkeley. C'est un langage de "collage" pour attacher ensemble plusieurs applications. C'est un langage interprété, mais compilé à la volée depuis la version 8.0. La version actuelle (en janvier 2016) est Tcl 8.6.4.

Après Berkeley, John Ousterhout est passé chez Sun, puis il a créé sa propre société Scriptix qui est devenue ensuite Ajuba Solutions et a été rachetée récemment. Des centaines de programmes et de société utilisent le tcl, mais souvent de manière souterraine. Tcl est donc un langage discret.

Ressources utiles

[modifier | modifier le wikicode]
  • "Practical Programming in Tcl/Tk" ISBN: 0-13-038560-3 par Brent Welch <welch@acm.org>, Ken Jones, et Jeff Hobbs ; en partie en ligne sur "http://www.beedub.com/book/" : la bible incontournable.
  • "Graphical Appications with Tcl&Tk" ISBN : 1-55851-471-6 par Eric F.Johnson : un très bon livre pour commencer.
  • "TCL/TK Apprentissage et référence" ISBN : 2-7117-8679-X par Bernard Desgraupes. Je l'ai seulement feuilleté, je suis très vexé de ne pas l'avoir écrit.

Vos premiers pas en Tcl

[modifier | modifier le wikicode]

Le premier pas

[modifier | modifier le wikicode]

Pour démarrer un interpréteur tcl, tapez :

tclsh

Alternativement, sous Windows, allez dans le menu "démarrer", déroulez-le sous menu tcl puis cliquez sur tclsh ou wish (plutôt wish).

Vous obtenez alors un invite de commande en %. Taper les lignes suivantes :

% set mytext "Wikilivre pour apprendre Tcl"
Wikilivre pour apprendre Tcl
% puts $mytext
Wikilivre pour apprendre Tcl
% set i 0
0
% puts $i
0
% incr i
1
% string toupper $mytext
WIKILIVRE POUR APPRENDRE TCL

Les points clés de cet exemple sont :

COMMANDES

set nom_de_variable "valeur"
puts "chaîne de caractères"
incr nom_de_variable_numérique [incrément]

Le deuxième pas

[modifier | modifier le wikicode]
% for { set i 0 } { $i < 3 } { incr i } { 
    puts $i 
    puts "$i" 
    puts [string toupper "Free&ALter Soft : $i"] 
} 
0 
0 
FREE&ALTER SOFT : 0 
1 
1 
FREE&ALTER SOFT : 1 
2 
2 
FREE&ALTER SOFT : 2 
% #this is a remarque 
% set i 0; set j 1; #that also 
1

COMMANDES

 for { initialisation } { end condition  } { incrementation } {
      code running at each loop
}

SYNTAXE

command [argument1] [argunment2]
first_command; second_command
"$substitution" "\$caracter printed as is"
[immediate execution] {execute as late as possible}
#remarque

La syntaxe du tcl

[modifier | modifier le wikicode]

Un des problèmes du tcl est sa simplicité. Concernant sa syntaxe, il n'y a que 2 choses à savoir.

Le premier mot de la commande est TOUJOURS la commande.

commande argument1 argument2 argument3 ...

Donc en tcl, pour initialiser une variable on écrit :

set toto "xxxx"

ET PAS

toto = "xxxx"

Les arguments de la commande sont séparés les uns des autres par des espaces. D'où obligatoirement :

for {set i 0} {$i < 4} {incr i} {
}

Et non pas

for{set i 0}{$i < 4}{incr i}{
}

La clé du tcl : la substitution

[modifier | modifier le wikicode]

Il y a une finesse en tcl. Les substitutions. L'interprétation d'une ligne se fait en 2 temps :

  • substitution de tout ce qui est substituable (variable, code entre crochets []),
  • exécution de la commande.

Donc :

% set toto "TOTO"
TOTO
% set tata "$toto"
TOTO
% set titi "[expr 1 + 2]"
 3
% set tutu [string trim [string tolower \
"Phrase avec des espaces : $toto va "]]
phrase avec des espaces : toto va
% set tutu "[string trim [string tolower \
"Phrase avec des espaces : $toto va "]]"
phrase avec des espaces : toto va
% puts "--$tutu--"
--phrase avec des espaces : toto va--

L'exécution d'un code entre [] et la substitution dans l'expression appelante du contenu de [] par son résultat. C'est ce que l'on appelle de la programmation fonctionnelle. Le LISP est l'archétype de ces langages. Le tcl permet de mélanger élégamment programmation fonctionnelle et procédurale. Dans certains cas (les traitements sur des chaînes de caractères) la programmation fonctionnelle est TRÈS (très, vraiment très, j'insiste encore ? non) pratique.

Pour empêcher la substitution, on utilise les accolades { } :

% set toto {TOTO}
TOTO
% set tata {$toto}
$toto
% set titi {[expr 1 + 2]}
set titi {[expr 1 + 2]}
% set tutu [string trim \
[string tolower "Phrase avec des espaces : $toto va "]]
phrase avec des espaces au bout : toto va
% set tutu {[string trim \
[string tolower "Phrase avec des espaces : $toto va "]]}
[string trim [string tolower "Phrase avec des espaces au bout : $toto va "]]
% puts {--$tutu--}
--$tutu--

Pour affiner ces notions, on peut ajouter que le caractère\ (barre oblique inverse), force l'interprétation du caractère suivant comme étant un simple caractère et rien d'autre :

set toto {TOTO}
TOTO
% set tata "\$toto"
$toto
% set tata Ce\ qui\ suit\ est\ une\ seule\ chaîne
Ce qui suit est une seule chaîne
% puts "\[ pas d'interprétation hâtive ]"
[ pas d'interprétation hâtive ]

Et que l'on peut utiliser les accolades autour d'un nom de variable pour lever l'ambiguïté :

% set var1 "CONTENU ORIGINE"
CONTENU ORIGINE
% set var12 "AUTRE CONTENU"
AUTRE CONTENU
% puts "${var1}2"
CONTENU ORIGINE2
% puts "${var12} == $var12"
AUTRE CONTENU == AUTRE CONTENU

Si vous avez complètement compris ce qui précédait, alors une friandise (sinon c'est le moment de piquer un roupillon, de papoter avec les voisins, de se taper un carton, de relever ses textos, et de noter qu'il faut relire le paragraphe qui suit dans 15 jours).

L'instruction eval permet de forcer une évaluation supplémentaire, et de temps en temps c'est fantastique (le préprocesseur de tcl est tcl contrairement au C):

#!/usr/bin/tclsh
set var1 "Un"
set var2 "Deux"
set var3 "Trois"
set var4 "Quatre"
set var5 "Cinq"
set var6 "Six"
for { set i 1 } { $i < 7 } { incr i } {
	set command "puts \$var$i"
	eval $command
}

Un
Deux
Trois
Quatre
Cinq
Six

Types de données

[modifier | modifier le wikicode]

Les chaînes de caractères et les scalaires

[modifier | modifier le wikicode]
* scalaire : tcl 7.6 - chaînes seulement => tcl 8.0 - chaînes et valeurs.

Tout est chaîne en tcl : c'est la clé de la facilité d'interaction : toute fonction peut envoyer des résultats à n'importe quelle autre.

%set str1 "0123456789"
%string length $str1
10
%string index $str1 5
5
%string range $str1 0 4
01234
% string compare $str1 "101112131415"
-1
%proc frame_string { str } {
         format "###->%<-###" $str
}
%frame_string "Arnaud LAPREVOTE"
###->Arnaud LAPREVOTE<-###
%frame_string "Arnaud LAPREVOTE "
###->Arnaud LAPREVOTE <-###
%frame_string [string trimright "Arnaud LAPREVOTE "]
###->Arnaud LAPREVOTE<-###

COMMANDES

string length $a_string
string index $a_string index ; #(0 is first)
string range $a_string first_index last_index

SYNTAXE

proc function_name { list of args } {
           instructions
           return 5
}

On utilise beaucoup les listes en tcl.

set mylist [list "toto et tata" 1 [list 1 2 3] stop] 
{toto et tata} 1 {1 2 3} stop
% puts [llength $mylist] 
4 
% lindex $mylist 0 
toto et tata
% lrange $mylist 0 1 
{toto et tata} 1
% lsort $mylist 
1 {1 2 3} stop {toto et tata}
% set mylist [linsert $mylist 1 coucou] 
{toto et tata} coucou 1 {1 2 3} stop
% lappend mylist "why not" 
{toto et tata} coucou 1 {1 2 3} stop {why not}
% puts $mylist 
{toto et tata} coucou 1 {1 2 3} stop {why not} 
% split "1,2,3,4,5,6" ,
1 2 3 4 5 6


COMMANDES Description
list first_elt second_elt ... Création d'une liste. Renvoi une liste.
llength $a_list Renvoi le nombre d'éléments de la liste
lindex $a_list elt_nber Renvoi l'élément n° elt_nber de la liste. elt_nber peut être end (dernier élément).
lrange $a_list start_nber end_nber Renvoi une liste composée des éléments commençants ) start_nber et finissant à end_nber
lsort $a_liste Ordonnancement de la liste. De nombreuses options permettent de classer en ordre croissant/ décroissant, en utilisant un élément d'une sous-liste comme clé, en ordre numérique, ... Renvoi une liste
linsert $a_list nber elt_to_insert Insère un élément dans une liste à l'endroit indiqué. Renvoi une liste.
lappend a_list elt_to_append_at_the_end Ajoute les éléments suivant à la fin de la liste. ATTENTION LAPPEND NE RENVOIE PAS DE LISTE. IL MET AU BOUT DE LA LISTE NOMMÉE a_list LES ÉLÉMENTS.
split "chaîne de caractère" [caractère] Transforme une chaîne de caractères en une liste. Le séparateur est le caractère fourni en second paramètre.

Chaînes et expressions régulières

[modifier | modifier le wikicode]

Les expressions régulières sont une fonction clé des langages de scripts (ksh, Perl, awk, tcl, python, ...). Elles ne sont pas du tout naturelles, mais une fois comprises, elles sont un outil très puissant. Vous devez les essayer !

Une seule méthode pour survivre en United States of Regular Expressions : essayez d'abord, puis programmez. Même une ceinture noire 4ème dan de tcl fait comme cela.

COMMANDES

regexp {sf(first expr)(second expr)} $string \
 matching_string first_matching_str  second_matching_string
  • . n'importe quel caractère,
  • * le caractère précédent 0 ou plusieurs fois,
  • + preceding character at least once or more,
  • [a-zA-Z] character list or range,
  • [^a-z] not these characters,
  • \$ exactly the character $ forget rules,
  • ^ first character of the string,
  • $ last character of the string,
  • ? matches preceding character once or nothing,
  • pattern1|pattern2 matches pattern1 or pattern2.
%set reg "This is a string = 12"
This is a string = 12
% regexp {([a-zA-Z]*) *= *([0-9]*)} $reg string var val 
1 
% puts $string 
string = 12 
% puts $var 
string 
% puts $val 
12
% set reg "string = 12; # forget the rest" 
string = 12; # forget the rest 
% regsub {([a-zA-Z]*) *= *([0-9]*)} $reg \
  {and \2 = \1}  string 
1 
%puts $string 
and 12 = string; # forget the rest 

Les tableaux associatifs

[modifier | modifier le wikicode]
%set good(name) "Free&ALter Soft"
%set good(first_name) "Laprevote"
%set good(sur_name) "Arnaud"

%proc puts_array { current_array } {
      upvar $current_array bad
      foreach name [array names bad] {
            puts "$name = $bad($name)"
      }
}

%puts_array good
first_name = Laprevote 
name = Free&ALter Soft 
sur_name = Arnaud 


COMMANDES EXPLICATION
set toto(tata) "string" Initialisation à string de l'entrée tata dans le tableau toto
array exists name Renvoi 1 si le tableau name existe
array names name Renvoi la liste des entrées du tableau
array get name Liste de paires clé valeur du tableau name
array set name list Initialise le tableau name en utilisant une liste à la syntaxe identique au résultat de array get name
parray name Affichage du tableau name
upvar $name name_to_use Passage d'un tableau par pointeur à une fonction
proc this_proc { sent_array } {
         upvar $sent_array array_to_use
         parray $array_to_use
}

this_proc toto

Commandes de contrôle

[modifier | modifier le wikicode]

Conditions, boucles, contrôle de l'exécution

[modifier | modifier le wikicode]
if { condition } {
	#code à exécuter si la condition est vraie
} elseif { condition2 } {
	# code à exécuter si la condition2 est vraie
} else {
	# code à exécuter si aucune condition n'est vraie
}
while { condition } {
	# code à exécuter tant que la condition est vraie
}
switch valeur {
	value1 {
		#code à exécuter si valeur remplie la condition value1
	}
	value2 {
		#code à exécuter si valeur remplie la condition value2
	}
	default {
		#code à exécuter si aucune des conditions précédentes n'est vraie
	}
}

Les options -exact -glob et -regexp permettent de choisir le type de règle de comparaison utilisé. Pour distinguer les options de switch de l'argument final de switch on utilise -- :

switch -exact -- $toto {
	1 {
		puts 1
	}
}

Si l'on est en mode -exact de switch, on cherche la section de switch dont la valeur est strictement identique à l'argument de switch.

Si l'on est en mode -glob, alors * remplace n'importe quel caractère zéro ou plusieurs fois. Donc :

set toto test
switch -glob -- $toto {
	t* {
		puts "Mode test"
	}
	default {
		puts "Autre chose"
	}
}

Enfin en mode regexp, on utilise un mode de comparaison de type expression régulière.

set toto test
switch -regexp -- $toto {
	[tT].* {
		puts "Mode test"
	}
	default {
		puts "Autre chose"
	}
}

Un exemple plus complet :

set i 0
while { $i < 200 } {
	switch -exact -- $i {
		0 {
			puts "Je ne vois pas de mouton"
		}
		1 {
			puts "Whoua un mouton là"
		}
		100 {
			puts "T'en a pas marre des moutons ?"
			puts "Tape Ctrl-c pour arrêter du plouc !"
		}
		default {
			puts "$i moutons"
		}
	}
	incr i
}
puts "J'ai une indigestion de mouton,"
puts "plus le mal de mer et la tête lourde"
puts "avec une grosse envie de dormir. J'arrête."
for { # code d'initialisation } { condition } \
    { # passage à l'état suivant (typ. incrémentation } {
	# code à exécuter }

Exemple

for { set i 1 } { $i < 100 } { incr i } {
	if { $i == 1 } {
		set pluriel ""
	} elseif { $i == 57 } {
		puts "Un mosellan"
		set pluriel "s"
	}else {
		set pluriel "s"
	}
	puts "$i mouton${pluriel}"
}

Enfin, il ne faut pas oublier l'instruction foreach. Cette instruction permet de boucler sur les éléments d'une liste.

set l [list lundi mardi mercredi jeudi vendredi samedi dimanche]
foreach jour $l {
	switch -regexp -- $jour {
		^[lmmjvs].* {
			puts "Le $jour on bosse"
		}
		default {
			puts "Le $jour on bulle"
		}
	}
}

Fonctions et procédures

[modifier | modifier le wikicode]

La commande permettant de définir une procédure est proc. C'est une commande comme une autre qui prend 3 arguments :

proc nom_de_la_procedure { liste des arguments } {
	# code a exécuter quand la procédure est appelée.
	# Les valeurs des variables $liste $des et $arguments sont disponibles
	# On peut avoir accès aux variables défini au niveau 0 de l'exécution
	# avec l'instruction global
	# upvar permet de passer des variables par pointeur
	# on retourne une valeur avec :
	return 1
}

Exemple :

#!/usr/bin/tclsh
set DEBUG 1
proc debug { message } {
	global DEBUG
	if $DEBUG {
		puts $message
	}
}

proc read_file { filename } {
	if { [catch { set fileid [open $filename] }] } {
		puts "Impossible d'ouvrir $filename"
		return ""
	}
	debug "Le fichier $filename est ouvert"
	set full_text [read $fileid]
	# Je vais renvoyer la liste des lignes du fichiers
	return [split $full_text "\n"]
}

set cour1_list [read_file "cour1.txt"]
puts "$cour1_list"

Entrées/sorties et gestion des erreurs

[modifier | modifier le wikicode]

Entrées/sorties

[modifier | modifier le wikicode]

Les commandes sont les suivantes : ||open gets seek flush close read tell puts file

La commande open retourne un identifiant qui sera utilisé lors des appels aux autres commandes. Ex:

set f [open "toto.txt" "r"]
=> file4
set toto [read $f]
=> xxxxxx
close $f

Ou encore :

set f [open "titi.txt" w]
=> file4
puts $f "Ecrit ce texte dans le fichier"
# puts permet d'écrire dans un canal déterminé (défaut sortie standard)
close $f

Les autres commandes utiles sont :

# lecture d'une ligne
set x [gets $f]
# read permet de lire un certain nombre d'octets
read $f 100
# seek pour se positionner
set f [open "database" "r"]
seek $f 1024
read $f 100
Ici on lit les octets 1024 a 1123

Gestion des erreurs

[modifier | modifier le wikicode]

La commande catch permet d'attraper les erreurs :

if [catch { n'importe quoi}] {
	puts "Vous avez dû taper une bêtise dans la commande appelée par catch"
	exit
}

Tout cela est bien sûr très utilisé lors de l'ouverture d'un fichier en lecture ou en écriture et plus généralement dès que l'on communique avec l'extérieur.

catch { exec cp toto tutu }

La commande error permet elle de générer une erreur dans un code et d'y associer un message d'erreur.

Récapitulatif des commandes du Tcl

[modifier | modifier le wikicode]

COMMANDES TRES UTILISEES

for incr list regsub close 
expr foreach llength
append concat format load return 
array gets lrange proc switch
file glob lappend lreplace puts
break global lsearch set 
catch eval lindex lsort while 
exec if linsert open regexp source

MOINS USITEES

clock exit package split unknown after
info pid rename string unset 
fblocked interp pkg_mkIndex subst update 
continue fconfigure join scan uplevel 
bgerror eof seek tclvars upvar 
error fileevent library pwd tell vwait 
filename history read socket time
cd flush trace

Comme vous pouvez le remarquer, cela représente vraiment peu de commandes, ce qui explique la facilité d'apprentissage du tcl.

Pas toujours les mêmes

[modifier | modifier le wikicode]

Lecture de fichiers

[modifier | modifier le wikicode]

L'objectif est d'écrire un programme tcl qui parcourra un fichier HTML et donnera la liste des noms entre les tags HTML h1 et /h1 et les variantes de ces tags h2 /h2 et h3 /h3. La liste sera imprimée sur la sortie standard. Il faut tester sur la page suivante : http://www.w3.org/TR/REC-html32.html Sauvegardez cette page dans /tmp sous le nom test.html, puis lancez votre programme dessus.

Lecture des arguments d'un programme

[modifier | modifier le wikicode]

Vous souhaitez écrire un programme qui a des options d'appel en ligne de commande. En particulier :

  • -h[elp] : affichage d'une aide ;
  • -f[ile] nom_de_fichier : fichier d'entrée ;
  • -l[evel] [0-9]+ : niveau de recherche de 1 à ce que vous voulez.

Les arguments peuvent être passés sur la ligne de commande dans n'importe quel ordre. À la fin de l'initialisation de la fonction vous avez 3 drapeaux à 1 ou 0 indiquant si les options -help, -file ou -level ont été appelées. Le nom du fichier d'entrée et le niveau sont stockés dans les variables filename et level.

Lors de l'appel d'un programme, les variables suivantes sont disponibles :

  • argc : nombre d'arguments sur la ligne de commande (stockée dans argv),
  • argv : liste des arguments sur la ligne de commande, sans la commande,
  • argv0 : nom de la commande,
  • env : tableau contenant les variables d'environnement.
#!/usr/bin/tclsh
puts "argc : $argc"
puts "argv : $argv"
set i 0
foreach arg $argv {
	puts "argument $i : $arg"
	incr i
}
puts "argv0 : $argv0"

Nous appelons cette commande args.tcl et la rendons exécutable puis testons :

args.tcl

$ ./args.tcl
argc : 0
argv :
argv0 : ./args.tcl

$ ./args.tcl -f test -level 3
argc : 4
argv : -f test -level 3
argument 0 : -f
argument 1 : test
argument 2 : -level
argument 3 : 3
argv0 : ./args.tcl

Vous allez créer une machine à état. Le passage d'un état à un autre se fait lorsque l'on passe à l'argument suivant. L'état de base de cette machine est :

  • check_args : attente d'un argument type -f, -l ou -h.

De cet état, vous allez sauter à l'état suivant lors du test de l'argument suivant, en fonction de la valeur de arg. Si arg est à -f*, alors vous sautez dans l'état is_file, et vous initialisez filename avec $arg. Au passage, vous initialisez le drapeau correspondant à la présence du nom de fichier sur la ligne de commande à 1. Il faut ensuite revenir à l'état check_arg.

Il vous reste juste à prévoir les états correspondants pour help et pour level et à les gérer de même.

Bon courage. Merci de ne pas oublier le guide à la fin de la visite. À votre bon cœur monsieur dame.


Vos premiers pas en Tk

Tk - la bibliothèque graphique de Tcl


Auteur de la version initiale : Arnaud LAPREVOTE

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

[modifier | modifier le wikicode]

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.

#!/usr/bin/wish

label .nouveau_label -text "Je dis juste bonjour !"
button .say_ok -text "OK ?"

pack .nouveau_label .say_ok -side top

bind .say_ok &lt;Button-1> { exit }
bind .say_ok &lt;Button-2> { .nouveau_label configure -text "Vous avez appuyez sur le bouton 2" }

Ou encore plus simplement :

#!/usr/bin/wish

label .nouveau_label -text "Je dis juste bonjour !"
button .say_ok -text "OK ?" -command { exit }

pack .nouveau_label .say_ok -side top

Création d'un widget

[modifier | modifier le wikicode]

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.

#!/usr/bin/wish
toplevel .nouvelle_fenetre

label .nouvelle_fenetre.label -text "Je dis juste bonjour !"
button .nouvelle_fenetre.say_ok -text "OK ?" -command { destroy .nouvelle_fenetre }

pack .nouvelle_fenetre.label .nouvelle_fenetre.say_ok -side top

Lors de la création d'un widget, des paramètres sont initialisés. Par exemple, dans :

label .exemple_label -text "Je dis juste bonjour !"

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 :

man options


options(n) Tk Built-In Commands options(n)


_________________________________________________________________

NAME
       options - Standard options supported by widgets
_________________________________________________________________


DESCRIPTION
       This  manual  entry  describes  the  common  configuration
       options supported by widgets in  the  Tk  toolkit.   Every
       widget  does not necessarily support every option (see the
       manual entries for individual widgets for a  list  of  the
       standard  options supported by that widget), but if a wid-
       get does support an option with one of  the  names  listed
       below,  then  the  option has exactly the effect described
       below.

       In the descriptions below, ``Command-Line Name'' refers to
.........

Modification des paramètres d'un widget existant

[modifier | modifier le wikicode]

Reprenons notre petit exemple : 2 solutions existent ici. Si vous tapez directement dans wish, alors tapez :

label .exemple_label -text "Je dis juste bonjour !"
pack .exemple_label
.exemple_label configure -text "puis au revoir"

Si vous souhaitez plutôt utiliser un programme dans un fichier, alors tapez :

#!/usr/bin/wish
set wait_var 0

label .exemple_label -text "Je dis juste bonjour !"
pack .exemple_label
# Dans une seconde je changerai la variable wait_var
after 1000 { incr wait_var }
# J'attends une modification de wait_var et je suis
# dans la boucle d'événement.
vwait wait_var
# wait_var a bougé je sors de la boucle
.exemple_label configure -text "puis au revoir"
after 1000 { incr wait_var }
vwait wait_var
# Je sors de l'application
exit

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 :

% label .exemple_label -text "Je dis juste bonjour !"
% pack .exemple_label
% .exemple_label cget -text
Je dis juste bonjour !

Remarque Dis tonton Arnaud, tu nous as bassiné avec le fait qu'une ligne tcl consistait toujours en :

commande argument1 argument2 ...

Or je constate ici que l'on a :

nom_du_widget commande argument1 ...

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

[modifier | modifier le wikicode]

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

destroy .nom_du_widget

Les options standard

[modifier | modifier le wikicode]
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

[modifier | modifier le wikicode]

Créez une application contenant un label, un bouton "Quitter", un bouton "Changer". Au départ, sur le label on lit :

Je dis juste bonjour

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

[modifier | modifier le wikicode]

3 modes de placement existent :

  • pack
  • grid
  • place
frame .top 
label .top.label -text "Name" 
entry .top.name -textvariable name 
image create photo test -file /usr/src/linux/Documentation/logo.gif 
label .bottom -image test 
pack .top.label .top.name -side left 
pack .top .bottom -side top 


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.

label .one -text "One"
entry .one_entry -textvariable one_entry
label .two -text "BIIIIIG TWO"
entry .two_entry -textvariable two_entry
label .three -text "Un très grand commentaire"
grid .one -column 1 -row 1
grid .one_entry -column 2 -row 1
grid .two -column 1 -row 2
grid .two_entry -column 2 -row 2
grid .three -column 1 -row 3 -columnspan 2 -stick e

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

[modifier | modifier le wikicode]

On peut utiliser la commande pack avec les options :

 -side [left|right|top|bottom]

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.

 -fill [x|y|both|none]

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.

-expand [true|false]

Lors du redimensionnement de la fenêtre, les widgets packés avec cette commande suivront l'expansion de la taille de la fenêtre.

-padx [0-9]+
-pady [0-9]+

Le nombre de pixels à droite et à gauche du widget courant sera ajouté en horizontal (padx) ou en vertical (pady) autour du widget.

-ipadx [0-9]+
-ipady [0-9]+

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.

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 :

label .lab1 -text "------------------------------lab1---------"
label .lab2 -text "lab2" -relief ridge
pack .lab1

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 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 :

frame .frame1 -background "#808080" -relief groove
frame .frame1.frame2 -background "#000000" -relief groove
label .frame1.frame2.lab1 -text "Ici :"
set ent1 "Entrée de texte numéro 1"
entry .frame1.frame2.ent1 -textvariable ent1 -width 20
set ent2 "Entrée de texte numéro 2"
entry .frame1.ent2 -textvariable ent2 -width 30
button .butt1 -background red -text "OK" -command {exit} 
pack .frame1.frame2.lab1 -side left
pack .frame1.frame2.ent1 -fill x -expand true -side left
pack .frame1.frame2 -side top -ipadx 5 -ipadx 5
pack .frame1.ent2 -side top -fill x -expand true
pack .butt1 -fill both -expand true -side left
pack .frame1 -fill x -expand true -ipadx 5 -ipady 5 -side left

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.

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 :

-column [0-9]+
-row [0-9]+

Identification de la case où le widget sera placé

-columnspan [0-9]+
-rowspan [0-9]+

Le widget est placé sur une ou plusieurs colonnes, sur une ou plusieurs lignes.

-padx
-pady
-ipadx
-ipady

Strictement identique à ces valeurs dans le packer.

-sticky [ewns]+

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 à

grid columnconfigure columnindex [-minsize [0-9]+] [-weight [0-9]+] [-pad [0-9]+]
grid rowconfigure rowindex [-minsize [0-9]+] [-weight [0-9]+] [-pad [0-9]+]

Assez étrangement, les numéros de lignes et de colonnes commencent à 1 et non pas à 0.

Événements disponibles

[modifier | modifier le wikicode]

On peut associer à chaque événement d'un widget des actions. Cela se fait avec la commande bind.

label .lab1 -textvariable var
bind .lab1 <Enter> { incr var }
bind .lab1 <1> { incr var 10 }
bind .lab1 <Button-2> { incr var 20 }
bind all <Key> { set var2 "%K" }
label .lab2 -textvariable var2
pack .lab2

La syntaxe de bind est :

bind [.nom_d_un_widget|all] <événement> { script à exécuter }

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

[modifier | modifier le wikicode]
  • 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, ...

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
label .lab -text "Bonjour"
button .but -text OK -command {\
 .lab configure -text "J'ai appuye sur OK" }
pack .lab .but -side top

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.

label .lab1 -text "Ceci est un label" -background \
	"#ff00ff" -foreground "#00ff00"
label .lab2 -textvariable var
set var "Ceci est un exemple"
button .but -text OK -command {
	set var "Tiens le label 2 a changé"
}
pack .lab1 .lab2 .but

Les entrées de texte

[modifier | modifier le wikicode]
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
label .lab -textvariable var
entry .ent -textvariable var
set var "Ceci est un exemple"
pack .lab .ent

Les cases à cocher

[modifier | modifier le wikicode]

Une case à cocher permet de savoir si une option est ou non sélectionnée. Cela s'utilise de la manière suivante :

checkbutton .c1 -text "Essai" -variable check1 \
 -command { puts "c1 contient $check1" }

Les options -onvalue -offvalue permettent de forcer une valeur pour la variable selon que la case est ou non cochée.

Les boutons radio

[modifier | modifier le wikicode]

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

radiobutton .r1 -text "Tout" \
-variable test -value "tout" -anchor w
radiobutton .r2 -text "Rien" \
-variable test -value "rien" -anchor w
.r1 select
.r2 invoke
.r2 deselect
.r1 toggle

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

tk_optionMenu .nom_du_widget variable_global \
elt1_du_menu elt2 elt3 ...

Ainsi que des menus pop-up.

set filetype text
menubutton .file \
        -text "File" -menu .file.menu
pack .file -side left
menu .file.menu 
.file.menu add command \
        -label "Nouveau" \
        -command { puts "New" } 
.file.menu add command \
        -label "Ouvrir..." \
        -command { puts "Open..." } 
.file.menu add separator
.file.menu add radiobutton \
	-label "Graphique" -variable filetype \
	-value "graphic" -command { puts $filetype }
.file.menu add radiobutton \
	-label "Texte" -variable filetype \
	-value "text" -command { puts $filetype }
.file.menu add separator
.file.menu add checkbutton \
	-label "Fichier rw seulement" \
	-variable rwfile -onvalue on \
	-offvalue off -command { puts $rwfile }
.file.menu add separator
.file.menu add cascade \
	-label "Autre menu" -menu .file.menu.sousmenu
.file.menu add command \
        -label "Exit" \
        -command { exit }

menu .file.menu.sousmenu

.file.menu.sousmenu add command \
	-label "Action 1" \
	-command { puts "Sous-menu action 1" }

.file.menu.sousmenu add command \
	-label "Action 2" \
	-command { puts "Sous-menu action 2" }

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 :

image create photo toto_photo -file nom_d_un_fichiergifoujpeg

Il ne reste plus qu'à associer la photo à un label

label .lab1 -image toto_photo

Il est évidemment possible de changer de photo par exemple :

toto_photo configure -file !nom_dunautrefichier.jpg

Vous pouvez vous reporter au man de la commande image pour voir les commandes disponibles.

Interface graphique contre grosse fatigue

[modifier | modifier le wikicode]

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 :

http://vtcl.sourceforge.com

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

[modifier | modifier le wikicode]

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 :

set filename [tk_getOpenFile -filetypes \
 [list [list "Fichiers graphique" {.gif .jpg .jpeg}] \
 [list "Tous fichiers" {*}]]]

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.

"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.


Notation particulière de Tcl

Tcl utilise une notation particulière pour indiquer les appels de fonctions.

En mathématique, ainsi que dans la plupart des langages de programmation, on utilise la notation y = f(a,b).

Tcl a magnifiquement simplifié cette notation en mettant les parenthèses AVANT la fonction :

set y [f a b]

Autrement dit, en mathématique, on écrirait y = (f a b) c'est lumineux, car on comprend où commence la fonction (par le crochet "[") et où elle finit (par "]"). Les virgules ont également disparu ... S'il y a des appels de fonctions imbriquées, les parenthèses doivent être imbriqués, par exemple set z [g x [f a b]] ... en math z=g(x,f(a,b)) Cette notation se rapproche évidemment de la notation utilisée dans les langages de scripts sh, csh, etc. mais là on écrit y = `f a b` avec des ` (accent grave) qui ne sont pas des parenthèses (ouvrantes et fermantes).


Dans tout langage de programmation, il faut toujours faire la distinction entre la référence à la variable et le contenu de la variable. Lorsque l'on utilise une variable, disons X, le fait que l'on parle de sa référence (ou son adresse) ou de son contenu dépend généralement de sa place dans l'instruction du langage de programmation. Ainsi en C on écrit X=Y ce qui signifie qu'on affecte à la variable X (on ne s'occupe pas de sa valeur) le contenu de la variable Y. X situé à gauche du signe "=" est traité différemment de Y qui est à droite du signe "=" (on ne s'intéresse qu'à sa valeur). En Tcl, (mis à part les raccourcis d'écriture décrits ci-dessous) c'est beaucoup plus logique ... Il existe la fonction set :

[set X] est la fonction qui rend le contenu de la variable X

set X 25 permet d'affecter à X la valeur 25

[set X 25] , par extension fait les deux, elle affecte 25 à X et rend la valeur de X, c'est-à-dire 25

ainsi x=y s'écrit set X [set Y]

c'est logique ... mais un peu lourd. En Tcl on utilise le raccourci d'écriture $Y à la place de [set Y], set X [set Y] s'écrit alors set X $Y

On utilise presque toujours $Y au lieu de [set Y] sauf quand il y a ambiguïté. On utilise aussi la notation ${VARIAB} à la place de $VARIAB (ou canoniquement [set VARIAB]) si ambiguïté.


Pourquoi tout cela ? ... Pour pouvoir concaténer des chaînes de caractères de la manière la plus naturelle possible, en les juxtaposant. Par exemple :

set Titre "Monsieur"
set Prenom "Jean"
set Nom "Martin"
set Texte "$Titre $Prenom $Nom est l'heureux gagnant ..."

Dans cet exemple les mots sont bien séparés par des espaces

Prenons un exemple sans espaces

set Rep "/usr/local/"
set Fichier "$RepToto.txt"

ne marche pas parce il semble qu'il y ait une variable de nom RepToto alors que ce qu'on voulait c'est (canoniquement)

set Fichier "[set Rep]Toto.txt"

ou (en raccourci sans ambiguïté),

set Fichier "${Rep}Toto.txt

pour bien faire la distinction entre la variable $Rep et le texte Toto.txt


Pour finir ces histoires de notation ... un appel à une fonction se fait par

set x [f $a $b]

si l'on veut transmettre à f le contenu de a et b

ou

set z [h $a b]

si l'on veut transmettre à h la valeur de a et la référence (ou adresse) de la variable b (pour que h la modifie, par exemple)

on aurait dû écrire, canoniquement

 set z [h [set a] b]
GFDL GFDL Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture.