Interface utilisateur en Tcl/tk

Sommaire (svp cliquer sur la ligne)

Retour à la première page.

1.Utilité de bien séparer le noyau de calcul de l'interface utilisateur

Cela a déjà été dit, mais je conseille de séparer le calcul et l'interface graphique de l'utilisateur en deux entités séparées. Examinons leurs différences:

- Dans un programme mixant interface et calcul en un seul morceau, toutes les données sont en mémoire vive :

Avantages
Les échanges entre les différentes parties de l'application sont très rapides
Le code peut être optimisé en ressource mémoire, en particulier en utilisant les bibliothèques dynamiques.
Inconvénients
Les développements doivent être simultanés, souvent par la même personne. Ce n'est pas une obligation, mais un développement séparé demande un bonne coordination du projet

- Dans un programme séparant les deux fonctions, les données utilisées par le noyau sont écrites sur le disque dur, puis relues par le noyau de calcul.

Avantages
L'avantage principal est de permettre un déboggage plus facile de l'ensemble. Il est possible d'examiner les données réellement envoyées au noyau de calcul. L'erreur est plus facile à localiser entre les parties calculs et interface.
Pouvoir faire une interface provisoire (en Tcl/tk dans mon cas), pour tester facilement le noyau de calcul
Confier le noyau à d'autres programmateurs qui l'intègrent dans le système du client.
Inconvénients
Une plus grande lenteur de l'ensemble à cause des accès disques intermédiaires. Cela est gênant dans le cas d'accès répétés pour des traitements courts. Il peut y avoir un décalage de version entre l'interface et le noyau de calcul.

2.Quel outil de construction de GUI choisir ?

La vitesse d'affichage des pages n'est plus un critère de choix. Pour un humain, 800 ms est un temps court. Mais il laisse le temps aux ordinateurs modernes de réaliser beaucoup d'actions. Aujourd'hui, les langages interprétés sont bien adaptés pour écrire une interface.

2.1.Ultra simple: le script shell

Tel que décrit dans le chapitre précédent, l'utilisation du noyau de calcul peut se limiter à une version "ligne de commandes". C'est la solution que j'utilise lorsque le programme n'est destiné qu'à mes propres besoins. Toutefois, si la commande est longue les erreurs de frappe rendent l'exercice un peu "casse-pieds" (pour rester poli).

Un solution simple est d'écrire un petit script shell. Il sera facile d'y changer les noms des fichiers dans un éditeur de texte.

datfile=./site/cas28.dat
tabfile=./site/cas28.tab
resfile=./site/cas28.res

bindir=/home/moi_meme/Programme/Prog_c

echo debut calcul
$bindir/le_prog -d2 -i $datfile -o $resfile -x $tabfile

Si le fichier a l'attribut exécutable, il doit être possible de le lancer par un double clic dans konqueror (il me semble). Les fichiers .bat permettent de faire la même chose dans Windows.

Le shell permet de faire beaucoup plus. Mais à ce stade il faut choisir entre rester avec le script bash ou utiliser d'autres langages.

2.2.Les "boite à outils graphique"

Il existe un certain nombre de "toolkit" qui permettent de créer des interfaces. Je les ai abandonnés il y a fort longtemps, leur défaut majeur "à l'époque" était d'être trop lié à une architecture système. Cela a évolué depuis, mais je n'y suis pas revenu.

2.3.C'est mieux avec Tk

Certains sont liés avec la bibliothèque Tk et permettent de faire rapidement une interface. Il y en a plusieurs : Lua, Perl-Tk Python-Tinker et bien sur Tcl/Tk.

J'utilise Tcl/Tk parce qu'il est libre, disponible sur Linux, Mac et Windows, facile et que l'on peut faire un exécutable "stand alone" facilement. Ce dernier point est important lors de la distribution. Ce langage de script est bien rodé, il peut réaliser à peu près toutes les interfaces classiques.

Mais la véritable raison est que je connais ce langage et que les autres n'apportent pas suffisamment d'avantages pour en changer, mais cette assertion peut être rapidement inversée si on connaît Perl.

Mon deuxième choix serait Python/wxWidget parce qu'il est élégant, orienté objet et d'un look plus moderne. Mais pour l'instant Tcl/Tk suffit et ne semble pas en passe de disparaître.

3.Exemple du lanceur seul

Le premier exemple sera un script de lancement de carré3.c tel que décrit dans le chapitre sur le noyau de calcul données dans la ligne de commandes

3.1.Un script pour filtrer les données

Avant de commencer véritablement à écrire une interface, nous allons nous servir des capacités de Tcl/Tk pour corriger certains comportements bogués qui ont été repérés précédemment.

Commençons par un premier script ! (s.v.p. cliquer sur la ligne)

Comme c'est habituel, il y a plus de lignes pour traiter les erreurs que pour réellement lancer le calcul.

    Détaillons quelques astuces et commentaires.
  • L'écriture comporte l'option -nonewline pour voir la valeur entrée au clavier sur la même ligne.
  • Il faut un ordre flush entre l'écriture à l'écran et la lecture au clavier. Sinon l'affichage ne se fait pas au bon instant.
  • Le nettoyage de l'entrée est fait avec regsub. Sa syntaxe est puissante mais complexe, même pour un programmeur confirmé.
  • L'appel au programme externe est fait avec l'ordre exec, il doit se trouver dans le dossier courant. L'interface est "inerte" pendant le calcul et tous les résultats arrivent d'un seul coup à la fin du calcul. Ce qui ne pose pas de problème ici, mais devra être modifié pour des calculs longs.
  • L'appel est en-capsulé par l'ordre catch. Cela permet d'éviter que l'ensemble s'arrête en cas d'erreur
  • Normalement $rep contient "son carré vaut : 529". Une des façons de récupérer la valeur et de transformer la variable rep en une liste et de prendre le dernier élément.

A ce stade l'unique avantage du script par rapport à l'utilisation directe du programme est de corriger les bogues : "séparateur décimal virgule" et "séparateur de millier espace". Faire le même traitement en C est possible (Tcl est du C) mais serait nettement moins concis.

3.2.Une mini interface

Le script précédent n'apporte pas beaucoup de fonctionnalités, mais il est facilement transformable en un vrai GUI.

Le même mais en GUI (cliquer s.v.p)
aperçu Il ne faut plus terminer le script par exit, sinon l'interface s'affiche et s'efface instantanément. Il faut laisser Tcl/Tk entrer dans la boucle cachée de Tcl de traitement des évènements.
    Quelques commentaires
  • L'interface est placée au début du fichier, comme cela elle est affichée rapidement et l'utilisateur ne se rend pas compte du temps nécessaire à l'installation du GUI. La définition des "callback" (fonctions appelées par les boutons et autres "widgets") se fait le temps que l'utilisateur regarde le premier écran. Honnêtement, ici le script est si court que l'ordre n'a pas d'importance.
  • Noter les options sticky ew et grid configure qui permettent d'ajuster le comportement des éléments graphiques lorsque la fenêtre est agrandie.
  • Le programme carre3 est exécuté à partir de la procédure calcule elle même activée par un retour chariot lorsque $fene.ent1 a le focus. L'ordre bind ajoute cet évènement à la liste des évènements surveillés par l'interface. Il est aussi possible d'ajouter un bouton [calcul].
  • La procédure calcule reçoit comme argument la donnée à traiter et le nom de la fenêtre à mettre à jour. Elle ignore complètement la structure de l'interface, elle peut donc être réutilisée pour une autre interface.

Il n'y a pas de variable associée au widget entry car l'ordre [$fene.ent1 get] va chercher le texte affiche à l'écran. L'ordre bind l'envoie directement (sans vérification) dans la procédure calcule. Mais ce n'est qu'une façon de faire, Tcl/Tk permettrait de limiter&valider les valeurs lors de la saisie.

3.3.Quelques digressions sur l'ordre exec

L'utilisation de l'ordre exec est en général facile. Mais j'ai déjà rencontré quelques situations délicates. Lorsque le programme appelé est long, plusieurs minutes de temps de calcul, l'interface peut rester bloquée lorsqu'il a terminé (constatation effectuée avec Windows). Pour l'instant la seule parade que j'ai trouvé consiste à exécuter le programme en mode arrière plan "background" avec un "&" en dernier argument, puis de demander au script d'attendre avec l'ordre after un_certain_temps. Le temps d'attente doit être plus long que la durée du calcul, ce qui pose problème si elle n'est pas connue.

Une possibilité (que je n'ai pas encore testé) est de vérifier régulièrement la présence du calcul avec l'ordre pid

4.Un exemple plus complet

Pour cela nous allons interfacer le programme de Pierre Legendre du Département de Sciences Biologiques de l'Université de Montréal appelé modele-ii.

La compilation avec g77 se passe sans trop de difficultés (un avertissement pour le programme Range qui n'est pas déclaré external et un dépassement de la colonne 72 dans le format ligne 322, il suffit d'enlever un espace).

Faisons le fonctionner dans un cas simple.
Il n'est pas possible de le lire un fichier dans un autre dossier ! La solution: se placer dans le dossier les fichiers de données et appeler le programme modele-ii de là. Le programme est tatillon sur les fichiers, en particulier, lorsque le fichier existe déjà il s'arrête brutalement sans laisser de seconde chance.

Mais nous pouvons être content car il cause le Français !

4.1.première étape : on repère les questions posées à l'écran

Il suffit de le faire fonctionner en mode en mode ligne de commandes, voici ce qu'il écrit dans le cas minimal:

 (1) Francais
 (2) English
1
 Nom du fichier de resultats?
E4_100x2.res
 Nom du fichier de donnees (2 colonnes: x et y, ou y et x)
E4_100x2.txt
 Nombre de lignes de la matrice ? (Maximum =  500)
100
 Identification de la variable dependante y:
 1 = x d'abord, y ensuite ?
 2 = y d'abord, x ensuite ?
1
 Calculer l'axe majeur des donnees cadrees (AMDC)?
 (0) non,  (1) oui
0
 Combien de permutations de y ? (ex. 499, 999, ...)
 0 = pas de test par permutation.
0

4.2.Execution de modele-ii sous controle de Tcl/Tk.

Ce n'est pas encore une interface, mais cette fois ci c'est Tcl/Tk qui répond aux questions.

Voici comment cela ce fait

Le script doit agir dans deux directions avec le programme.

  1. d'une part il récupère les écritures
  2. de temps en temps répondre aux questions posées.

Il n'est plus possible de lancer un sous process par exec. Mais Tcl/Tk permet d'ouvir un pipeline (tuyau de process) qui permet de communiquer dans les deux sens. Il s'ouvre par l'ordre open de la même façon qu'un fichier. La barre avant le nom de commande permet de distinguer les véritables fichiers des tuyaux de process.

4.2.a.La façon simpliste

La façon la plus simple est de lire autant de lignes que ce que le noyau de calcul affiche (ordre gets $input ligne) et d'insérer les réponses au bon endroit par l'ordre (puts $input reponse). Malheureusement cela ne fonctionne que dans des cas très simples.

La véritable difficulté vient des tampons d'écriture. Lorsque le programme écrit une ligne, elle est mise dans un tampon (espace en mémoire provisoire) et réellement écrite lorsque le tampon est plein. Puisque c'est l'écriture qui est interceptée par input l'arrivée des lignes se fait par paquets.

Ce qui a pour effet que la plupart des ordres (gets $input ligne) récupèrent une ligne vide.

En général l'ensemble se bloque, l'interface attendant une question qui est restée dans le tampon et le programme de calcul attendant une réponse que l'interface n'envoie pas car elle n'a pas reçu la question, ou il la déjà envoyée de puis longtemps et le tampon a été vidé.

4.2.b.vider les tampons après écriture

Pour que cela marche, il faut que le programme de calcul vide son tampon après chaque écriture et que l'interface fasse de même.

Pour l'interface
Le tuyau input doit être configuré par fconfigure $input -buffering line, c'est à dire qu'il vide le buffer à chaque fin de ligne.
Pour le programme de calcul
Normalement, le système de fenêtres vide le buffer après chaque écriture vers la sortie standard. Mais ce n'est pas toujours le cas, en particulier avec l'environnement Mingw. Dans le programme modele-ii, j'ai ajouté un certain nombre de fois call flush(6) après toutes les questions. En C l'ordre est fflush(stdout);. Si on n'a pas accès au code source, il peut être délicat d'interfacer correctement le programme.

4.2.c.N'envoyer la réponse qu'au bon moment

En fait, vider les buffers ne résout pas toutes les difficultés. Si l'interface est plus rapide que le programme de calcul, elle peut très bien avoir écrit la première réponse, puis vidé le buffer et écrit la réponse à le seconde question. Ceci avant que le noyau de calcul ait lu la première réponse. Donc il faut envoyer la réponse uniquement lorsque le programme de calcul pose la question.

Pour cela il faut ajouter un évènement dans la boucle cachée de Tcl/Tk. C'est ce que fait l'ordre fileevent $input readable. Tk va maintenant surveiller le pipeline $input et chaque fois qu'une ligne est écrite par le programme de calcul exécuter la procédure processline.

Celle ci lit la ligne, puis envoie la réponse à la question posée et enfin demande un rafraîchissement de l'interface. L'avantage est de n'envoyer qu'une réponse à la fois et donc d'éviter les pertes.

L'inconvénient principal de cette méthode est de ne pouvoir traiter qu'un petit nombre de configurations. En particulier, si il a des réponses incompatibles au bon déroulement du calcul, la procédure processline devient vite complexe.

Mais elle est bien adaptée aux programmes scientifiques où il y a relativement peu de données comparé à la difficulté du traitement numérique.

4.3.Et maintenant une vraie interface

A force d'écrire des interfaces, j'en suis arrivé à inclure systématiquement des fonctionnalités comme se souvenir du dernier cas traité, permettre d'afficher la notice, ... Elles peuvent sembler superflues, mais sont utiles pour la rendre agréable à l'utilisateur.
Du coup une interface même simple comporte de beaucoup de lignes de script.

4.3.a.Interface sur une page

Cette interface sera simple car il n'y aura qu'une seule page, mais on prépare le terrain pour une interface plus complexe.

Je préfère diviser le script en plusieurs fichiers, car permet d'afficher les différentes étapes en un coup d'œil. Survol des différents fichiers :

  • le fichier principal appelé en premier
  • la définition des widgets, elle même séparée en plusieurs fichiers, un commun et un par page
  • les fonctions appelées par les widgets, aussi séparées par catégorie ou par page
  • util.tcl et help.tcl qui contiennent des fonctions standards identiques dans plusieurs interfaces
  • et les contributions externes en package
Source de la structure principale (cliquer SVP)

Elle comporte plusieurs items, dont l'ordre proposé est conseillé pour obtenir une interface qui s'affiche rapidement.

  1. le cartouche entête
  2. initialisation des variables générales à leur valeur par défaut
  3. lecture de leur valeur définie lors de l'installation
  4. lecture des fonctions utilitaires et des packages n'écessaire à l'affichage
  5. définition et affichage de la première page
  6. lecture des préférences des utilisateurs, écrites lors de l'utilisation précédente
  7. définition des autres pages (sans affichage)
  8. lecture et enregistrement des fonctions callback
  9. fignolage, enregistrement des messages des bulles d'aide
page principale

La définition de la page principale ne présente pas de particularité marquante, elle n'est pas détaillé ici. Elle utilise des tk_Optionmenu, mais il est possible de les remplacer par des radiobutton.

Pour que l'utilisateur n'ait pas l'impression d'être en face d'un programme bloqué, il est important que les actions effectuées avant l'affichage de la première page soient imperceptibles. Ce qui peut poser problème lors d'utilisation de "gros packages" comme bwidget. Il est possible alors d'afficher une fenêtre temporaire demandant à l'utilisateur de patienter !

4.3.b.Simuler une console

La façon la plus simple de terminer l'interface est d'activer une version modifiée du script précédent. Cela a un inconvénient, l'utilisateur ne voit pas le déroulement du calcul. Aussi lorsque le calculs long, il a l'impression que tout est figé. Il faut donc simuler une console pour afficher les messages échangés avec le programme.

Lorsque on appuie sur le bouton Run on active quatre procédures:

  1. topconsole : c'est ma version graphique de la console. C'est simplement une fenêtre popup contenant un widget texte. Il y a en bas une barre de boutons.
  2. LanceExe qui met en forme la ligne de commande puis ouvre le pipeline (il est possible de le joindre au précédent)
  3. ecritrace qui gère les échanges avec modèle2
  4. stop permet interrompre le calcul prématurément, (uniquement avec Linux)
Source des procédures appelées (callback) (cliquer S.V.P.)

4.3.c.Difficultés rencontrées avec modele-ii

La mise au point du script m'a pas été aussi facile que habituellement. Voici quelques ajouts qui ont été nécessaires comparés à mes programmes habituels.

  • des ordres call flush(6) ont été ajoutés dans le code FORTRAN pour éviter les situations de blocage. La raison en est expliquée plus haut.
  • dans l'ordre FORTRAN open du fichier résultat le status='NEW' a été remplacé par status='UNKNOWN'. Sinon le programme de calcul s'arrête lorsque une fichier d'un calcul précédent existe déjà sans possibilité de forcer.
  • Avant d'appeler le programme de calcul, il faut se placer dans le dossier des fichiers data. Sinon le programme ne trouve pas le fichier même si le nom est donné avec le chemin d'accès. Ceci est sans doute une particularité du compilateur g77 utilisé ici avec Linux.

Les deux dernières remarques génèrent des erreurs qui sont envoyées dans stderr. Celles ci ne sont pas capturées par l'interface ! En conséquence, l'ensemble se bloque !

Intercepter stderr est explicitement décrit avec l'ordre exec, il faudrait utiliser l'option 2>@. Ce n'est pas possible avec l'ordre open.

C'est une limitation de la version actuelle de Tcl/Tk. Celle ci a été levée dans la nouvelle version 8.5.

4.3.d.Arrêter un calcul en cours

Il arrive que l'on ai besoin d'arrêter un calcul qui entre dans un boucle "infinie" ou dont les résultats partiels sont manifestement erronés. Normalement le bouton Compute permet d'interrompre le calcul en cours. Il devient Stop pendant l'exécution.

Problème, à la fois sous Linux et sous Windows, seule la fenêtre toplevel est fermée et le calcul se continue en arrière plan. Le programme de calcul devenu orphelin et il faut l'arrêter manuellement. Avec Windows, la combinaison magique des touches [Atl-Ctrl-Supp] active le gestionnaire de programme, qui lui permet d'arrêter le programme. L'équivalent sous Linux est l'ordre ps -aux pour reperer le numéro du precessus puis kill pour l'arrêter.

J'ai trouvé récemment une solution sur le wiki anglophone à ce problème voir ( kill en tcl ou l'article original). Il n'est pas obligatoire l'utiliser les extensions proposées comme un package, il suffit de charger (load) les fichiers kill-linux-x86.so ou kill-windows-x86.dll et de les activer par

kill [pid $input]          ;# end spawned process

La modification a été intégrée dans l'exemple "point de départ".

4.3.e.Quelques commentaires sur la console

Le lancement du calcul ne s'effectue pas directement après l'affichage de la fenêtre, mais après avoir appuyé sur le bouton Compute.

Le bouton "Change directory exe" est une facilité. Souvent j'oublie de préciser où se trouve le programme à appeler, le bouton permet de corriger cela sans revenir à l'interface principale.

La console profite des possibilités du widget text pour mettre en valeur certaines lignes, en particulier celles contenant le mot erreur ou error.

Le script n'est pas n'est pas très propre car il utilise beaucoup de variables globales. Cette façon de procéder peut provoquer des interférences avec d'autres variables de l'interface. C'est une solution suffisante pour des interfaces pas trop complexes.

L'ordre tkwait visibility est nécessaire après chaque écriture, sinon Tcl/Tk attend de n'avoir "rien à faire" pour mettre à jour l'affichage. Cela perturbe l'utilisateur qui peut croire, à tord, que le calcul est planté.

Les procédures topconsole et stop sont génériques, elles resterons identiques si elles devaient être utilisées dans un autre contexte.

5.Rendre une interface agréable à l'utilisateur

C'est bien de faire une interface qui fonctionne, c'est encore mieux elle est pratique pour l'utilisateur. Je ne suis pas ergonome, mais voici quelques astuces qui simplifient la vie.

5.1.Initialisation et préférences utilisateurs

Dans une interface il est souvent nécessaire d'initialiser certaines variables en fonction de l'ordinateur hôte, la plupart du temps pour préciser l'emplacement de certains composants (dll ou autres). La manière classique est de créer un fichier contenant les données de configuration (extension ini), puis dans le script lire le fichier. Avec Tcl/Tk cela peut être fait plus rapidement et de façon plus souple en "sourçant" le fichier.

Un exemple est donné plus haut dans une vraie interface. Le fichier "lmodel2.ini" est directement "sourcé".

L'ordre catch évite de fermer l'interface sur une erreur lorsque le fichier est absent, ce qui est très déstabilisant pour l'utilisateur. Dans ce cas des initialisation par défaut sont utilisées, ceci est une bonne pratique lorsqu'il est possible de les définir. Par exemple sous Windows le navigateur classique est "internet explorer" et il se trouve par "C:\\Program Files\\Internet Explorer\\iexplore.exe".

Attention, cette pratique est une porte d'entrée dans le script. Si des personnes mal intentionnées introduisent du code dangereux, celui-ci sera exécuté. J'utilise ce défaut pour activer une "rustine" de correction de l'interface sans avoir à tout réinstaller.

Dans le wiki anglais il y a les exemples "safesource" et "readprof" qui permettent de lire un fichier config dans un environnement sûr. Il est conseillé de les utiliser. safesource ne permet que d'initialiser des variables, certains ordres comme wm geometry .... ne seront pas interprétés.

J'utilise la même technique pour enregistrer et sauvegarder les préférences de l'utilisateur. Celles-ci incluent les noms des derniers fichiers utilisés comme dans lmodel2.cfg, en fait le ficher sauvegardé est un petit morceau de code Tcl qui initialise les variables internes de l'interface.

wm title . Model-II
wm geometry . 672x397+243+262

# last used file names
set nomfichdat "~/model-ii/data/E4_100x2.txt"
set nomfichres "~/model-ii/data/E4_100x2.res"
set exedir "~/model-ii"

# modele-ii parameters
set langue Anglais
set AMDC 0
set ordre 1
set permu 0
set nbligne 100
set cady 2
set cadx 2

# graphics titles
set mtitle "Diverses régressions"
set xlabel "données X"
set ylabel "données Y"

# summary variables
set variable1 "Explication données X"
set variable2 "meme chose pour Y"
Voici les procédures utilisées (cliquer S.V.P.)

my_exit remplace l'ordre exit, le nom du fichier cfg, en dur, est à adapter au contexte.

writeexist écrit le contenu d'une variable dans le fichier de préférence.

cfg_save sauve les préférences utilisateur sans quitter l'interface.

save_cfg écrit le fichier, celui ci contient directement des ordres Tcl/Tk qui sont spécifiques à chaque application. Il est appelé par les deux procédures précédentes.

cfg_read et cfg_read_safe "sourcent" un fichier déjà enregistré.

Il faut noter l'ordre uplevel est nécessaire pour évaluer le fichier dans le bon contexte lors de la lecture.

l'utilisation de writeexist dispense de déclarer les variables comme globales, mais sont principal avantage est de ne pas lever d'erreur si la variable n'existe pas. Ce cas arrive lorsque l'initialisation de l'interface n'est pas complète.

5.2.Outils pour construire l'interface

L'utilisation de package est un moyen fréquemment utilisé pour étendre les possibilités de Tcl/Tk. Une caractéristique des "packages" est d'être chargés en mémoire uniquement lorsque le script en a besoin. C'est bénéfique car ils ne pénalisent pas le temps d'affichage du premier écran de l'interface. De plus l'ordre package forget permet de limiter l'encombrement de la mémoire

Le système des packages suppose que ceux ci soient placés dans des dossiers spécifiques. Sur la machine du développeur cela ne pose en général pas de difficulté. Par contre, lors que la distribution de l'interface, cela peut être gênant (et même rédhibitoire) si ces dossiers ont un droit d'accès restreint à l'administrateur uniquement.

Le truc lappend auto_path [file dirname [info script]] permet de le placer dans le même dossier que le script principal. Ce qui facilite l'installation, mais, cela ne fonctionne pas à tous les coups. La solution de dépannage est de "sourcer" les packages comme si ils étaient des scripts classiques.

La version 8.5 de Tcl/Tk introduit une nouvelle classe de biblothèque, tcl module qui peut simplifier la gestion.

Voici quelques packages que j'utilise

Nom Utilité
safesource charger la configuration du script sans danger
htmldoc faire un fichier html à partir plus simple, ne pas confondre avec l'utilitaire qui permet de créer des notices pdf à partir de fichier html
Ne semble plus disponible
trampoline faire un fichier pdf type "copie/écran" d'un canvas
helpsystem système d'aide en Tcl/Tk, mais je préfère faire l'aide directement en html et appeler un navigateur
tinycombobox comme son nom l'indique, il existe plusieurs autres packages qui construisent un combobox
stext widget texte avec ascenseur. Il est très compact, aussi autant utiliser ce package même si il est facile de faire sont propre code
progressbar encore un widget qui manque à Tk, mais qui est facilement remplacé par des implantations en Tcl/Tk
efftcl ensemble d'outils divers
lcd émule l'affiche digital de nombre comme sur une montre (un peu gadget)

Il existe de nombreuses collections de procédures qui facilitent la construction de la partie graphique de l'interface ou comblent des lacunes dans les widgets de base de Tk. Certaines sont assez complètes comme [BLT] ou [itcl] et permettent de faire facilement un interface au look professionnel. Je ne les utilise pas, principalement par méconnaissance, de plus souvent seule une petite partie est réellement utilisée. Ce sont en général des packages qui compliquent l'installation chez l'utilisateur. Mais je reconnais une part de subjectivité sur ce sujet, je préfère connaître tous les recoins des codes que j'utilise.

5.3.Autres astuces diverses

Le menu $menu.system (où $menu est le nom de la barre des menus) est traité prédéfini par le système. Pour le rendre accessible par un clic droit sur le cadre supérieur de la fenêtre, il suffit d'ajouter les lignes suivantes.

menubutton $bm.system                                         ;# Default system menu
pack $bm.system -side left

Il est souvent utile d'avoir plusieurs pages dans une interface. Il y a trois façons de procéder:

Voici ma propre implantation de multipages

Il reste à écrire le contenu de chaque page. Chacune est contenue dans une frame dont les noms viennent d'être définis:

zlanc pour la page 1
dedit pour la page 2
etc

5.4.Internationalisation avec msgcat

La façon naturelle d'internationaliser un code Tcl/Tk est le package msgcat. Il est bien fait et pratique et j'ai pris l'habitude de d'écrire les textes de l'interface en anglais, et de n'effectuer la traduction que au moment de la livraison (release). Mais je prépare ce travail en encadrant les messages [::msgcat::mc {English text}], bien sur English text est adapté au contexte.

Mais j'ai une petite réticence avec l'ordre mcload qui charge un fichier de messages. En fait ce fichier est "sourcé" ce qui crée une porte d'entrée dans votre script, de plus le mélange entre les commandes Tcl et les messages peut créer des confusions pour le traducteur.

J'utilise la procédure suivante

Cette procédure recherche un fichier appelé fr.msg pour les ordinateurs configurés en français (ou de.msg en allemand, es.msg en espagnol, ...) et charge les messages. Ces fichiers sont plus simples car ils ne contiennent que les messages originaux et leurs traductions, une par ligne.

Exemple: fr.msg
"About"                    "A propos"
"File"                     "Fichier"
"Run"                      "Execute"
"Read configuration"       "Lire config"
"Read"                     "Lire"
"Help"                     "Aide"
"Save configuration"       "Enregistre config"
"Save as"                  "Enregistre sous"
"Close"                    "Fermer"
"Read data File"           "Lit fichier de donnees"
"Output file"              "Fichier sortie"
"Change directory"         "Change de dossier"
"Exit"                     "Quitter"
"Not a valid file name !"  "Ce n'est pas un nom de fichier valide !"
"Start computation"        "Lance le calcul"
"Save data file (overwrite existing file)" "Enregistre fichier dat (écrase le précédent)"
"Save data in a new file" "Enregistre dans un nouveau fichier"

Dans le wiki anglophone j'ai trouvé deux utilitaires msgcat-ready et msgcatmagic qui permettent de vérifier que on a bien traduit tous (ou presque) les textes affichés dans l'interface.

Ce qui vient d'être décrit est relativement artisanal et surtout mono développeur. La page du wiki francophone Utiliser msgcat avec gettext donne des astuces pour utiliser les outils standards de traduction comme gettext.

5.5.Changer de langue à la volée

Il est relativement facile de changer de langue du script avec l'ordre configure ou entryconfigure (pour les menus).

Exemple: pour changer vers la langue $lang
    msgcat::mclocale $lang

# change menu principal
    set bm .wmain.menu
    foreach mmenu {file exec edat lang help} mtitre {File Run Edit Language Help} {
        $bm.$mmenu configure -text [msgcat::mc $mtitre]
    }

# menu langue
    set mmenu lang
    foreach i {1 2 3 4} langlab {English French German Spanish} {
        $bm.$mmenu.menu entryconfigure $i -label [msgcat::mc $langlab]
    }

# les boutons
    set fpage .wmain.graph
    foreach pwidget {labmtit labX labY grid xrange yrange date} \
            wtext {{Main title} {X label} {Y label} {grid} {Auto xrange} {Auto yrange} } {
        $fpage.$pwidget configure -text [::msgcat::mc $wtext]
    }
# les listbox
    set xlist [list [::msgcat::mc "Time"] [::msgcat::mc "Distance"] ]
        $fpage.labXent configure -value $xlist

Notez l'utilisation de la boucle foreach qui permet une écriture plus concise. Cette façon de faire ne peut pas être automatisée, ce qui demande une grande attention pour lister tous les éléments de l'interface qui ont besoin d'être modifiés.

Une personne dans le wiki.tcl francophone propose de mettre tous les noms de widget dans une liste lors de la construction. C'est sans doute une bonne idée.

5.6.Aide, et bulles d'aide

Il existe plusieurs outils pour appeler une documentation hypertexte. Certains outils sont plus spécifiques Unix et d'autres Windows. Il est plus sage de l'écrire dans un format commun à ces deux mondes (sans oublier Mac). C'est pourquoi j'utilise le HTML ou PDF et j'appelle directement les lecteurs/navigateurs natif du système.

ici exemple d'appel de lecteurs HTML

Voici une autre version plus générale sur l'appel des navigateurs. Par contre elle peut poser problème si le nom du fichier comporte des espaces (Windows).

seconde version

Plusieurs auteurs utilisent le package tkhtml. Il permet d'afficher des fichiers html dans une structure en pur Tcl. Je ne l'ai jamais utilisé, car je trouve la méthode précédente plus simple. Le principal intérêt est que la notice garderait le même "look" que le reste de l'application.

Une autre alternative est HelpSystem d'Andrei Gratchev écrit en pur Tcl et qui est facilement portable. Le code source est libre donc on peut l'ajuster à ses besoins. Le résultat est de bonne qualité à condition de passer un peu de temps pour peaufiner la notice.

Un dernier point, les bulles d'aide sont aussi une aide utile. A condition que le message affiché ne se contente pas de répéter le texte du widget ! Il existe de nombreuses implémentations de bulle d'aide en TCL.

6.Documenter avec doxygen

Le tcl est prévu dans la liste de language supporté avec doxygen. Il y a un exemple dans doxygen+tcl. Mais l'exemple est adapté à la documentation des "class itcl".

C'est d'ailleurs une remarque générale pour doxygen qui est plus adapté à la documentation de API (interface de programmation) que d'un programme complet.

De plus, je trouve l'exemple compact et pas très lisible en affichant le code source.

Avec la version actuelle, il n'est pas possible d'ajouter des commentaires à l'intérieur du script de démarrage. Du moins doxygen les ignorera. La seule façon est de déporter les commentaires dans le cartouche initial.

Il est toutefois possible de bien documenter la page principale, les fichiers et les procédures voir les fichiers exemples.


Retour à la première page. Dernière révision le 26/Dec/2014