TP XWindow / Motif 1

Module EO / Dominante INF

Eric Lecolinet @ ENST


TP1 TP2 TP3 Java Elc Exemples

Mode d'Emploi

  1. Créér un nouveau répertoire et y copier les fichiers se trouvant dans le répertoire: ~domeo/TP-Motif/Motif1

  2. Faites les exercices qui suivent dans l'ordre car ils dépendent les uns des autres. Dans certains cas vous aurez juste à regarder le programme et ce qu'il fait. Dans d'autres cas il sera nécessaire de le compléter.

  3. compilez les programmes au fur et à mesure des exercices. Pour compiler un fichier, vous devrez utiliser la commande make. Par exemple, pour compiler le programme xxx à partir du fichier xxx.c, tapez la commande : make xxx

Attention : la commande make depend du fichier Makefile situé dans le répertoire courant. Ce fichier definit les options de compilation et les librairies adéquates pour générer un programme Motif sous un système d'exploitation donné. Le Makefile courant est conçu pour produire des exécutables sous SunOS5 (alias Solaris2).

Remarques:

Afficher un message au moyen d'un Label

Regarder le fichier label.c. On remarquera qu'il y a création de 3 widgets :

Noter que box et message sont "managés". (il faut "manager" les widgets les rendre actifs et les faire apparaître a l'écran).

Un fois les widgets crées, ils sont réalisés physiquement (par XtRealizeWidget) puis l'application entre dans la boucle infinie de gestion des événements.

Compiler et exécuter le programme "label". Quel est le message affiché ?

Attention : ne pas confondre les VARIABLES C qui pointent sur les widgets (valeur renvoyée par les fonctions XmCreateXXX) avec le NOMS des widgets (2eme argument des fonctions XmCreateXXX). Les premiers sont des pointeurs alors que les second sont des chaînes de caractères servant à désigner les widgets dans les Fichiers de Ressources.

Ajouter un PushButton

Compléter le fichier pushbutton.c comme suit :

  1. Créer (et manager) un nouveau widget :

  2. Lier la fonction Sortir (déjà définie dans le code source) au bouton que l'on vient de créer de telle sorte que cette fonction soit appelée quand on clique sur le bouton.

    Utiliser la fonction XtAddCallback (faire "man XtAddCallback") avec XmNactivateCallback en guise de "callback_name"

    Note : XmNactivateCallback ==> fonctions de callback appelées quand on "active" le bouton, c'est-à-dire quand on clique dessus avec le bouton 1 de la souris ou que l'on frappe la touche SPACE lorsque le widget "a le focus".

  3. Compléter la fonction Sortir de telle sorte :

    Compiler et exécuter le programme pushbutton.

Fichiers de Ressources

Les fichiers de ressources permettent de spécifier la valeur des ressources des widgets sans modifier le code C. Les spécifications sont de la forme :


    appli.widget1.widget2. .... widgetN.ressource : valeur
Avec :

De plus : * = joker qui évite de préciser les widgets intermédiaires.

Exemples :


  *box.message.labelString : Hello New World
  *button.labelString : Click and Die!
! les lignes commençant par ! sont des commentaires
! sur une station couleur seulement (sinon se limiter aux "couleurs" white et black) :

  *background : red
  *box*foreground : blue
  *message.background : green
  *XmPushButton.background : pink
Remarque : Comme le montre la dernière ligne, on peut également spécifier des classes de widgets. On peut ainsi modifier globalement tous les objets d'une même classe, sans avoir à les spécifier individuellement.
Noter que les noms des classes commencent généralement par une majuscule et ceux des instances par une minuscule.

Exercice :

Créer un fichier de ressources pour les programmes label et pushbutton en vous inspirant des exemples précédents. Ce fichier devra obligatoirement :

Relancer les programmes label et pushbutton après création du fichier de ressources. Essayer diverses variantes (sans oublier de relancer les programmes après chaque modification du fichier de ressources).

Comment faire pour que les ressources des widgets des programmes label et pushbutton aient des valeurs différentes ?

Remarques :

Fichier .Xdefaults

Le fichier .Xdefaults permet également d'initialiser des ressources. Contrairement au cas précédent, ce fichier est commun à toutes les applications quelle que soit leur classe.

Le principe est le même que précédemment sauf que :

Exemples :


  XMdemos*message.labelString : Message defini dans .Xdefaults
  pushbutton*message.labelString : Message juste pour l'application "xmbutton"
  pushbutton*button.labelString : Adieu \n Monde \n Cruel...
Remarques :
  • le fichier .Xdefaults est situé dans le "home directory",
  • les definitions dans .Xdefaults sont prépondérantes sur celles des fichiers de ressources,
  • les lignes commençant par des ! sont des commentaires.

    Listes d'Arguments

    Les listes d'arguments (ou ArgLists) permettent d'initialiser ou de modifier les valeurs des ressources dans le fichier C. Leur utilisation est plus complexe (que celle des fichiers de ressource) car les valeurs des ressources doivent alors être explicitement converties en un type adéquat (Par exemple, les chaînes de caractères C doivent être converties en chaînes de caractères Motif (de type XmString), les noms de couleurs doivent être convertis en valeurs de type Pixel, etc ...)

    Le programme arglist.c montre comment initialiser les ressources du Label message pour lui faire afficher un texte en noir sur fond blanc. Noter que :

    Attention

    Remarque : Noter l'utilisation de la fonction XmStringCreateLocalized pour transformer des chaînes de caractères normales en chaînes de caractères Motif (de type XmString). On aurait également pu utiliser les fonctions XmStringCreate ou XmStringCreateLtoR qui ont des fonctionnalités légèrement différentes (ces fonctions permettent de créer des chaînes Motif mélangeant plusieurs polices de caractères). Enfin, la fonction XmStringFree permet de récupérer la place mémoire allouée à la chaîne Motif quand on n'en a plus besoin.

    Compilez et exécutez arglist.c.

    Modification dynamique des valeurs des ressources

    Les étapes précédentes ont montré comment spécifier les valeurs des ressources :

    Dans les deux cas les ressources sont définies à l'initialisation, c'est-à-dire de manière "statique". Cet exercice propose de les modifier de manière dynamique, c'est-à-dire en cours d'exécution du programme.

    Le programme dynarg.c crée 3 widgets : un Label pour afficher un message, un bouton pour terminer l'application et un troisième bouton qui aura pour fonction de faire alterner les couleurs du "foreground" et du "background" du Label.

    Comme précédemment, le texte du Label est affiché en noir sur fond blanc, mais ceci est réalisé d'une manière différente : le Label est d'abord crée puis ses resources sont modifiées dynamiquement au moyen de la fonction XtVaSetValues. On remarque que :

    Remarques

    Exercices

    En vous inspirant de ce qui précède, rajoutez une fonction de callback qui alterne les couleurs du Bouton xchange quand on clique dessus (ie. qui échange les valeurs de son foreground et de son background)

    Encore plus fort ! : faire la même chose mais en échangeant cette fois les couleurs du Label message et sans utiliser de variable globale (aide : regarder le manuel de la fonction XtAddCallback ou ... le cours).

    Geometry Managers et Orientation

    Cet exemple montre comment modifier les ressources d'un Geometry Manager de classe RowColumn afin de disposer verticalement, horizontalement ou matriciellement les widgets qu'il contient.

    Le programme orientation.c crée un Label et 5 PushButtons.

    Les 4 derniers boutons appellent des fonctions de callback qui changent dynamiquement certaines ressources du widget box, lequel est un manager de type RowColumn.

    1. Compiler et exécuter le programme. Essayer les 4 possibilités : horizontal+linéaire, vertical+linéaire, horizontal+matriciel, vertical+matriciel.

    2. Changer la taille de la fenêtre à l'aide de la souris dans chacun des 4 cas. que constate-t'on ?

    3. Examiner le code. On voit que :

      • pour simplifier l'écriture, on a défini une fonction utilitaire (CreateButton) qui cree un PushButton et lui associe une fonction de callback

      • le dernier argument de cette fonction (le widget box) sera automatiquement passé à la fonction de callback lorsque celle-ci sera appelée. Cette technique évite d'avoir a utiliser des variables globales

      • les enfants du RowColumn box sont managé globalement au moyen de la fonction XtManageChildren

    Ajout dynamique de widgets et changements de taille

    Cet exemple montre :

    Compilez et exécutez le programme addobj. Celui-ci comprend initialement:

    Cliquez sur les boutons small et big. Que se passe-t'il ? Changez la taille de la fenêtre à l'aide de la souris puis faites de même que précédemment. Que constatez-vous ? Cliquez maintenant sur le bouton add plusieurs fois. Même chose sur remove.

    Examinez le code source à partir de la fonction main en faisant attention aux points suivants :

    Boîtes de Dialogue

    Un MessageDialog sert à afficher un message important tel que : message d'erreur, question, avertissement, information, etc ... Ce type de boîte de dialogue comprend :

    Les MessageDialogs sont crées au moyen des fonctions Motif XmCreateXXXDialog avec XXX = Message, Error, Information, Question, Warning et Working. Toutes ces fonctions créent des boîtes de dialogue identiques au pixmap près. Dans tous les cas l'objet crée est un Manager particulier de classe XmMessageBox (voir manuel Motif).

    Les principales ressources des MessageDialogs sont:

    De même que précédemment, les valeurs des ressources XmNxxxString sont des XmStrings (i.e. des chaînes de caractères Motif) qui sont créées au moyen des fonctions XmStringCreate... (et que l'on peut avantageusement spécifier dans les fallbacks (voir exercice précédent) ou dans les fichiers de ressources).

    Les callbacks s'ajoutent aux boutons OK, Cancel et Help de la même façon que précédemment (c'est-à-dire à l'aide de la fonction XtAddCallback mais avec les "callback_names" adéquats).

    Les boîtes de dialogues sont géneralement des objets éphémères ("transient" en anglais) qui n'apparaissent à l'écran que de manière temporaire (par exemple pour signaler une erreur, demander confirmation d'une commande, etc ...). Pour faire apparaître un Dialog, on utilise la fonction XtManageChild (comme pour les autres widgets). Inversement, la fonction XtUnmanageChild permet de faire disparaître un Dialogue. La ressource XmNautoUmanage entraine une disparition implicite quand on clique sur les boutons OK ou Cancel du dialogue.

    Exemple : Compilez et exécutez le programme dialog.c. On remarquera :

    Ce programme utilise les mêmes "fonctions utiles" que l'exemple précédent. La boîte de dialogue est créée une seule fois en même temps que les autres widgets et n'est jamais détruite. Elle est initialement invisible (i.e. pas managée) et n'apparaît que lorsque l'on clique sur le bouton open. Ce bouton appelle la fonction de callback OpenDialogCB, une nouvelle "fonction utile" que l'on pourra réutiliser ailleurs.

    Exercice :

    Rajouter un QuestionDialog demandant à l'utilisateur de confirmer lorsque l'on clique sur le bouton quit (i.e. cliquer sur quit ouvrira ce dialogue de confirmation (au lieu de sortir directement) et il faudra alors cliquer sur le bouton "OK" de ce dialogue pour terminer l'application).
    Remarque: il faudra utiliser 2 fonctions de callback, l'une pour ouvrir le QuestionDialog et l'autre pour terminer l'application.

    Récuperer des données depuis une boîte de Dialogue

    L'objet crée est un Manager particulier de classe XmSelectionBox dans les deux premiers cas et XmFileSelectionBox dans le dernier cas. Ces dialogues n'ont pas de ressources XmNmessageString ni XmNsymbolPixmap. Les PromptDialogs ont par contre les ressources suivantes:

    Pour les autres ressources, voir le manuel le Motif de ces classes.

    Exemple : Compilez et exécutez le programme promptdialog. Ce programme montre comment récupérer une chaîne de caractères depuis un PromptDialog.

    Regardez comment la fonction de callback GetAndSetTextCB récupère le texte tapé par l'utilisateur. Le 3eme argument (ou "call_data") de cette fonction pointe vers une structure dépendante du PromptDialog (c'est-à-dire du widget qui a appelé cette fonction). Cette structure comprend entre-autres un champ value qui contient le texte entré par l'utilisateur (pour savoir ce que contient cette structure, voir manuel de XmSelectionBox). Comme d'habitude, ce texte est stocké sous forme de XmString qu'il faut éventuellement convertir en String standard au moyen de la fonction XmStringGetLtoR,

    Remarques:

    Modalité et Fonctions Utiles

    Le programme modaldialog.c est semblable au précédent sauf que:

    Examinez le code source:

    Exercices :

    1. Rajouter un bouton qui ouvre un FileSelectionDialog. Le bouton OK du FileSelectionDialog devra lancer la lecture du fichier selectionné par l'utilisateur. Si ce fichier existe, il faudra en afficher le contenu sur le Label message, sinon, il faudra ouvrir un dialogue d'alerte modal pour signaler l'erreur à l'utilisateur.

      Remarques :

      • utiliser la fonction XmStringCreateLtoR pour transformer les chaînes de caractères C en XmString Motif. Cette fonction interprète les \n comme des passage à la ligne contrairement à XmStringCreateLocalized.
      • on pourra se contenter d'afficher la première ligne du fichier sur le Label message.

    2. positionner interactivement le PromptDialog lorsque l'on tape un couple de coordonnées (i.e. x puis y séparés par des blancs) dans le champ texte du PromptDialog.

      Remarque : modifier dynamiquement les ressources XmNx, XmNy et XmNdefaultPosition du PromptDialog. Noter que XmNdefaultPosition doit valoir False sinon le dialog est positionné automatiquement par le Window Manager sans tenir compte des valeurs de XmNx et de XmNy.

      Autres Dialogues Prédéfinis

      • un CommandDialog sert à taper une commande qui est conservée dans une historique.

      • un TemplateDialog est un MessageDialog vide (à part les 3 boutons OK, Cancel et Help), servant de base à la création de dialogues complexes

      • un FormDialog (resp. BulletinBoardDialog) est une boîte de dialogue entièrement vide comprenant uniquement un Form (resp. BulletinBoard) Manager. Ces boîtes de dialogues servent à créer des fenêtres secondaires complexes.

      Annexe A: Afficher un pixmap

      Regarder le fichier pixmap.c. On remarquera qu'il y a création de 3 widgets :
      • toplevel, le widget de plus haut niveau de l'application (toplevel est créé en même temps que le toolkit est initialisé)
      • box, le "conteneur" ou "Manager" de widgets
      • label1, le Label widget qui servira à afficher un pixmap

      Ceci s'apparente au programme "label.c".

      Cependant on crée maintenant un Pixmap qui peut être vu comme un masque de points. Il y a différentes façons de créer un Pixmap, nous utilisons ici la fonction XmGetPixmap qui prends en argument un nom de fichier au format xbm (format bitmap) , ainsi que les couleurs d'avant et d'arrière-plan du dessin.

      Les fichiers au format xbm peuvent être créés par le programme bitmap

      Compiler et exécuter le programme "pixmap".

      Elc: Cours & Exemples

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