TD X-Window numero 2: un afficheur de graphes
A faire avant de commencer:
- Recopier le squelette du TD chez vous. Créez un directory xlib/td2 sous votre $HOME (ou ailleurs) et recopierz dedans tous les fichiers qui se trouvent dans le répertoire ~buffa/cours/minfo/xlib/td2.
- Etudier le Makefile.
- Etudier les fichiers sources qui vous sont fournis. Certains sont prêts à l'emploi comme graphe.h et grapheIO.c, d'autres sont à complèter comme graphe.c et display.c
- Etudier les fichiers sample.g et sample2.g qui sont des fichiers ASCII contenant des descriptions de graphes.
But du TD:
Nous allons écrire ensemble un petit programme graphique permettant de
lire des graphes, de les visualiser et de les manipuler
interactivement.
Vous pouvez tester, avant de commencer, le programme
binaire graphe_corrige afin d'avoir une idée plus précise de ce que
vous devez réaliser.
Le source corrigé sera disponible à la fin du TD.
Pour essayer le binaire fourni :
Lancer la commande :
cat sample2.g | graphe_corrige <return>
Important:
- Comme d'habitude, référez-vous au cours Xlib que je vous ai concocté pour y voir plus clair!
- Utilisez les manuels! Pour chaque fonction que vous ne connaissez pas, consultez le man, soit dans un xterm, soit en utilisant le mode man de emacs (ESC-X man), soit avec xman, enfin... utilisez un peu ce que vous voulez mais apprenez à utiliser les manuels !.
- Si vraiment vous ne trouvez pas ce que vous cherchez dans les manuels, essayez de regarder dans /usr/include/X11/Xlib.h, dans /usr/include/X11/X.h ou bien appelez-moi!
Différentes étapes dans ce TD:
- Prendre le temps d'étudier les sources et les fichiers de description des graphes qui sont fournis. Il est nécessaire que vous ayez une idée générale de ce que vous allez devoir réaliser.
- Ecrire petit à petit les parties de code manquantes dans le fichier graphe.c, puis dans le fichier display.c
Code à écrire dans le fichier graphe.c :
- Pour l'ouverture du display, référez-vous au TD précédent et/ou à la partie du cours sur le Display.
- Le chargement d'une police de caractères se fait à l'aide de la fonction XLoadQueryFont(), voir la partie du cours concernant l'affichage de texte. Penser à traiter le cas où le serveur ne trouve pas la fonte de caractères spécifiée.
- Le calcul de la taille des noeuds du graphe en fonction de la police de caractères chargée se fera en appelant la fonction FixGraphLabelDimensions() qui se trouve dans le fichier display.c
- Créer une fenêtre d'affichage. Voir le TD précédent et la partie du cours sur les fenêtres. Etudiez l'exécution du programme graphe_corrige et déduisez les événements minimums que la fenêtre devra gérer (aide: il y en a 4).
- Créer trois contextes graphiques (GC).
- Le premier, que j'ai appelé fill_gc servira pour les appels à XfillRectangle() qui seront utilisés pour dessiner les noeuds du graphe;
- Le second draw_gc servira à dessiner le texte et le cadre des noeuds;
- Le troisième, xor_gc servira lorsqu'on déplacera la noeuds à la souris pour effacer les anciennes parties du graphe. Il faudra positionner le champ function de la structure XGCValues que j'ai appelée gcv dans le source.
- Gérer les événements :
- Appui du bouton 1 de la souris (fonction = séléctionner un noeud du graphe pour le déplacer) :
- Trouver le noeud dans lequel le click souris a eu lieu. Cela se fait en appelant la fonction FindNode() qui se trouve dans le fichier display.c. tester la valeur de retour car il se peut que le click n'ait pas eu lieu dans un noeud.
- Si le click a bien eu lieu dans un noeud, appeler la fonction RelatedEdges(graph, node) qui se trouve dans le fichier graphIO.c. Cette fonction retourne la liste des arcs (edges) reliés au noeud (node) passé en paramètre. Cette liste sera utilisée pour redessiner ces arcs pendant le déplacement du noeud.
- Déplacement de la souris bouton 1 appuyé (fonction = déplacer un noeud et les arcs qui lui sont reliés avec un effet "élastique") :
- Je vous laisse vous débrouiller ! Aide : utilisez les fonctions DisplayNode() et DisplayEdges() qui se trouvent dans le fichier display.c et utilisez à bon escient les contextes graphiques disponibles.
- Relâche du bouton 1 : (fonction = redessiner le graphe s'il y a eu un déplacement de noeud)
- Tester la valeur de la variable selected_node pour voir s'il faut redessiner le graphe.
- Effacer la fenêtre.
- Afficher le graphe (utiliser la fonction DisplayGraph(graph, dpy, win, draw_gc, fill_gc) qui se trouve dans le fichier display.c).
- Libérer la liste des arcs reliés au noeud séléctionné en utilisant la fonction FreeEdges(related_edges) qui se trouve dans graphIO.c
- Appui du bouton 2 de la souris (fonction = print_graph) :
- Appeler la fonction PrintGraph(graph, stdout) qui se trouve dans le fichier graphIO.c. Cette fonction affiche dans un xterm le contenu du fichier de description du graphe.
- Appui du bouton 3 de la souris : fin du programme.
Code à écrire dans le fichier display.c :
- DisplayGraph(graph, dpy, window, draw_gc, fill_gc) :
- Pas bien difficile. Regardez comment s'appellent les fonctions suivantes dans le fichier! Deux lignes suffisent !
typedef struct _Graph {
char *title; /* symbolic */
Node nodes; /* null-terminated list */
Edge edges; /* null-terminated list */
} *Graph;
- DisplayNode(node, dpy, window, draw_gc, fill_gc) :
typedef struct _Node {
int id; /* unique identifier (number) */
int x; /* position */
int y;
int width; /* to be calculated */
int height; /* to be calculated */
char *label; /* text to be displayed */
int baseline_x;
int baseline_y;
struct _Node *next; /* next node in graph */
} *Node;
- DisplayEdge(edge, dpy, window, gc) :
- Pas de commentaires ! Un arc c'est une l.... ! Etudiez la structure Edge pour obtenir les coordonnées des extrêmités de l'arc passé en paramètre.
typedef struct _Edge {
int id; /* unique identifier (number) */
int from_id; /* nodes connected by id */
int to_id;
Node from; /* the nodes themselves */
Node to;
struct _Edge *next; /* next edge in graph */
} *Edge;
- Node FindNode(graph, x, y) :
- Retourner le premier node dont la bounding box contient le point (x,y). Utilisez la macro NodeContains(nodes, x, y) qui est définie dans le fichier display.c
Source du squelette du TD et Makefile :
Squelette du TD :
michel.buffa@essi.fr