Les écouteurs

From GeoGebraWiki

Jump to: navigation, search

Programmer les écouteurs d'objet en javascript.


Consultez cette page de référence de la documentation de GeoGebra.

Cette page du wiki est écrite pour la version 3.0 de GeoGebra. Elle donne des informations complémentaires sur la programmation des scripts à l'aide d'écouteurs.


Contents

Pourquoi utiliser des écouteurs ?

Cette technique est utile pour lier des appliquettes GeoGebra entre elles. Un exemple dans cet article

Mais cette technique permet aussi d'augmenter les possibilités d'interaction avec GeoGebra et de créer de nouvelles fonctionnalités. Cette solution est beaucoup plus souple et fluide que l'utilisation de timer.

Le script qui sert d'illustration permet d'obtenir des points semi-libres c'est à dire des points libres et dépendants :

- deux exemples de points C et D qui sont libres dans une région, mais ne peuvent pas en sortir. problème évoqué dans cet article ;

- un exemple de point libre, mais qui en même temps suit le déplacement du point D. Un exemple traité sans écouteur, mais à l'aide de timer.

Ces deux exemples ne sont pas triviaux : rien ne permet d'obtenir un tel résultat dans GeoGebra sans avoir recourt à du javascript.

L'exemple développé dans cette page du wiki est ici Affichez le code source de la page à l'aide de votre navigateur pour voir le script complet


Initialiser les écouteurs à l'ouverture de la page.

L'évènement load est déclenché par le navigateur lorsque toute la page html est chargée. Malheureusement les appliquettes n'y sont pas prises en compte. Il faut donc plutôt utiliser un script comme celui-ci :

<SCRIPT language=JavaScript>
var app; // l'applet GeoGebra
setTimeout('init()',0);

function init()
{
	try
	{
	app = document.figureGGB; // Ici l'applet est déclarée. Si elle n'existe pas encore une erreur est déclenchée 
                                 //et provoque l'arrêt du bloc try et l'éxecution du bloc catch.
	app.registerObjectUpdateListener("C","ecouteC"); // création de l'écouteur du point C
	}
	catch (e)                                // l'applet figureGGB n'éxiste pas encore
	{
		setTimeout('init()',500); // on relance l'initialisation dans une demi-seconde.
	}
}

Cependant, l'instruction setTimeout("init()",0); peut être remplacée par une déclaration dans la balise BODY en réponse à l'évènement load.

<BODY onload="'init();">

ou même

<BODY onload="setTimeout('init()',0);">

Utilisation d'un écouteur pour agir sur un autre point

var ax; var ay; // ces deux variables doivent être initialisées. A faire dans la section init()
function ecouteC(obj)
{

	var x=app.getXcoord(obj);
	var y=app.getYcoord(obj);

	// Déplacement du point semi-libre
	var xE = app.getXcoord("E");
	var yE = app.getYcoord("E");
	app.setCoords("E",xE + x - ax,yE + y - ay);
	
	ax = x; ay=y;
} 

Remarque au sujet des changements de nom :

Le point C peut changer de nom, et comme l'indique la documentation l'écouteur reste opérationnel dans ce cas. Mais il faut donc utiliser son nom communiqué par GeoGebra via la variable obj. A l'inverse si le point E change de nom, le script ne fonctionne plus.

Pour palier à ce problème il faut déclarer un registerRenameListener dans la fonction init()

var pointE;
function init()
{
	…
	app.registerRenameListener("renom");
	pointE = "E"
	…
}

function renom(obj1,obj2)
{
	if (obj1 == pointE) pointE=obj2;
}

puis dans ecouteC :

	var xE = app.getXcoord(pointE);
	var yE = app.getYcoord(pointE);
	app.setCoords(pointE,xE + x –ax , yE + y – ay);

Rétro agir sur un point.

Le principe est le même, mais avant de modifier les coordonnées d'un point il faut bien penser à supprimer l'écouteur sur ce point ; sinon des appels récursifs à l'écouteur seront lancés ce qui bloquera le navigateur.

function ecouteC(obj)
{
	app.unregisterObjectUpdateListener(obj);
	var x=app.getXcoord(obj);
	var y=app.getYcoord(obj);
	var Xmin = app.getXcoord("A"); // La zone est rectangle de diagonale [AB]
	var Xmax = app.getXcoord("B");
	var Ymin = app.getYcoord("A");
	var Ymax = app.getYcoord("B");
	
	if (x<Xmin) {x=Xmin;} // Le point sort ?
	if (x>Xmax) {x=Xmax;} // on le remet au bon endroit
	if (y<Ymin) {y=Ymin;}
	if (y>Ymax) {y=Ymax;}
	

	
	// le point C est contraint de rester dans la zone
	app.setCoords(obj,x,y);
	app.registerObjectUpdateListener(obj,"ecouteC");
}

Là encore si A ou B changent de nom…


Vie et mort des écouteurs :

Un écouteur d'objet est tué lorsque l'objet en question est supprimé (Effacer). C'est aussi le cas si la figure est réinitialisé (bouton "refresh"). Mais des actions inattendues provoquent aussi ce phénomène :

- Undo (reprendre une étape);

- Redéfinir un objet quelconque (et pas seulement celui qui est écouté !).

Heureusement, ce type d'évènement peut être facilement capturé avec un écouteur déclaré par registerClearListener. Malheureusement, l'évènement est déclenché avant que GeoGebra n'agisse : au moment de l'appel les objets et les écouteurs existent encore. Il est donc trop tôt pour agir.

Attention : Tous les écouteurs ne disparaissent pas. Par exemple, survivent les écouteurs déclarés par :

- registerClearListener

- registerUpdateListener

- registerRenameListener

Et il faut donc prendre garde à ne pas multiplier les écouteurs d'un même évènement, sous peine de ralentir considérablement la réactivité de l'appliquette GeoGebra.


Avant ou après ?

Il est donc crucial de savoir si GeoGebra déclenche l'évènement avant ou après traitement dans l'appliquette.

- registerObjectUpdateListener : l'évènement est déclenché après modification. C'est ce qui permet de rétro-agir. On peut donc modifier la position d'un point efficacement puisqu'il est déjà déplacé. La preuve de ce comportement : dans l'exemple la trace jaune du point Fou est sa position avant modification (le point Fou est sous le curseur de la souris), puis il est modifié par l'écouteur (qui change aussi sa couleur).

A l'inverse l'écouteur déclaré par registerClearListener est appelé avant que la figure soit effectivement "nettoyée". Si on souhaite re-déclarer les écouteurs d'objets, il faut laisser le temps à GeoGebra de faire le ménage. Un timer s'impose donc :

function init()
{
	try
	{
	app = document.figureGGB; 
	app.registerObjectUpdateListener("C","ecouteC");
	app.registerObjectUpdateListener("r","ecouteD");
	app.registerObjectUpdateListener("Fou","ecouteFou");
	app.registerClearListener("down");               // Celui qui capture "Clear"
	app.registerRenameListener("renom");
	…
} 


function down(obj) // l'écouteur Clear
{
	app.unregisterClearListener("down");             // Ces deux écouteurs survivent
	app.unregisterRenameListener("renom");           // On fait donc un peu de ménage
	setTimeout('init()',1000);                        // Pour laisser le temps à GeoGebra d'effacer la figure.
}

Attention cependant : init sera appelé 1 seconde après le déclenchement de l'évènement clear de l'appliquette. Il se peut que dans l'intervalle d'autres évènements clear soient déclenchés. Dans ce cas init() sera appelé plusieurs fois, et donc les écouteurs seront eux aussi déclarés plusieurs fois.

Pour palier à ce problème, une première solution simple consiste à tuer le chronomètre en cours :

var chrono;
function down(obj)
{
	app.unregisterClearListener("down");             
	app.unregisterRenameListener("renom");          
       clearTimeout(chrono);                            // Si un chrono est déjà lancé, on le tue.
	chrono = setTimeout('init()',1000);                        // Pour laisser le temps à GeoGebra d'effacer la figure.
}

Une deuxième solution consiste à gérer les déclarations d'écouteurs dans des méthodes spécifiques qui se chargeront alors de ne pas dupliquer les écouteurs. Un exemple de mise en œuvre de cette solution dans TBI.htm.

Personal tools