plot of chunk
Lise_Vaudor_headband

EDIT: Ce billet date d'une époque où je n'avais pas encore découvert la magie de Shiny... Désormais, je conseillerais à quiconque souhaite faire une petite interface interactive sous R d'aller voir de ce côté là... Il y a déjà des tutoriels très bien faits ici.

Ce billet vise à vous montrer comment construire une interface "clique-boutons" pour R.

Pour un utilisateur régulier de R, le recours à une telle interface est plutôt "gadget" (ou en tout cas d'un rapport utilité/temps d'écriture très peu intéressant). MAIS il peut se révéler utile de fournir une telle interface à un utilisateur novice de R, auquel on souhaite permettre d'utiliser des scripts R sans qu'il ait à mettre le nez dedans...

Exemple: construction d'une interface simple

Pour ce billet, je vais montrer, à travers un exemple, comment construire une petite interface à l'aide du package tcltk.

Il s'agira de faire un mini-jeu consistant à deviner un chiffre (tiré au hasard par l'ordinateur) en faisant le moins d'essais possibles. Le joueur sera guidé par des indications de type "Le nombre est plus grand"/"Le nombre est plus petit".

Soyons clairs, on a déjà vu des jeux plus amusants (par exemple - message subliminal - Zelda : A Link between Worlds sous Nintendo 3DS). Mais bon, commençons notre carrière de développeur informatique doucement.

L'interface, in fine, aura l'apparence suivante:

Interface tcltk sous
R

Elle est composée de divers composants d'interface graphiques (les widgets). Ici, elle est composée en particulier:

  • de plusieurs courts textes (labels)
  • d'une boîte permettant à l'utilisateur d'entrer une chaîne de caractère (entry)
  • d'un bouton poussoir "OK" (button)

Voici comment construire l'interface de base:

#décommenter la ligne ci-dessous pour installer le package
#install.packages("tcltk")
require(tcltk)
base=tktoplevel()
tkwm.title(base, "supergameR")

A ce stade, l'interface est totalement vide.

On va pouvoir afficher les widgets dans un cadre (frame). On crée donc un premier frame, fr1.

On le remplit simplement avec un court texte (un label).

# creation et affichage du frame
fr1=tkframe(base)
tkpack(fr1)

# creation et affichage du label
t1=tklabel(fr1, text="SupergameR, a super game on R!")
tkpack(t1)

La fonction tklabel permet de créer le widget, mais ne l'affiche pas directement dans l'interface. C'est l'appel de la fonction tkpack qui permet de l'afficher dans l'interface.

A ce stade, l'interface ressemble simplement à ceci:

Interface + 1
frame

On crée un deuxième frame, fr2. On va le remplir avec un court text (label) et avec une boîte de saisie (entry).

# creation et affichage du deuxieme frame
fr2=tkframe(base)
tkpack(fr2)

# creation et affichage du deuxieme label 
t2=tklabel(fr2,text="Find mystery number (between 0 & 100)")
tkpack(t2, side="left")

# creation et affichage de l'entry
v_prop_n=tclVar("")
e1=tkentry(fr2, textvariable=v_prop_n)
tkpack(e1,side="right")  

A ce stade, l'interface ressemble à ceci

Interface +
entry

Le widget de type entry est associé à une variable tcl (il s'agit de la variable correspondant à la valeur écrite par l'utilisateur dans le widget).

Pour créer le widget, il faut donc commencer par créer cette variable (qu'on a appelé ici v_prop_n pour "variable correspondant à la proposition de nombre"). Ici, on a initialisé cette valeur pour qu'elle soit non renseignée a priori. Si l'on avait indiqué v_prop_n=tclVar("50"), alors la valeur par défaut proposée dans l'interface serait 50:

Interface + 1 frame + defaut
50

On peut récupérer la valeur de cette variable v_prop_n à travers la commande:

tclvalue(v_prop_n)

Vous pouvez faire le test consistant à:

  • taper une valeur dans l'interface que vous avez créée
  • vérifier la valeur renvoyée par la commande ci-dessus si vous l'exécutez dans l'invite de commande R

Enfin, créons le troisième et dernier frame, qui contient le bouton poussoir (button). L'activation du bouton poussoir lance un certain nombre de processus, qui sont regroupés dans une fonction R. Ici, le bouton "OK" va être lié à une fonction que j'ai appelé ok.

Il faut donc écrire la fonction ok avant de créer le bouton poussoir... C'est l'étape la plus délicate!

# creation et affichage du troisieme frame
fr3=tkframe(base)
tkpack(fr3)

# tirage au hasard d'un nombre entier entre 0 et 100
n=sample(0:100,1)

# nombre d'essais
k=0

# fonction à executer lorsque l'utilisateur appuie sur le bouton "OK"
ok=function(){
  ## Récupère la valeur de la variable v_prop_n
  prop_n<<-as.numeric(tclvalue(v_prop_n))
  ## Compare cette valeur à la valeur de n
  if(prop_n<n){ans="No! it's more!"}
  if(prop_n>n){ans="No! it's less!"}
  if(prop_n==n){ans="Yes! exactly!"}
  ## Augmente le nombre d'essais de 1
  k<<-k+1
  ## Crée un texte comprenant 
  ## d'une part l'indication "plus grand ou plus petit"
  ## d'autre part le nombre d'essais.

  ans=paste(ans,"(number of trials:", k, ")", sep="")
  ## Si une telle étiquette a déjà été créée lors d'un essai précédent, 
  ## elle est détruite 
  if(exists("lab", env=globalenv())){tkdestroy(lab)}
  ## L'etiquette est créée et affichée
  lab<<-tklabel(fr3,text=ans)
  tkpack(lab)
}
okbutton=tkbutton(fr3, text="OK", command=ok)
tkpack(okbutton)    

Notez l'usage du symbole <<- à la place de = ou <-. Il est lié à la notion d'environnement (c'est une notion un petit peu compliquée à expliquer comme ça entre la poire et le fromage, mais je vais quand-même en dire quelques mots en attendant de faire un billet sur le sujet).

Sous R, un environnement est en quelque sorte l'"endroit" comprenant les objets. Par défaut, la plupart des objets créés sont stockés sous l'environnement "de base". Les objets créés lors de l'exécution d'une fonction font exception (un nouvel environnement local et temporaire est créé à chaque exécution de la fonction). Le symbole <<- permet de passer outre cette propriété et de créer les objets dans l'environnement de base.

Pour aller plus loin

Il existe beaucoup d'autres types de widgets que je n'ai pas abordés ici: radiobuttons, checkbuttons, menus, listboxes, etc. Il existe également une infinité de manière de modifier l'arrangement géométrique et les propriétés graphiques (taille, couleur, polices, etc.) de votre interface.

Ici, par souci de simplicité, je n'ai pas abordé ces aspects, mais vous pouvez trouver tout un tas d'exemples complémentaires sur le net...

  • ici (cours de Phil Spector)
  • ici (collection d'exemples créée par James Wettenhal et maintenue par Philippe Grosjean)