TP XWindow / Motif 3

Eric Lecolinet, Bertrand Dupouy - Dept. INFRES - ENST
home page elc page cours dept. infres enst


Sommaire

Première partie: Dessin et Interactions non standard

Interactions non standard: exemple du dessin à main levée

Les fonctions de callback "classiques" ne peuvent être attachées qu'à des ressources prédéfinies des widgets qui correspondent aux conditions les plus courantes d'interaction. Motif prévoit ainsi par exemple que l'on puisse appeler une fonction de callback quand on clique sur un PushButton (ressource XmNactivateCallback), quand on appuie dessus (XmNarmCallback) ou quand on relache ce bouton (XmNdisarmCallback). Mais rien n'est prévu pour appeler une telle fonction quand la souris entre sur le PushButton, bouge à l'intérieur du bouton, etc. Il est alors nécessaire de faire appel à d'autres techniques d'interactions qui sont illustrées dans les exemples suivants.

Le programme ima1.c montre comment utiliser le mecanisme des Actions / Translations pour afficher la position de la souris quand on la bouge interactivement sur une zone d'écran. Le programme ima2.c fait la même chose mais permet de plus de faire du dessin à main levée quand on maintient le bouton 2 de la souris enfoncé. Enfin le programme ima3.c est similaire au programme ima2.c mais utilise le mécanisme des Event Handlers.

Ces exemples montrent également comment faire appel aux fonctions de la Xlib depuis une application Motif pour afficher du "dessin" ou des graphiques. L'affichage se fait sur un DrawingArea contenu dans une ScrolledWindow (i.e. une fenêtre avec des ascenseurs). On montre d'ailleurs au passage comment programmer une ScrolledWindow pour qu'elle gère automatiquement son contenu (c'est-à-dire une fenêtre de plus grande taille: le DrawingArea dans le cas qui nous interesse) lorsque l'on interagit sur les ascenseurs.

Precisions:

Cette exemple montre:

Application: Dessin et Aide sensitive

Ecrire un nouveau programme (que l'on appelera "imaexo.c") qui s'inspire des exemples précédents en y rajoutant les fonctionnalités suivantes:

  1. permettre l'affichage d'un trait, d'un rectangle ou d'un cercle à une position donnée. Les coordonées de la primitive graphique pourront être spécifiées: (choisir une des deux possibilités)

  2. rajouter une aide contextuelle qui fasse en sorte qu'un message adéquat s'affiche automatiquement sur une zone prédéterminée chaque fois que l'on fait entrer la souris sur un des widgets de l'interface (il s'agit de faire une aide contextuelle similaire à ce que l'on peut trouver sur Word, Netscape, etc.).

    Indication: on pourra soit utiliser le mecanisme des Actions / Translations soit employer des Events Handlers pour implémenter cette fonctionnalité.

    Gestion des rafraîchissements

    Les exemples précédents sont en fait incomplets car ils ne rafraîchissent pas la zone d'affichage graphique lorsque cela est nécessaire. Pour s'en convaincre : faites passer une autre fenêtre au dessus du DrawingArea qui sert à l'affichage graphique puis enlevez cette fenêtre. Le problème est du au fait que les primitives graphiques (traits, rectangles, etc.) ne sont jamais réaffichées implicitement contrairement aux widgets qui intègrent des méthodes spécifiques pour effectuer leur réaffichage de manière automatique.

    Comment pourrait-on résoudre ce problème de réaffichage des primitives graphiques (plusieurs solutions sont possibles) ?

    Deuxième partie: Réactivité des interfaces

    R1. Rafraîchissement

    Le programme "gc1" (composé des fichiers gc.c, disp.h et disp1.c - voir le fichier Makefile) montre un exemple d'interface qui lance un "gros calcul". Le problème posé concerne le rafraîchisement (c'est-à-dire) la mise a jour de la présentation graphique) et la réactivité (le fait que les événements soient immédiatement pris en compte) de l'interface.

    Compiler, exécuter et regarder le code de gc1. Le bouton GO lance le calcul et le bouton Exit permet de sortir. Tester le rafraîchissement lorsque le calcul est lancé (typiquement en faisant passer une autre fenêtre par dessus l'interface). Tester la réactivité en appuyant sur Exit. Que se passe-t'il ? Pourquoi ?

    Remarque:
    le code de cette interface comporte une bizarrerie. Laquelle et pourquoi ? (indication: que se passe-t'il quand on essaie de fermer l'application non pas avec "Exit" mais avec le bouton "Close" du menu du Window Manager).

    R2. Rafraîchissement (bis)

    Le programme "gc2" (composé des fichiers gc.c et disp2.c) comporte une légère amélioration par rapport à la version précédente. Pour s'en convaincre, tester de nouveau le rafraîchissement de l'interface et regarder plus particulièrement le code de la fonction AfficheMessage.

    Conclusions ?

    R3. Retours visuels

    Le programme "gc3" (composé des fichiers gc.c et disp3.c) montre comment programmer un retour visuel approprié destiné à indiquer à l'utilisateur que l'interface est bloquée car en train d'effectuer un calcul de longue durée. De nombreuses solutions peuvent être envisagées (par exemple un voyant qui passe du vert au rouge, un curseur qui progresse en fonction de l'état des calculs, des boutons qui changent d'aspect ou de sensitivité ...). Dans l'exemple proposé, la fonction BusyCursor permet de changer l'apparence du pointeur qui matérialise la souris à l'écran et d'afficher une montre à la place de la flèche usuelle.

    R4. Reactivite

    Nous allons maintenant essayer de résoudre le problème de la réactivité de l'interface. Pour se faire, on rajoute un bouton Stop qui est censé permettre d'interrompre le calcul une fois lancé. Ce type de problème peut éventuellement être réglé en utilisant en exécutant l'interface et le noyau fonctionnel de l'application au moyen de threads ou de processus séparés. Nous allons essayer ici de le résoudre avec une solution "purement X". L'idée est de faire en sorte que la fonction de calcul teste à intervalle régulier (par exemple chaque fois qu'elle affiche un message) si l'on a appuyé sur le bouton stop.

    Il vous faut donc à partir du code de l'exercice précédent :

    Indications: bien réfléchir et regarder le manuel de la fonction XtAppMainLoop.
    Fichiers: regardez gcstop.c et complétez disp4.c.

    Remarque: passez à l'exemple suivant si vous n'arrivez pas à faire cet exercice.

    R5. Synthèse

    Cet exemple constitue une synthèse des précédents et montre à la fois comment gérer de manière adéquate les rafraîchissements, la réactivité et la mise en évidence de retours visuels appropriés.

    Compilez et exécutez le programme gc5. Consultez les fichiers gcstop.c et disp5.c.

    Troisième partie:
    Couplage entre Interface et Noyau fonctionnel

    C1. Boucle d'événements et sockets

    Cet exemple montre comment coupler un programme qui comporte une interface graphique avec un autre programme (non graphique) qui peut éventuellement tourner sur une machine distante. Ces deux programmes communiquent au moyen de sockets Unix.

    Plus précisément:

    Regarder, comprendre et compiler les programmes client et interx. Lancer ces deux programmes (dans le bon ordre!) avec les arguments adéquats.

    Remarque:
    le fichier servx.c définit gere_reseau une fonction de call-back d'un genre particulier (son prototype diffère des fonctions de call-back usuelles) qui sera automatiquement appelée par la XtMainLoop chaque fois qu'un nouveau message sera envoyé par le programme client sur la socket d'écoute. Cette fonction est déclarée à la XtMainLoop lors de l'initialisation par appel de la fonction XtAppAddInput.

    C2. Application

    Reprendre l'exemple imaexo réalisé dans la première section et faire en sorte que l'affichage graphique puisse être "commandé à distance" par le programme client de l'exemple précédent (on donnera au "client" le type de primitive à afficher et ses coordonnées, la figure correspondante s'affichera automatiquement dans l'interface de "imaexo").

    C3. Utilisation des threads

    (*Optionnel) Voir la partie B du TP: http://www.enst.fr/~domas/TPIHM.html

    home page elc page cours dept. infres enst

    Page maintenue par Eric Lecolinet (elc@enst.fr). Sept 95 / Fev 97.