Une belle calculatrice

 

Parfois on trouve de vrais petits bijoux, des petits programmes qui ont une beauté ou un élégance rare. Exemple d'un tel bijou, ce "script" de Richard Suchenwirth nous donne une "calculatrice" en moins que 50 lignes :

  wm title . Calculator
  grid [entry .e -textvar e -just right] -columnspan 5
  bind .e  =
  set n 0
  foreach row {
     {7 8 9 + -}
     {4 5 6 * /}
     {1 2 3 ( )}
     {C 0 . =  }
  } {
     foreach key $row {
         switch -- $key {
             =       {set cmd =}
             C       {set cmd {set clear 1; set e ""}}
             default {set cmd "hit $key"}
         }
         lappend keys [button .[incr n] -text $key -command $cmd]
     }
     eval grid $keys -sticky we
     set keys [list]
  }
  grid .$n -columnspan 2 ;# make last key (=) double wide
  proc = {} {
     regsub { =.+} $::e "" ::e ;# maybe clear previous result
     if [catch {lappend ::e = [set ::res [expr 1.0*$::e]]}] {
         .e config -fg red
     }
     .e xview end
     set ::clear 1
  }
  proc hit {key} {
     if $::clear {
         set ::e ""
         if ![regexp {[0-9().]} $key] {set ::e $::res}
         .e config -fg black
         .e icursor end
         set ::clear 0
     }
     .e insert end $key
  }
  set clear 0
  focus .e           ;# allow keyboard input
  wm resizable . 0 0

Pourquoi peut-on dire que c'est un bijou ? Notamment grâce à cette petite partie :

  foreach row {
     {7 8 9 + -}
     {4 5 6 * /}
     {1 2 3 ( )}
     {C 0 . =  }
  } {
     ...
  }

On voit dans le code lui-meme le clavier de la "calculatrice" - voyez la capture de la fenêtre de l'application ci-dessous :

Mais, revenons au départ :

  wm title . Calculator
  grid [entry .e -textvar e -just right] -columnspan 5
  bind .e  =

Ces lignes définissent : le titre de la fenêtre ; un champ d'entrée (nomme ".e") qui est positionné dans la fenêtre à l'aide d'un grille (une methode basique de placement des "widgets"). Dans ce cas le champ s'étend sur 5 colonnes. Notez que ce champ d'entrée est lié à une variable globale, qui s'appelle "e" (les deux-points :: indiquent qu'il est global). Quand on change le contenu du champ, la valeur de cette variable changera automatiquement - et vice versa ! Finalement, taper au clavier sur la touche "Entrée", ici "Return", invoquera une action sous l'effet de la commande "bind". Chaque événement peut être associé à des actions, dans notre cas : exécuter la procedure "=". Parce que Tcl est très généreux, les noms de variable et de procedures peuvent être des noms symboliques comme, ici, le symbole d'égalite ou, plus exactement, d'assignation d'une valeur. Le code apres la double itération "foreach" définit deux procédures : La procédure "=" éclaircira premierement le champ d'entree en enlevant d'anciens résultats, ensuite il "calcule" le resultat de l'expression courante présente dans le champ d'entree (et donc qui est la valeur de la variable "e") à l'aide de la commande "expr". Le résultat du calcul est ajouté au champ d'entrée :

    1+2+3=6.0

La difficulté des divisions par zero, des logarithmes de nombres négatifs ou autres impossibilités, est gérée de manière simple : si la commande "expr" emet une erreur, la commande "catch" renverra la valeur 1, sinon 0. Donc, en cas d'erreur numérique, le texte apparaîtra en rouge. La procédure "hit" est bien plus simple : si une touche est tapée sur la clavier, la procédure ajoute le caractère correspondant à la valeur de la variable "e" (et donc le champ d'entrée est automatiquement actualisé) :

    .e insert end $key

Les commandes l'entourant garantissent seulement la coloration noire et le bon affichage du calcul après une éventuelle erreur. Maintenant la double itération: On voit une liste de liste qui définit les lignes ("rows") de bouton. La première itération prend un "row" et le passe à la deuxième itération : pour chaque élément de la liste, un bouton est créé auquel on associe une commande propre (qui se trouve dans la variable cmd) :

     switch -- $key {
         =       {set cmd =}
         C       {set cmd {set clear 1; set e ""}}
         default {set cmd "hit $key"}
     }
     lappend keys [button .[incr n] -text $key -command $cmd]

L'iteration stocke alors le nom du bouton dans une liste. Apres cette itération on voit:

    eval grid $keys -sticky we

La commande "eval" remplace toutes les variables par leur valeur et exécute la commande résultante de cette substitution : la liste $keys est remplacée par tous les éléments qui y sont contenus et la commande "grid" les ajoute dans la grille dont on a parlé au début du programme. (L'option "-sticky we" signifie que les boutons "collent" à gauche (ouest) et à droite (est) aux cellules de la grille). Et voila: une "calculatrice" fonctionnelle en moins de 50 lignes !