Découvrir Scilab/Créer une interface graphique GUI

Table des matièresIndex



13. Créer une interface graphique GUI


Jusqu'ici, nous avons interagit avec notre programme Scilab via la ligne de commande. Il est également possible d'utiliser une interface graphique utilisateur (UI : user interface, ou GUI : graphical user interface).

Rappelons-le : le fait de disposer d'une interface graphique est peut-être une fonctionnalité importante du script, notamment si celui-ci doit être utilisé par un « non-spécialiste », mais la priorité — en terme de délais et de moyens — est de faire un programme qui calcule juste ! On commence donc par réaliser un programme avec une interface peu attractive, et l'interface graphique est réalisée en dernier, lorsque tout le reste fonctionne.

Interface graphique préprogrammée

modifier

Scilab propose quelques fonctions permettant une interaction via une interface graphique utilisateur :

  • nom_chemin = uigetdir() : ouvre une boîte de dialogue permettant de sélectionner un répertoire (dossier, chemin d'accès) ;
  • nom_fichier = uigetfile() : ouvre une boîte de dialogue permettant de sélectionner un fichier ;
  • nom_fichier = uiputfile() : ouvre une boîte de dialogue permettant de définir un nom fichier (en vue de le créer ou le modifier) ;
  • editvar nom_de_variable : permet de modifier une variable.

Création d'une interface graphique utilisateur

modifier

Il est possible de créer soi-même une interface graphique utilisateur. Cela se fait avec la commande uicontrol.

Pointeur d'un objet

modifier

Chaque composant de l'interface graphique — widget : texte, bouton, curseur, liste déroulante, … — est désigné par un pointeur (handle, littéralement « poignée »). Le pointeur est une variable créée automatiquement par Scilab lors de la création du composant.

La fenêtre graphique (figure) elle-même est désignée par un pointeur. Le nom du pointeur peut être défini lors de la création de la fenêtre, par exemple :

  • f = figure(1) : crée la fenêtre graphique no 1, la variable f pointant sur cette figure ;
  • f = scf(0) : indique que la figure courante est la figure 0, la variable f pointant sur cette figure.

On peut aussi récupérer le pointeur de la fenêtre courante par la commande f = gcf() — raccourci de f = get("current_figure").

Les objets de l'interface graphique ont eux aussi leur pointeur, dont le nom est défini à la création :

pointeur = uicontrol(...)

Le premier argument de la commande uicontrol est normalement le pointeur de la fenêtre graphique concernée. Les propriétés de l'objet peuvent être définies à la création dans la commande sous la forme d'un couple « propriété, valeur ». Elles peuvent aussi être définies a posteriori avec la syntaxe classique des pointeurs : « pointeur.propriété = valeur ».

Écrire dans une figure

modifier

Pour écrire un texte statique, on utilise la propriété "style" avec la valeur "text".

Pour le texte en lui-même, on utilise la propriété "string", la valeur étant le texte à insérer. Pour la mise en forme, on peut utiliser du code HTML ou bien inclure des formules mathématiques en LaTeX.

Pour définir la taille et l'emplacement de la zone de texte, on utilise la propriété "position", la valeur étant une vecteur contenant les coordonnées (x, y) du point en bas à gauche, et les dimensions (l, h) (largeur et hauteur) : [x y l h].

Par exemple :

h = figure(0);
t = uicontrol(h, "style", "text", ...
    "string", "Ceci est une figure", ...
    "position", [50 70 100 100]);

Ce script crée une zone de texte, située en (50, 70), et ayant pour dimension 100 × 100, et contenant le texte « Ceci est une figure ».

On peut définir la fonte utilisée :

  • police : propriété "fontname", la valeur étant le nom de la police ;
  • graisse : propriété "fontweight", avec les valeurs "light", "normal" (valeur par défaut), "demi" ou "bold" (gras) ;
  • corps : propriété "fontsize", avec la taille de corps en points (scalaire), ou en unité définie par fontunit ;
  • unité pour le corps : propriété "fontunit", avec les valeurs "points" (par défaut), "pixel" ou "normalized" ;
  • inclinaison : propriété "fontangle", avec les valeurs "normal" (par défaut), "italic" ou "oblic" ;
  • alignement :
    • propriété "HorizontalAlignment", avec les valeurs "left", "center" (par défaut) ou "right",
    • propriété "VerticalAlignment", avec les valeurs "top", "middle" (par défaut) ou "bottom".

Par exemple :

h = gcf();
t = uicontrol(h, "style", "text", ...
    "string", "Ceci est <code>une</code> figure", ...
    "fontsize", 12, ...
    "fontweight", "bold", ...
    "fontangle", "italic", ...
    "position", [50 70 120 100]);

ou bien

h = gcf();
t = uicontrol(h, "style", "text", ...
    "string", "Ceci est <code>une</code> figure", ...
    "position", [50 70 120 100]);
set(t, "fontsize", 12);
set(t, "fontweight", "bold");
t.fontangle = "italic";

Créer un bouton

modifier

Les boutons sont créés avec la propriété "style" et la valeur "pushbutton". C'est par ailleurs le style par défaut, on peut donc omettre de déclarer la propriété.

Les propriétés du bouton sont :

  • "string", "..." : texte dans le bouton ;
  • "position", "[...]" : position et taille du bouton ;
  • "callback", "code scilab" : fonction de rappel (instructions à exécuter lorsque l'on clique).

Exemple :

b = uicontrol("style", "pushbutton",...
    "string", "$x^2$",...
    "callback", "X = [0:0.1:5]; plot(X, X.^2);");

Modifier les propriétés d'un élément

modifier

Les pointeurs permettent de définir les propriétés d'un élément graphique. Ainsi, une fonction de rappel (callback) peut modifier un autre objet.

Dans l'exemple ci-dessous, nous traçons une courbe en vert ; le pointeur de cette courbe est appelé courbe. Puis, nous créons des boutons pour modifier la couleur de la courbe, c'est-à-dire la propriété courbe.foreground.

Notons que nous travaillons avec des unités normalisées (propriétés "units", "normalized") : dans ce système d'unités, la largeur et la hauteur de la fenêtre valent 1. Pour la fonction xsetech(), le point (0, 0) se trouve en haut à gauche, le point (1, 1) en bas à droite (sens de lecture latin). Pour la définition des autre objets, et notamment des boutons que l'on crée, le point (0, 0) est en bas à gauche et le point (1 ,1) en haut à droite (convention mathématique).

f = figure(0);
clf;

// la fenêtre de traçage occupe 90 % de la hauteur
// réserve 10 % de la fenêtre en bas pour les boutons
xsetech([0, 0, 1, 0.9])

// tracé de la courbe
X=[0:0.1:5];
plot(X, X.^2, "g"); // courbe verte

composant = gce();
courbe = composant.children;

// bouton changeant la couleur en noir
bnoir = uicontrol("style", "pushbutton",...
    "units", "normalized",...
    "position", [0, 0, 0.1, 0.05],...
    "BackgroundColor", [0,0,0],...
    "callback", "courbe.foreground=1");

// bouton changeant la couleur en rouge
brouge = uicontrol("style", "pushbutton",...
    "units", "normalized",...
    "position", [0.11, 0, 0.1, 0.05],...
    "BackgroundColor", [1,0,0],...
    "callback", "courbe.foreground=5");

Notons également que les couleurs sont définies de trois manières :

  • par une « chaîne de caractères-raccourci », le paramètre "g" de la commande plot() ;
  • par un vecteur RVB, dans la propriété "BackgroundColor" des boutons ;
  • par le numéro de la couleur dans carte de couleurs (colormap), pour la fonction de rappel "callback" des boutons.

Le seul point ambigu est le dernier, puisque la carte de couleurs peut changer. Au sein d'un programme, il faut donc s'attacher à maîtriser la carte de couleurs (voir Graphiques et sons > Cartographie couleur).

Créer une zone d'édition

modifier
 
Interface graphique utilisateur : calcul et tracé de la fonction carré.

Une zone d'édition est créée avec la propriété "style" et la valeur "edit". On peut indiquer le texte initial qu'elle contient avec la propriété "string". C'est également par ce biais-là que l'on récupère sa valeur.

Exemple : calcul et tracé de la fonction carré

f = scf(0);
// zone d'édition, saisie de la valeur
e = uicontrol(f, "style", "edit", ...
    "position", [0 0 100 20]);
// zone de texte, affichage du résultat
t = uicontrol(f, "style", "text", ...
    "position", [200 0 100 20], ...
    "string", "...");
// bouton effectuant le calcul
b = uicontrol(f, "style", "pushbutton", ...
    "string", "$x^2$",...
    "position", [100 0 100 20], ...
    "callback", "x = evstr(e.string);" + ...
                "y = x^2;" + ...
                "t.string = string(y);" + ...
                "plot2d(x, y, style = -1);");

Essayer l'exemple en prenant des valeurs successives sans interrompre le programme. Par exemple commencer par des valeurs extrêmes — 0 puis 10 — pour fixer l'échelle graphique, et « remplir » par des valeurs intermédiaires (2, 5, 7, …).

Analysons la fonction de rappel :

  • x = evstr(e.string) : récupère la chaîne de caractères de la zone d'édition (pointeur e), la transforme en nombre (« évaluation » de la chaîne) et la place dans la variable x. On prendra toutefois garde au fait qu'au moment où le script sera exécuté (lorsque le bouton sera pressé, possiblement longtemps après la création des composants interactifs), la variable e peut ne plus exister. Pour conserver l'"adresse" de la zone d'édition en zone sûre, on peut par exemple la sauvegarder dans le champ userdata du bouton (voir plus loin).
  • y = x^2 : calcule le carré de x et place le résultat dans la variable y ;
  • t.string = string(y) : transforme la valeur en chaîne de caractère, et place le résultat dans la chaîne de caractères de la zone de texte (pointeur t) ; cela provoque l'affichage de la valeur. t est aussi sujet que e à effacement ou écrasement intermédiaire.
  • plot2d(x, y, style = -1) : place le point sur le graphique.

Encapsulation dans une fonction

modifier

Si l'interface graphique est définie dans une fonction (function … endfunction), alors la fonction crée les composants de l'interface grapique (wigets) puis se termine. Lorsque l'on utilise les éléments de l'interface graphique, nous ne sommes donc plus dans l'environnement de la fonction, ce qui pose des problèmes ; en particulier, les pointeurs n'existent plus.

Pour gérer ces problèmes, on peut utiliser la propriété userdata pour stocker des données, puis y faire appel avec la variable gcboget callback object, c'est une variable créée automatiquement par Scilab lors de l'exécution dans la fonction de rappel callback).

Par exemple, le petit programme suivant affiche « foo », et lorsque l'on clique sur le bouton [OK], il affiche « 0 » à la place :

function []=test(h)
    texte = uicontrol(h, "style", "edit", ...
        "string", "foo",...
        "fontsize", 14, ...
        "position", [50 100 100 20]);
    bouton = uicontrol(h, "string", "OK",...
        "position", [50 50 50 20], ...
        "userdata", texte, ...
        "callback", "gcbo.userdata.string = string(0);");
endfunction

fig = figure(0);
test(fig);

Lorsque l'on définit le bouton, on enregistre donc le pointeur texte dans la propriété userdata du bouton. On peut ensuite faire appel à ce pointeur grâce à la variable gcbo.userdata : gcbo.userdata.string est l'équivalent de texte.string..

Exemple

modifier

Le script suivant reprend l'exemple du calcul du PGCD et du PPCM par l'algorithme d'Euclide. Comme nous avons voulu encapsuler la création de la fenêtre graphique dans une fonction (bonne pratique), il nous faut sauvegarder les pointeurs contenant les données dynamiques (qui varient lors de l'exécution). Comme il y a quatre pointeurs — deux variables d'entrée (n1 et n2), et deux variables de sortie (PGCD et PPCM) —, le champ userdata du bouton est un vecteur de quatre pointeurs : la définition du bouton d'exécution (composant dont le pointeur est bouton) contient

"userdata", [N1, N2, aff_pgcd, aff_ppcm]

avec :

  • N1 : pointeur vers la première zone d'édition, contenant la valeur n1 ;
  • N2 : pointeur vers la seconde zone d'édition, contenant la valeur n2 ;
  • aff_pgcd : pointeur vers la zone de texte destinée à afficher PGCD(n1, n2) ;
  • aff_ppcm : pointeur vers la zone de texte destinée à afficher PPCM(n1, n2).

La fonction d'appel du bouton contient :

  • n1 = eval(gcbo.userdata(1).string) : récupération de n1, car gcbo.userdata(1) est l'équivalent de N1 ;
  • n2 = eval(gcbo.userdata(2).string) : idem pour n2 ;
  • gcbo.userdata(3).string = string(pgcd) : écrit le contenu de la variable pgcd dans la chaîne du pointeur gcbo.userdata(3) qui n'est autre que aff_pgcd ;
  • gcbo.userdata(4).string = string(ppcm) : idem avec le PPCM.

Ce qui nous donne le code source suivant.

//============================================================================
// nom : pgcd_ppcm_euclide_gui.sce
// auteur : Christophe Dang Ngoc Chan
// date de création : 2013-01-02
// dates de modification : ...
//----------------------------------------------------------------------------
// version de Scilab : 5.4.1
// module Atoms requis : aucun
//----------------------------------------------------------------------------
// Objectif : détermine le PGCD et le PPCM par l'algorithme d'Euclide
// Entrées : deux nombre entier (scalaires)
// Sorties : deux nombres entiers (scalaires)
//============================================================================

// ***** Fonctions et procédures *****

function [resultat]=euclide(a, b)
    r=modulo(a,b); // initialisation du reste
    while r<>0
        a = b;
        b = r;
        r=modulo(a,b); // calcul du reste
    end
    resultat = b;
endfunction

function []=fenetre_graphique(h)
    // titre de la boîte
    titre = uicontrol(h, "style", "text", ...
        "string", "PGCD et PPCM", ...
        "fontweight", "bold", ...
        "fontsize", 14, ...
        "position", [50 400 120 20]);
    // consignes
    texte_expl = uicontrol(h, "style", "text", ...
        "string", "Entrez deux nombres entiers et cliquez sur [OK]", ...
        "fontsize", 14, ...
        "position", [50 370 300 20]);
    // saisie du 1er nombre
    texte_b1 = uicontrol(h, "style", "text", ...
        "string", "N1 = ", ...
        "fontsize", 14, ...
        "position", [50 340 50 20]);
    N1 = uicontrol(h, "style", "edit", ...
        "string", "1",...
        "fontsize", 14, ...
        "position", [85 340 100 20]);
    // saisie du 2nd nombre
    texte_b2 = uicontrol(h, "style", "text", ...
        "string", "N2 = ", ...
        "fontsize", 14, ...
        "position", [50 310 50 20]);
    N2 = uicontrol(h, "style", "edit", ...
        "string", "1", ...
        "fontsize", 14, ...
        "position", [85 310 100 20]);
    // affichage du PGCD
    texte_res1 = uicontrol(h, "style", "text", ...
        "string", "PGCD = ", ...
        "fontsize", 14, ...
        "position", [50 250 50 40]);
    aff_pgcd = uicontrol(h, "style", "text", ...
        "string", "...", ...
        "fontsize", 14, ...
        "position", [110 250 50 40]);
    // affichage du PPCM
    texte_res2 = uicontrol(h, "style", "text", ...
        "string", "PPCM = ", ...
        "fontsize", 14, ...
        "position", [50 220 50 40]);
    aff_ppcm = uicontrol(h, "style", "text", ...
        "string", "...", ...
        "fontsize", 14, ...
        "position", [110 220 50 40]);
    // bouton d'exécution et fonction de rappel
    bouton = uicontrol(h, "string", "OK",...
        "position", [50 280 50 20], ...
        "userdata", [N1, N2, aff_pgcd, aff_ppcm], ...
        "callback", "n1 = eval(gcbo.userdata(1).string);" + ...
                    "n2 = eval(gcbo.userdata(2).string);" + ...
                    "pgcd = euclide(n1, n2);" + ...
                    "ppcm = n1*n2/pgcd;" + ...
                    "gcbo.userdata(3).string = string(pgcd);" + ...
                    "gcbo.userdata(4).string = string(ppcm);");
endfunction

// ***** Programme principal *****

fig = figure(0); // création de la fenêre
clf;
fenetre_graphique(fig);

Voir aussi

modifier



Programmation < > Environnement