Tutoriel:Éviter des tâches répétitives en éditant le code XML

De GeoGebra Manual
Aller à : navigation, rechercher

S'éviter des tâches répétitives en éditant le code XML

Objectif

Ce tutoriel est né d'une appliquette qui m'a fait créer une quarantaine de points, tous munis d'un script au clic : il s'agit d'un boulier où l'on peut interagir dans deux sens :

  • On choisit un entier grâce à des boutons et une zone de texte (entier tapé, entier choisi au hasard, entier choisi dans une liste) : le boulier affiche alors cet entier.
  • On clique sur les boules du bouliers pour les déplacer, l'entier correspondant s'affiche alors en écriture décimale.

Chaque boule est représentée par un (gros) point muni d'un script; le but de ce tutoriel est de présenter un moyen de rentrer cette quarantaine de scripts sans devenir fou ! Un bon éditeur de texte et de la concentration suffisent...

Le boulier présente plein d'avantages pour enseigner la numération en école primaire ou au début du collège : il permet de plus des exercices de calcul mental, et permet d'apprendre à poser les additions et les soustractions. Mais cela revient beaucoup moins cher de disposer d'un boulier GeoGebra plutôt que d'acheter autant de bouliers que d'élèves ! Le boulier choisi est de type Soroban, avec sur chaque tige, 4 boules valant l'unité (en bas) et une boule valant 5 (en haut). Les boules sont activées lorsqu'elles sont poussées contre la barre centrale. Voici le produit fini.

J'ai fait une première version du boulier où les boules étaient gérées par des listes de points. Il est assez facile de déterminer quelles boules doivent être activées en fonction de l'entier à afficher. C'est un peu de manipulation de la division entière. Ce faisant on arrive facilement à coder la première partie de l'interaction: on choisit un entier, on définit trois listes dépendant de cet entier: les boules du bas activées, les boules du bas désactivées, les boules du haut . Quelques commandes Séquence permettent d'afficher les points correspondants au bon endroit et le tour est joué (voir la section suivante). Mais coder l'interaction dans l'autre sens (déplacer les boules pour changer l'entier) demande de récupérer, dans un script, sur quel élément d'une liste on a cliqué. Or, j'ai cherché, demandé dans les forums, et sauf erreur de ma part, cette fonctionnalité n'existe pas dans GeoGebra. Il a donc fallu faire une version où chaque boule est un point indépendant muni d'un script. D'où le problème: comment éditer facilement un grand nombre de scripts, qui se ressemblent tous à des différences d'indice prêt? En l'absence d'une commande 'SoitScript', on est obligés de passer par le menu propriété->scripts et de rentrer tous les scripts un par un, soit un nombre considérable de manipulations à la souris. Il faut être particulièrement zen et patient pour mener cette tâche sans oublier une boule ou sans se tromper sur l'édition d'un script.

Il y a heureusement moyen de modifier un fichier GeoGebra via un éditeur de texte : on édite en fait un fichier XML à la structure relativement claire. On rentre alors les scripts directement dans le code XML à l'aide de votre éditeur de texte favori, dont vous maîtrisez la fonction 'chercher/remplacer'.

Les sections qui suivent présentent :

  • la mise en place du boulier avec des listes de points
  • l'adaptation, à l'aide de commandes "Exécute" de cette première version pour obtenir des points indépendants.
  • l'édition des scripts via le code XML.

Première mise en place avec des listes de points

La figure ci-dessous illustre le rendu graphique souhaité :

Boulier interactif disposition.png

  • La tige des unités est en x=6, celle des dizaines en x=5, ..., celle des centaines de mille en x=1.
  • Pour les boules du haut : elles sont activées en y=6, désactivées en y=7.
  • Pour les boules du bas : elles occupent les ordonnées entières, de y=0 (contre la barre du bas) à y=4 (contre la barre centrale).

Si n est l'entier à afficher (0 <= n <= 999999) , on peut obtenir sa liste de chiffres par : listechiffres=Retourner[Séquence[Quotient[Reste[n,10^i], 10^(i - 1)], i, 1, 6]].

Liste pour les boules du bas : listebas=Séquence[Reste[Elément[listechiffres, i], 5], i, 1, 6]. Liste pour les boules du haut : listehaut=Séquence[Quotient[Elément[listechiffres, i], 5], i, 1, 6]. Ces listes indiquent, pour chaque tige, combien de boules sont activées en bas et en haut.

Liste des boules du haut : listeptshaut=Séquence[(i, 7- (Elément[listehaut, i])), i, 1, 6] : ainsi les boules du haut sont positionnées soit en (i,7) (pas de boule activée pour la ième tige) soit en (i,6) (boule activée pour la ième tige).

Liste des boules du bas activées : listeptsbas1=Séquence[Séquence[(i, 5-j), j, 1, Elément[listebas, i]], i, 1, 6] : ainsi les boules du bas activées de la ième tige sont placées en (i,4), (i,3), (i,2), (i,1).

Liste des boules du bas désactivées : listeptsbas2=Séquence[Séquence[(i, j-1), j, 1, 4 - Elément[listebas, i]], i, 1, 6] : ainsi les boules du bas désactivées de la ième tige sont placées en (i,0), (i,1), (i,2), (i,3).

La mise en place est terminée, ainsi que l'interaction entier décimal vers boulier : il suffit de changer la valeur de n pour que les boules prennent automatiquement la bonne position : à vous de créer des boutons et zones de textes pour régler n, et de dessiner le boulier. Il reste encore à régler l'interaction dans l'autre sens.

Les limites des scripts pour les listes

Recréer la construction avec la commande Exécute

On peut associer un script (au clic, à l'actualisation) à une liste d'objets. Pour, par exemple, un script au clic, l'effet est que le script sera activé dès lors qu'on clique sur un élément de la liste. Mais, sauf erreur de ma part, il n'existe pas de moyen de récupérer dans le script sur quel élément de la liste on a cliqué.

Or, mon idée était que lorsqu'on clique sur une boule (par ex : une boule désactivée du bas), on modifie par script la valeur de n: si cette boule est la première du tas, alors on fait n=n+1 (tige des unités), n=n+100 (tige des centaines), et l'affichage suit automatiquement via les listes dépendantes de n. Mais pour cela il faut savoir :

  • sur quelle tige on a cliqué (indice i des séquences)
  • pour les boules du bas, savoir si on a cliqué sur un élément qu'on peut bouger : celui du bas pour les boules activées, celui du haut pour les boules désactivées.

Je vais donc refaire l'application avec, au lieu de listes, des nombres et points indépendants créés avec la commande Exécute. Cela permet de réutiliser le travail fait avec des listes, mais en créant des points indépendants. Pour faciliter le copier/coller ultérieur sous XML, je crée des noms logiques : la liste de chiffres (listechiffres) devient ainsi n1, n2, n3 ... par la commande :

  • Exécute[ Séquence[ "n_"+(7-i)+" = Div[ Mod[ n , 10^"+(i)+"] , 10^"+(i-1)+" ]" , i , 1, 6 ]]

La liste du nombre de boules activées en haut devient les nombres nh1, nh2, ... par :

  • Exécute[ Séquence[ "nh_"+i+" = Div[n_"+i+",5]" , i , 1, 6 ]]

La liste du nombre de boules activées en bas devient les nombres nb1, nb2, .... par :

  • Exécute[ Séquence[ "nb_"+i+" = Mod[n_"+i+",5]" , i , 1, 6 ]]
Note : La commande Exécute prend en argument une liste de chaînes de caractères, qu'elle fait exécuter par GeoGebra. Je suis donc parti d'une commande Séquence initiale, qui crée une liste, j'ai mis entre guillemets son premier argument, j'ai rajouté n= au début, puis à chaque fois que la valeur de i doit apparaître, je brise la chaîne et j'utilise l'opérateur + (concaténation de chaînes).
Note : Il faut faire attention que les commandes arguments de Exécute (donc ce qui est entre guillemets) doivent être en anglais : Quotient est ainsi devenu Div, Reste est devenu Mod. Utiliser le wiki, section commandes, en français pour trouver votre commande, puis changer la langue (menu en haut à droite de la page) pour avoir son nom anglais.

Ces trois commandes créent, au lieu de trois listes de nombres, les nombres n1, n2, ..., nh1, nh2, ..., nb1, nb2, ...

On peut alors placer les boules : dans la version initiale, c'étaient des listes de points, maintenant cela doit être des points indépendants. Je vais d'abord créer la liste de la première tige (centaines de mille), et la dupliquer dans le code XML.

Ces points s'appelleront A0 (boule du haut), A1 ... A4 (boules du bas). Je crée ces 5 points comme points de la tige puis je change leurs valeurs avec SoitValeur.

SoitValeur[A_0,(1,7-nh_1)]

SoitValeur[A_1,(1,Si[nb_1==0,3,4])]

SoitValeur[A_2,(1,Si[nb_1<=1,2,3])]

SoitValeur[A_3,(1,Si[nb_1<=2,1,2])]

SoitValeur[A_4,(1,Si[nb_1<=3,0,1])]

Note : On aurait pu faire ça avec une commande Exécute/Séquence ... mais ça devient barbare ! Par ailleurs, pourquoi d'abord créer les points puis les modifier avec SoitValeur, au lieu de les créer directement avec = ? J'ai envie que ces points aient comme définition : point de la tige, et que l'utilisateur ne puisse pas les changer de tige. SoitValeur se contente de modifier la valeur, pas la définition initiale.

Créer les premiers scripts

Je crée alors les scripts de chacun de ces points à la main. J'ai choisi un script par clic, ce qui implique qu'il faut cliquer sur les points et non les déplacer. Je cherche encore un moyen d'empêcher leur déplacement : si on les rend non sélectionnables, on ne peut plus cliquer dessus...

Voici les scripts à rentrer :

  • point A0 : SoitValeur[ n , Si[ y(A_0)==6 , n-500000 , n+500000 ]]
  • point A1 : SoitValeur[n , Si[y(A_1)==3,n+100000,Si[y(A_1)==4 ∧ y(A_2)==2,n-100000,n] ] ]
  • point A2 : SoitValeur[ n , Si[ y(A_2)==2 ∧ y(A_1)==4, n+100000 ,Si[y(A_2)==3 ∧ y(A_3)==1 , n-100000 , n] ] ]
  • point A3 : SoitValeur[ n , Si[ y(A_3)==1 ∧ y(A_2)==3, n+100000 ,Si[y(A_3)==2 ∧ y(A_4)==0 , n-100000 , n] ] ]
  • point A4 : SoitValeur[ n , Si[ y(A_4)==0 ∧ y(A_3)==2, n+100000 ,Si[y(A_4)==1 , n-100000 , n] ] ]

Ces scripts modifient l'entier n, qui, via le dynamisme des constructions de GeoGebra, actualise la position des points A0 ... A4.

  • Pour le point A0, l'interaction est simple, si la boule est activée (y(A0)=6), retirer 500000, sinon ajouter 500000.
  • Pour le points suivants, par exemple A2 : on vérifie qu'on peut le bouger vers le haut (y(A2)=2 et y(A1)=4)), si oui on augmente n de 100000, sinon on vérifie qu'on peut le bouger vers le bas ( y(A2)=3 et y(A3)=1), si oui on diminue n de 100000, sinon on laisse n inchangé (boule non déplaçable). Il y a un test de moins pour A1 et A4, qui sont des boules qui n'ont qu'un voisin.

Restaurer le dynamisme de la construction

Je dis plus haut qu'il suffit de changer la valeur de n pour que les boules se positionnent correctement. C'est vrai de la première version avec liste, qui sont réellement dépendantes de n. Avec la commande Exécute, on a créé des points indépendants, mais qui du coup ... ne dépendent plus de n. Si n change, ils ne sont pas mis à jour, et donc les boules ne bougent pas. Il suffit de rajouter des scripts à l'actualisation des chiffres de n. Par exemple, je rajoute un script d'actualisation à n1 :

  • Mise à jour du nombre de boules activées (bas et haut) : nb_1=Reste[n_1,5] et nh_1=Quotient[n_1,5].
  • Mise à jour de l'emplacement des boules :

SoitValeur[A_0,(x_1,7-nh_1)]

SoitValeur[A_1,(x_1,Si[nb_1==0,3,4])]

SoitValeur[A_2,(x_1,Si[nb_1<=1,2,3])]

SoitValeur[A_3,(x_1,Si[nb_1<=2,1,2])]

SoitValeur[A_4,(x_1,Si[nb_1<=3,0,1])]

Ces scripts sont aussi à dupliquer dans le fichier XML.

Dupliquer à l'aide du code XML

On peut alors ... refaire la même chose pour toutes les tiges (ouf ! autant de scripts que de points...) ou essayer de tirer parti de la logique de notation pour appliquer en masse du copier/coller. Une commande 'SoitScript' permettrait d'entrer ces scripts via une commande Exécute/Séquence et quelques aspirines, mais en l'absence de cette commande, on peut attaquer le code XML. La procédure est la suivante (les commandes sont celles de Linux, mais il suffit d'adapter) :

  • Sauver la construction, fermer GeoGebra et faire une copie du fichier GeoGebra en fichier zip : cp boulier.ggb boulier.zip. Bien garder le fichier ggb original, il servira de sauvegarde en cas d'inévitable fausse manœuvre ! En particulier, je déconseille fortement d'utiliser la possibilité offerte par les systèmes d'exploitation de modifier directement un fichier.zip sans le décomprimer au préalable. Vous prenez le risque de perdre votre travail si vous avez laissé GeoGebra ouvert.
  • Utiliser le logiciel de décompression de votre choix: unzip boulier.zip
  • On se trouve alors (éventuellement dans un sous-dossier boulier) avec plusieurs fichiers, dont un s'appelle geogebra.xml. On a aussi une image geogebra_thumbnail.png, c'est la prévisualisation de votre appliquette; un fichier geogebra.js qui contient les éventuelles scripts javascript global. C'est le fichier XML qu'on va éditer. Aller dedans et repérer les objets à dupliquer. C'est une structure assez claire, les objets sont des balises xml element et command. Ainsi, pour la définition du point A0, on trouve dans le fichier XML :

<command name="Point"> <input a0="a_1"/> <output a0="A_0"/> </command>

<element type="point" label="A_0"> <show object="true" label="false"/> <objColor r="0" g="0" b="0" alpha="0.0"/> <layer val="0"/> <labelMode val="0"/> <animation step="1" speed="1" type="1" playing="false"/> <ggbscript val="SetValue[ n , If[ y(A_0)==6 , n-500000 , n+500000 ]]"/> <coords x="1.0" y="7.0" z="1.0"/> <pointSize val="9"/> <pointStyle val="0"/> </element>

Interprétation : A0 est un point de l'objet a1 (la tige 1) : c'est la balise commande, la définition du point. Puis on a ses propriétés dans la balise élément. Dans l'ordre : l'étiquette du point, sa visibilité et celle de son étiquette, sa couleur, son calque, le type d'étiquette, l'animation, le script, l'emplacement, la taille et le style du point. Ce qui est intéressant est que son script apparaît en clair, chaque commande étant séparée des autres par un simple espace : c'est la balise ggbscript.

Il suffit alors de:

  • prendre une grande respiration...
  • modifier par copier coller le fichier xml : j'ai personnellement créé un nouveau fichier où j'ai extrait, depuis le fichier XML de départ, les informations de la tige (points A0 A1 A2 A3 A4, donc 5 balises command et 5 balises element); je fais les modifications dans ce fichier que je réinjecte dans le fichier de départ. C'est un peu répétitif mais en utilisant bien le copier coller et la fonction 'remplacer' des éditeurs de texte, ça va assez vite. Ici, il faut remplacer A_ par B_ (puis C_, D_, ...), idem pour les a_, changer les 00000 en 0000 (tige 2), puis 000 (tige 3), 00 (tige 4), 0 (tige 5), "" (tige 6), et enfin changer les positions (x= dans la balise coords). Une fois les modifications faites, on les insère dans le XML initial en respectant la structure, c'est-à-dire, en faisant attention de ne pas insérer au beau milieu d'une balise ! Insérer les nouvelles tiges immédiatement à la suite de l'ancienne suffit.
  • procéder de même pour les scripts d'actualisation des chiffres de n.

Une fois le fichier XML modifié, recréer le fichier ggb: il suffit de comprimer en zip les fichiers de départs, SANS OPTION DE COMPRESSION PARTICULIERE -tout laisser par défaut- renommer le zip en ggb, et le tour est joué ! Exemple sous linux:

zip boulier-modif.zip geogebra.xml geogebra.js geogebra_thumbnail.png

mv boulier-modif.zip boulier-modif.ggb

Il faut faire très attention, les manipulations du fichier XML sont toujours délicates et il n'y a pas vraiment d'outil de débogage. En cas de fausse manipulation, il peut arriver que:

  • le fichier ggb n'est pas reconnu par GeoGebra. Un message d'erreur vous dit que le fichier est corrompu, mais ne vous dit pas pourquoi.... Mon conseil est de tester à chaque modification. Ainsi, en cas d'erreur, vous pouvez supposer que cela vient de la dernière modification. Un petit control-Z dans l'éditeur vous remet dans la dernière bonne version, reprenez une aspirine et vérifiez puis refaites la modification...
  • le fichier ggb est lu mais vous n'y trouvez pas les modifications faites sur le XML. Vérifiez tout de même que vous ouvrez le bon fichier, mais même si c'est le cas, ce cas de figure peut se présenter. Je suppose que cela arrive quand on rentre un code syntaxiquement OK mais non exécutable par GeoGebra. Même conseil que précédemment !
  • si vous aviez laissé GeoGebra ouvert et que vous sauvegardez à nouveau, vos modifications sont perdues. En règle générale, quand on modifie hors GeoGebra les fichiers ggb, il est préférable de fermer GeoGebra et de faire une copie de sauvegarde de votre travail. Cela m'est arrivé plusieurs fois, c'est juste énervant car on dispose toujours du fichier XML. Il suffit de recréer le fichier ggb.

Si vous voulez vraiment comprendre le code XML, le format est décrit dans la page référence XML. Dans l'exemple présenté, cela a été inutile, la structure du code XML étant très facile à décoder.

Conclusion

Je ne prétends pas avoir trouvé la meilleure solution pour le boulier. Mais ce tutoriel donne au moins une manière de dupliquer/éditer en masse des objets et leurs scripts, ce que ne permet pas le copier-coller de GeoGebra.

Dans cet exemple, j'ai rentré 6 scripts à la main, en prenant bien le temps de les tester: 5 boules de la première tige et le script d'actualisation du premier chiffre. J'ai ensuite utilisé le fichier XML pour copier ces scripts et les adapter pour chacune des 6 tiges, soit au total 36 scripts.

J'ai donc évité de rentrer 30 scripts à la main ! Maintenant, ai-je gagné du temps? La première fois qu'on le fait, certainement pas, tout au plus a-t-on la sensation d'avoir appris une nouvelle chose et d'avoir évité une tâche particulièrement ennuyeuse. La deuxième fois par contre, je pense que cela peut être considérable. Tenez, si l'envie me prend de faire un boulier à 10 tiges, je prends le pari qu'en moins d'une heure, c'est fait...

© 2024 International GeoGebra Institute