Documentation d’aide au développement Ncurses/Ruby


ATTENTION : Cette documentation n’est pas terminée, elle contient certainement des fautes d’ortographe voire quelques erreurs techniques.

INFOS : La version présente ne contient que la premiére partie nommée “Les bases”.

DERNIÉRE VERSION DE LA DOC DISPONIBLE ICI : http://azmodaisoft.tuxfamily.org/index.php?target=download


PARTIE : Les bases

Ncurses et ruby

Pourquoi apprendre Ncurses ?

Le principal atout d’une interface programmée en curses c’est de pouvoir être lancée n’importe oû, aussi bien en mode console qu’en mode X par le biais d’un terminal. Cet atout distingue les librairies curses des librairies dites graphiques telles que gtk ou qt qui ne permettent pas de créer des interfaces utilisable en mode console pure.

Cette qualité permet aux utilitaires programmés avec une interface curses d’être utilisables dans un environnement restreint post-installation (et démuni de X) par exemple. Apprendre à utiliser Ncurses permet donc de créer des utilitaires au plus proche du systéme qui utilise la souplesse des interfaces. Ncurses est la solution de réference pour programmer en curses, c’est pourquoi ce tutoriel y est consacré.

Pourquoi utiliser Ruby avec Ncurses ?

Grâce au binding ncurses-ruby le langage ruby supporte trés bien ncurses. De plus les qualités de ce langage telles que la programmation objet, la simplicité ou encore la clarté de son écriture font de ce langage le parfait allié pour écrire une interface curses réussie. Si vous ne connaissez pas le langage ruby vous pouvez trouvez des documentations dans la section liens et “ruby”. Si vous désirez utiliser ncurses avec un autre langage que ruby reportez-vous à la section liens et “autres langages”.

En quoi diffère l'utilisation de Ncurses avec Ruby ?

L’utilisation de Ncurses avec Ruby diffère en trés peu de points de l’utilisation de Ncurses avec le language C. Mise à part les quelques différences d’utilisation de certaines fonctions (telle que stdscr = initscr() et initscr, getx()/gety() et getyx(), getbegx()/getbegy() et getbegyx(), win = window.new() et win = subwin() et quelques autres...) Ncurses s’utilise globalement de la même façon qu’en C.

A la difference du C en ruby lorsque l’on appelle des fonctions Ncurses on appelle les fonctions Ncurses de la class Ncurses alors qu’en C on appelle les fonctions Ncurses comme des fonctions indépendantes. Exemple :

En C:

initpair(1,COLOR_GREEN,COLOR_BLACK);

En Ruby :

Ncurses.initpair(1,COLOR_GREEN,COLOR_BLACK);

Quel est le but de cette documentation ?

Cette documentation a pour but l’introduction à Ncurses avec le langage Ruby, elle ne réference pas toutes les fonctions Ncurses ni n’explique tous les rouages de cette librairie. Si après la lecture de cette introduction vous souhaitez accéder à toutes les informations concernant Ncurses afin de peaufiner vos connaissances vous pouvez accéder à une trés bonne documentation de Ncurses avec le langage C (en anglais) dans la section liens puis “autres langages”.

Comme l’utilisation de Ncurses avec le langage Ruby est proche de l’utilisation de Ncurses avec le C vous n’aurez pas de mal à utiliser les informations contenues dans la documentation en anglais disponible en section liens et “autres langages”.

Obtenir tout le nécessaire

De quoi avons-nous besoin ?

Nous avons besoin de ruby, si celui-ci n’est pas déja présent sur votre machine vous pouvez le télécharger ici :

http://www.ruby-lang.org/

Nous avons besoin des librairies Ncurses si vous ne disposez pas des librairies Ncurses sur votre machine téléchargez les ici :

http://ftp.gnu.org/pub/gnu/ncurses/

Et enfin nous avons besoin du binding ncurses-ruby qui est disponible à l’adresse suvante :

http://ncurses-ruby.berlios.de/

I) Initialiser et configurer Ncurses.

Avant de pouvoir utiliser les libraires Ncurses il faut d’abord initialiser son utilisation dans le programme ainsi que configurer les principales caractéristiques de l’interface. Une fois Ncurses initialisé et ses principales caractéristiques configurées nous devrons initialiser les “paires de couleurs”.

1) Initialisation de la librairie Ncurses.

L’initialisation de Ncurses dans le programme se fait en utilisant la fonction initscr du module Ncurses qui renvoie comme valeur les caractéristiques de la fenêtre mère (stdscr). Dans toute la documentation la fenêtre mére sera nommée stdscr. Son utilisation s’écrit comme cela :

stdscr = Ncurses.initscr;

2) Configurer les principales caratéristiques de l'interface.

Une fois Ncurses initialisé dans le programme nous pouvons configurer les principales caractéristiques de la fenêtre mère et des fenêtres filles. Il nous sera possible d’activer/désactiver certaines caractéristiques telles que l’affichage du curseur, l’echo du clavier ou encore l’utilisation du keypad. Ces étapes ne sont pas obligatoires.

a) Désactiver/re-activer l'affichage du curseur.

Pour afficher ou ne pas afficher le curseur dans l’interface il nous suffit d’utiliser la fonction curs_set du module Ncurses. Pour ne pas afficher le curseur il suffit de passer comme argument une valeur null. Et par la suite si nous souhaitons que l’affichage du curseur soit à nouveau activé il suffit de passer comme argument à la fonction Ncurses.curs_set une valeur non-null. Par exemple si nous souhaitons désactiver l’affichage du curseur j’utiliserai :

Ncurses.curs_set(0);

Et si par la suite nous souhaitons re-activer l’affichage du curseur nous taperons :

Ncurses.curs_set(1);
NOTE

Activer le curseur en utilisant Ncurses.curs_set(1); est utile uniquement lorsque l’affichage du curseur a été désactivé précedemment et que l’on souhaite le réactiver, étant donné que l’affichage du curseur est activé par défaut.

b) Désactiver/re-activer l'echo du clavier.

Par défaut lorsque l’on utilise le clavier il y a un echo des lettres tapées sur l’interface. Cet echo peut devenir génant lorsque l’interface ou les widjets qui la compose ne nécessitent pas d’entrées de caractéres. Pour palier à ce probléme il suffit simplement d’utiliser la fonction noecho du module Ncurses :

Ncurses.noecho;

Si par la suite vous souhaitez re-activer l’echo du clavier dans l’interface il vous suffit de taper :

Ncurses.echo;
c) Activer/re-désactiver le keypad.

Lorsque l’on active le keypad on active la prise en charge par l’interface de touches telles que ESC (ou echap), F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, les flêches directionelles, les touches CTRL et les touches ALT. En quoi cela peut-il être utile ? Si par exemple vous souhaitez que l’utilisateur puisse quitter votre interface en tapant sur echap par exemple, vous aurez besoin d’activer le keypad afin que l’appui de la touche echap soit “entendu” par votre interface.

Pour activer/désactiver le keypad il faut appeler la fonction keypad du module Ncurses. La fonction Ncurses.keypad demande deux arguments : le premier argument est la fenêtre mére utilisé et le second TRUE ou FALSE. Si vous souhaitez activer le keypad il vous suffit de taper :

Ncurses.keypad(stdscr,TRUE);

Autrement si par la suite vous souhaitez désactiver le keypad il vous faut taper :

Ncurses.keypad(stdscr,FALSE);
d) Désactiver/re-activer le "line buffering".

Alors d’abord qu’est-que le “line buffering” ?

Lorsque le “line buffering” est activé le buffer s’attend à recevoir une ligne de plusieurs code ASCII représentant les touches tapées. Or lorsque l’on souhaite que l’utilisteur puisse piloter l’interface en appuiant sur des touches particuliéres du clavier nous avons besoin de récupérer un unique code ASCII. C’est la fonction getch qui va nous permettre de récupérer ce code ASCII, nous l’étudierons plus tard dans la documentation.

Pour désactiver le “line buffering” nous pouvons utiliser les fonctions cbreak ou raw :

Ncurses.cbreak;
Ncurses.raw;

Alors quelles sont les différences entre ces deux types de fonctions ?

La fonction cbreak laisse passer les instructions (CTRL+Z) et (CRTL+C) alors que la fonction raw traite ces appuis de touches comme les autres. Si par la suite vous souhaitez re-activer le “line buffering” il vous suffit d’utiliser une de ces deux fonctions :

  • Si vous avez appelé auparavant la fonction cbreak : Ncurses.nocbreak;
  • Si vous avez appelé auparavant la fonction raw : Ncurses.noraw;

3) Créer les paires de couleurs.

Avant de créer les paires de couleurs nous devons lancer la prise en charge des couleurs par Ncurses en lançant la fonction start_color du module Ncurses :

Ncurses.start_color;

Afin de colorer notre interface nous devons d’abord initialiser les paires de couleurs. Une paire de couleurs est constituée d’une couleur pour le fond (background) et d’une couleur pour le texte (foreground). Ainsi lorsque nous voudrons qu’une fenêtre soit bleu et que le texte soit noir nous appelerons la paire de couleur correspondante.

Ncurses peut gérer 8 couleurs différentes :

COLOR_BLUE
COLOR_RED
COLOR_CYAN
COLOR_YELLOW
COLOR_MAGENTA
COLOR_GREEN
COLOR_BLACK
COLOR_WHITE

C’est avec ces 8 couleurs que nous allons créer nos paires de couleurs pour colorer nos fenêtres et nos textes. Pour créer une paire de couleurs il faut appeler la fonction init_pair du module Ncurses. Celle-ci s’utilise comme cela :

Ncurses.init_pair(id_de_la_paire, couleur_du_texte, couleur_du_fond);

Voici un exemple d’utilisation :

Ncurses.init_pair(1,COLOR_BLACK,COLOR_CYAN);

Ci-dessus id_de_la_paire correspond à un nombre identifiant la paire de couleurs. C’est avec ce nombre que l’on va utiliser la paire de couleurs par la suite. Les deux valeurs couleur_du_texte et couleur_du_fond correspondent quant à elles aux 8 couleurs proposées ci-dessus.

II) Créer une fenêtre avec Ncurses

Lorsqu’on utilise les librairies Ncurses avec ruby cette étape est simplifiée par rapport au C/Ncurses. En effet en C/Ncurses pour créer une fenêtre il faut écrire ceci:

WINDOW la_fenêtre;
la_fenetre = subwin(stdscr,y,x,begin_y,begin_x,);

Alors qu’avec le ruby/Ncurses il suffit simplement d’écrire cela :

la_fenetre = WINDOW.new(y,x,begin_y,begin_x);

Avant de vous en expliquer un peu plus je tiens à vous informer de la maniére dont Ncurses gére les coordonnées. En effet, contrairement aux mathématiques, les valeurs ‘y’ ne croissent pas de bas en haut mais bien de haut en bas. De plus on n’écrit pas les valeurs ‘x’ avant les ‘y’ mais bien les valeurs ‘y’ avant les valeurs ‘x’ lorsqu’on appelle une fonction nécessitant des coordonnées.

Pour créer une fenêtre en Ruby/Ncurses il suffit de procéder ainsi:

la_fenetre = WINDOW.new(y,x,begin_y,begin_x);

Maintenant je vais vous expliquer à quoi correspondent les quatres valeurs begin_y, begin_x, y et x pour les fenêtres.

  • La valeur begin_y correspond à la valeur verticale de commencement de la fenêtre (en commençant par le haut). Si begin_y est égal à 4 la fenêtre se placera ainsi :
1               |
2               |
3               v
4 ------------------------------
  • La valeur begin_x corresspond quant à elle à la valeur horizontale de commencement de la fenêtre (en commençant par la gauche). Si begin_x est égal à 6 la fenêtre se placera ainsi :
          |
          |
- - - - > |
          |
          |
1 2 3 4 5 6
  • La valeur y correspond à la hauteur de la fenêtre. Si y est égal à 10 et begin_y à 4 sur une hauteur totale de 20, par exemple, alors la fenêtre se situera ainsi :
1               |
2               | 4
3               v
4-------------------------------
5               ^
6               |
7               |
8               |
9               | 10
10              |
11              |
12              |
13              v
14------------------------------
15
16
17
18
19
20
  • La valeur x correspond à la largeur de la fenêtre. Si x est égal à 20 et si begin_x est égal à 5 sur une largeur totale de 30 alors la fenêtre se situera ainsi :
        |                                                       |
  5     |                          20                           |
- - - > |<- - - - - - - - - - - - - - - - - - - - - - - - - - > |
        |                                                       |
        |                                                       |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  • Et enfin si begin_y est égal à 4, y est égal à 10, begin_x est égal à 5 et x est égal à 20 sur une hauteur totale de 20 et une largeur totale de 30 alors la fenêtre se dessinera ainsi :
1                                     |
2                                     | 4
3                                     v
4          | - - - - - - - - - - - - - - - - - - - - - - - - - - - |
5          |                          ^                            |
6          |                          |                            |
7          |                          | 10                         |
8     5    |                          |     20                     |
9 - - - >  |< - - - - - - - - - - - - - - - - - - - - - - - - - - >|
10         |                          |                            |
11         |                          |                            |
12         |                          |                            |
13         |                          v                            |
14         | - - - - - - - - - - - - - - - - - - - - - - - - - - - |
15
16
17
18
19
20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

III) Colorer les fenêtres.

1) Initialiser une paire de couleurs à une fenêtre.

Afin de colorer les fenêtres nous devons utiliser la fonction bkgd appartenant aux class des fenêtres que nous souhaitons colorer. La fonction bkgd s’utilise de la façon suivante :

fenetre_a_colorer.bkgd(Ncurses.COLOR_PAIR(id_de_la_paire));

Ici id_de_la_paire est le nombre correspondant à la paire de couleurs souhaitée pour la coloration. La fonction COLOR_PAIR du module Ncurses permet, en donnant un id, à la fonction bkgd de colorer la fenêtre fenetre_a_colorer avec la paire de couleur correspondante à l’id donné.

NOTE

Si vous souhaitez colorer la fenêtre principale vous devez utiliser la fonction bkgd de la façon suivante :

stdscr.bkgd(Ncurses.COLOR_PAIR(id_de_la_paire));

2) Changer la paire de couleurs d'une fenêtre temporairement.

Dans la suite des sections nous apprendrons à écrire des textes dans nos fenêtres. Mais comment dois-t-on procéder si l’on souhaite changer la couleur du texte momentanément afin de mettre en valeur un passage de celui-ci ?

Tout d’abord nous devons créer une autre paire de couleurs (voir section I 3). Cette autre paire de couleurs sera utilisée pour le changement momentané de couleur. Ensuite nous devrons utiliser les deux fonctions attron et attroff appartenant aux class des fenêtres qui subirons les changements temporaires et demandant chacune comme argument une paire de couleurs. Ces deux fonctions s’utilisent ainsi :

fenetre_a_modifier_temporairement.attron(Ncurses.COLOR_PAIR(id_de_la_paire_utilise_momentanement));
fenetre_a_modifier_temporairement.attroff(Ncurses.COLOR_PAIR(id_de_la_paire_originel));

La fonction attron nous permet de changer la couleur de la fenêtre concernée en utilisant la nouvelle paire de couleurs et la fonction attroff nous permet de désactiver la paire de couleurs temporaire et de ré-activer la paire de couleur originelle.

NOTE

Le texte qui doit subir la modification de couleur doit être écrit après l’usage de attron et avant l’usage de attoff.

IV) Ecrire dans notre interface.

Pour prevenir l’utilisateur ou le questionner il peut être utile de pouvoir écrire dans notre interface, c’est ce que nous allons étudier dans cette section. Afin d’écrire dans l’une de nos fenêtres nous avons plusieurs types de fonctions à notre disposition : les fonctions de types addstr et les fonctions de types printw.

1) Les fonctions de types addstr.

Il existe plusieurs fonctions de type addstr, parmi ces fonctions nous allons étudier les plus courantes telle que :

  • la fonction Ncurses.addstr,
  • la fonction Ncurses.waddstr,
  • la fonction Ncurses.addnstr,
  • la fonction Ncurses.waddnstr,
  • la fonction Ncurses.mvaddstr,
  • et la fonction Ncurses.mvwaddstr.
a) La fonction Ncurses.addstr.

La fonction addstr nous permet d’écrire sur la fenêtre mére un texte quelconque commençant en haut et à l’extremité gauche de la fenêtre. La fonction addstr appartient au module Ncurses et s’utilise ainsi :

Ncurses.addstr("mon_text");
b) La fonction Ncurses.waddstr.

La fonction waddstr permet, contrairement à la fonction addstr, d’écrire un texte quelconque sur une fenêtre fille. Le texte écrit commençera aussi en haut et à l’extreminté gauche de la fenêtre. La fonction waddstr appartient au module Ncurses et s’utilise ainsi :

Ncurses.waddstr(fenetre_fille,"mon_text");
c) La fonction Ncurses.addnstr.

La fonction addnstr a exactement le même comportement que la fonction addstr (voir a) mis à part le fait que le nombre de caratères affichés est limité par une valeur de son choix. La fonction addnstr du module Ncurses s’utilise ainsi :

Ncurses.addnstr("mon_text",n);

Ici n est le nombre de caractères de la chaine “mon_text” que la fonction addnstr affichera. Voici un exemple concret :

Ncurses.addnstr("salut comment ça va ?",5);

Ci-dessus la fonction affichera “salut”.

d) La fonction Ncurses.waddnstr.

La fonction waddnstr a ici exactement le même comportement que celui de la fonction addnstr. La seule différence est que la fonction waddnstr permet en plus d’écrire dans des fenêtres filles. La fonction waddnstr appartient au module Ncurses et s’utilise ainsi :

Ncurses.waddnstr(fenetre_fille,"mon_text",n);
e) La fonction Ncurses.mvaddstr.

La fonction addstr a pour principale faiblesse son incapacité à écrire un texte à un endroit précis et voulu ; c’est pourquoi la fonction mvaddstr a été créée ! Avec cette fonction nous pouvons écrire une texte là où nous le souhaitons dans la fenêtre mère. La fonction mvaddstr appartient au module Ncurses et s’utilise ainsi :

Ncurses.mvaddstr(y,x,"mon_text")
NOTE

Attention contrairement en mathématique on donne les valeurs de y avant les valeurs de x avec Ncurses.

f) La fonction Ncurses.mvwaddstr.

La fonction mvwaddstr se comporte exactement comme la fonction mvaddstr à la seule différence qu’avec cette fonction nous pouvons écrire où nous souhaitons sur une fenêtre fille. La fonction mvwaddstr appartient au module Ncurses et s’utilise ainsi :

Ncurses.mvwaddstr(fenetre_fille,y,x,"mon_text");

2) Les fenêtres de types printw.

Les fonctions de types printw se distinguent des fonctions de type addstr par leur capacité à gérer plusieurs types de données. En effet avec les fonctions de types addstr on ne peut afficher que des chaines de caratères. Avec les fonctions de types printw on peut afficher des chaines de caratères, des entiers, des nombres à virgules etc ....

Là aussi il existe plusieurs fonctions de type printw, nous allons étudier les plus connues telle que :

  • la fonction Ncurses.printw,
  • la fonction Ncurses.wprintw,
  • la fonction Ncurses.mvprintw,
  • et la fonction Ncurses.mvwprintw.
a) La fonction Ncurses.printw.

La fonction printw permet d’afficher n’importe quel type de donnée dans la fenêtre mère. Cette fonction appartient au module Ncurses et s’utilise ainsi :

Ncurses.printw("chaîne_de_contrôle",donnees);

Exactement comme en C c’est par le biais de la chaîne de contrôle que l’on gére l’affichage des données par type. Il existe plusieurs élements de contrôle pouvant constituer la chaîne de contrôle parmi :

  • l’élement de contrôle %d pour les décimales et les entiers,
  • l’élement de contrôle %s pour les chaînes de caratéres,
  • l’élement de contrôle %c pour les caratères simples,
  • l’élement de contrôle %f pour les virgules flotantes etc ....

Voici plusieurs exemples concrets d’utilisation de Ncurses.printw :

Ncurses.printw("%d",1);
Ncurses.printw("%s","salut à toi");
Ncurses.printw("%c",'y');
Ncurses.printw("%f",0.12132133);

Dans la même chaîne de contrôle il est tout à fait possible d’utiliser plusieurs élements de contrôle, par exemple :

Ncurses.printw("%s : %d","Aujourd'hui nous sommes le",16);

Ce qui nous donnera : “Aujourd’hui nous comme le : 16”.

b) La fonction Ncurses.wprintw.

Le comportement de wprintw est strictement identique à celui de la fonction printw (voir a) mis à part le fait que wprintw permet d’écrire dans les fenêtres filles. La fonction wprintw appartient au module Ncurses et s’utilise ainsi :

Ncurses.wprintw(fenetre_fille,"chaine_de_contrôle",données);
c) La fonction Ncurses.mvprintw.

Là aussi concernant le traitement des types de données (voir a) le comportement de la fonction mvprintw est identique à celui de la fonction printw. Cette fonction se distingue par le fait qu’elle permet d’afficher des données n’importe où dans la fenêtre mère. La fonction mvprintw appartient au module Ncurses et s’utilise ainsi :

Ncurses.mvprintw(y,x,"chaîne_de_contrôle",donnees);
d) La fonction Ncurses.mvwprintw.

L’usage de cette fonction est analogue à celui de la fonction mvprintw excepté le fait que cette fonction permet d’afficher des données n’importe où dans les fenêtres filles et non uniquement dans la fenêtre mère. La fonction mvwprintw appartient au module Ncurses et s’utilise ainsi :

Ncurses.mvwprintw(fenetre_fille,y,x,"chaîne_de_contrôle",donnees);

V) Enjoliver son interface.

1) Rajouter un effet d'ombre aux fenêtres.

Si vous desirez donner plus de perspective à votre interface vous pouvez affecter un effet d’ombre à vos fenêtres. Pour cela il suffit de créer une fenêtre supplémentaire à chaque fenêtre à laquelle vous voulez rajouter cet effet. Cette fenêtre supplémentaire doit :

  • conserver la même taille que la fenêtre à laquelle vous voulez rajouter l’effet,
  • avoir une couleur de fond noir (voir section I et III),
  • être légerement décalée par rapport à la fenêtre à laquelle vous voulez ajouter l’effet.
  • être rafraîchie avant la fenêtre à laquelle vous voulez ajouter l’effet (voir section VI).

Comme il est écrit ci-dessus lors de la création des “fenêtres ombres” vous devez bien faire attention à ce que les valeurs begin_y et begin_x soient légerement différentes de celles des fenêtres auxquelles vous souhaitez ajouter cet effet (voir section II).

Comme valeur begin_y pour les “fenêtres ombres” je vous conseille de donner les valeurs begin_y des fenêtres auquelles vous voulez ajouter l’effet +1.

Pour les valeurs begin_x des “fenêtres ombres” je vous conseille là de donner les valeurs begin_x des fenêtres auxquelles vous souhaitez ajouter l’effet +2.

2) Ajouter un contour "box" aux fenêtres.

Afin de faire ressortir un peu plus nos fenêtres nous pouvons aussi affecter un effet “box” aux fenêtres que nous avons créées. Cet effet “box” consiste à créer une pseudo-bordure autour des fenêtres. Il s’applique en utilisant la fonction box du module Ncurses. Cette fonction s’utilise ainsi :

Ncurses.box(fenetre_fille,bordure_verticale,bordure_horizontale);

Ici bordure_verticale et bordure_horizontale peuvent être remplacés par :

ACS_VLINE,
ACS_HLINE,
ACS_ULCORNER,
ACS_URCORNER,
ACS_LLCORNER,
ACS_LRCORNER.

Pour un effet de bordure classique je vous conseille d’utiliser la fonction comme suit :

Ncurses.box(fenetre_fille,ACS_VLINE,ACS_HLINE);
NOTE

Comme la bordure est créée avec des caratéres nous pouvons trés facilement changer sa couleur si nous le souhaitons afin que la couleur de la bordure se distingue bien de la couleur des textes de la fenêtre (voir section III).

VI) Afficher/rafraichir les fenêtres créées.

Pour visualiser les modifications soumises à l’interface de l’interface elle-même, il est nécessaire de refroidir celle-ci. Afin d’afficher ou de rafraichir une fenêtre Ncurses nous devons utiliser la fonction refresh appartenant aux class des fenêtres que nous souhaitons rafraichir. Ainsi si nous souhaitons rafraichir une fenêtre nous allons taper :

fenetre_a_rafraichir.refresh;

Lorsqu’une fenêtre fille a été créée il nous faut rafraichir la fenêtre mère avant la fenêtre fille afin que la fenêtre fille puisse s’afficher. Pour rafraichir la fenêtre mère nous devons taper :

stdscr.refresh;

Pour les actions suivantes il est nécessaire de rafraîchir la fenêtre mère :

  • ajout de fenêtres filles,
  • suppression de fenêtres filles,
  • ajout de textes dans la fenêtre mère,
  • modification de taille de fenêtres filles,
  • changement de couleur de la fenêtre mère.

Concernant les fenêtres filles, tant que les modifications sont internes à celles-ci et qu’elles n’ont aucune incidence sur l’extérieur il suffit de les rafraichir simplement sans rafrichir la fenêtre mére auparavant pour afficher les modifications.

VII) Faire perdurer l'affichage de l'interface et gérer son fonctionnement.

Si vous avez construit une interface avec tout ce qui a été dit auparavant et que vous l’avez essayée, vous vous êtes sûrement aperçu d’une chose : à peine l’interface est lancée qu’elle se ferme.

À cela il existe une solution. Afin de faire perdurer l’affichage et surtout de gérer les actions de l’interface nous devons utiliser une boucle while. Cette boucle continuera à boucler tant qu’une touche définie ne sera pas appuyée, dans l’exemple ci-dessous cette touche sera ECHAP (ou ESC). Si vous ne l’avez pas déjà fait vous devez activer le keypad (voir section I).

Afin de vérifier à chaque bouclage que la touche ECHAP n’a pas été appuyée nous devons utiliser la fonction getch. La fonction getch permet de savoir quelle touche est appuyée par l’utilisateur, elle s’utilise ainsi :

stdscr.getch;

La fonction getch renvoie la touche appuyée en code ASCII (pour avoir la liste de correspondance des touches du clavier et des codes ASCII voir section liens et “autres”).

Afin que l’interface soit quittée lorsque l’utilisateur tape sur ECHAP nous allons d’abord déclarer une constante ESC avec comme valeur 27 (ECHAP en code ASCII) puis nous allons construire la boucle while avec comme condition de s’arrêter si la fonction getch renvoie 27 (donc ESC). Voici ce que cela donne :

ESC=27;
  
while stdscr.getch!=ESC
    # La gestion de l'interface
end

Comme le commentaire ci-dessus le stipule toute la gestion de l’interface va se passer dans cette boucle. C’est ici que nous allons décider des réactions de l’interface en fonction de ce que l’utilisateur souhaite. Si par exemple nous souhaitons qu’une certaine fenêtre s’affiche lorsque l’utilisateur tape sur F1 nous allons modéliser la boucle while de la façon suivante :

ESC=27;
key=0;
  
while key!=ESC
    key=stdscr.getch;

    if(key==KEY_F1)
        message.refresh;
    end
end
NOTES
  • Il n’y a pas de code ASCII pour les touches F1, F2 etc ... Afin de savoir si une de ces touches à été appuyée il faut comparer la sortie de stdscr.getch avec les valeurs KEY_FX prédéfinies.
  • Il n’existe pas de code ASCII pour les flèches directionnelles mais il existe des valeurs prédéfinies :
KEY_LEFT
KEY_RIGHT
KEY_UP
KEY_DOWN
  • Il est préférable de rafraîchir la globalité de l’interface avant la boucle while et ensuite dans la boucle while de rafraîchir au fur et à mesure les fenêtres ayant subit des modifications (voir section VI).
  • Dans le programme, après la boucle while, on touve généralement les fonctions de suppression de fenêtres et de sortie de Ncurses (voir section VIII et IX).

VIII) Supprimer une fenêtre

Pour supprimer une fenêtre nous devons utiliser la fonction delwin du module Ncurses. La fonction delwin prend comme argument la fenêtre à supprimer. Voici comment s’utilise cette fonction :

Ncurses.delwin(fenêtre_à_supprimer);

Si la fenêtre est volontairement supprimée en cours d’exécution du programme nous pouvons refroidir la fenêtre mére avec :

stdscr.refresh;

Une fois l’ensemble refroidit la fenêtre supprimée n’apparaîtra plus.

NOTE

Il est recommandé de supprimer les fenêtres créées en fin de programme afin de libérer la mémoire allouée pour celles-ci.

IX) Quitter Ncurses dans le programme

Tout d’abord avant de quitter Ncurses dans le programme nous allons supprimer toutes les fenêtres créées auparavant avec la fonction delwin du module Ncurses (voir section V). Une fois les fenêtres supprimées et afin de quitter Ncurses nous allons lancer la fonction endwin du module Ncurses:

Ncurses.endwin;
NOTE

La fonction endwin ne prend aucun argument.

X) Quelques trucs et astuces pour bien construire son interface

(Avant d’utiliser ces astuces il est nécessaire d’avoir bien lu la présente documentation auparavant)

1) Créer des fenêtres proportionnelles à la taille du terminal.

Afin que les fenêtres de l’interface soient toujours proportionnées à la taille du terminal nous devons construire nos fenêtres en fonction de la taille de la fenêtre mére (la taille maximale de l’interface). Pour cela nous devons d’abord récupérer les valeurs y et x maximales de la fenêtre mère en utilisant les fonctions getmaxy et getmaxx. Ces deux fonctions renvoient des entiers et s’utilisent de la maniére suivante :

$maxy=Ncurses.getmaxy(stdscr);
$maxx=Ncurses.getmaxx(stdscr);

Une fois les valeurs y et x maximales de la fenêtre mére récupérées nous pouvons créer sans difficultés des fenêtres entièrement proportionnelles à la taille du terminal. Vous devons procéder à un calcul simple pour obtenir la taille de la fenêtre fille que nous voulons créer en proportion de la taille du terminal.

a) Si la fenêtre n'a pas pour but d'être centrée.

Si la fenêtre n’a pas pour but d’être centrée alors les calculs seront les suivants :

taille_y_de_la_fenetre_fille = $maxy-(begin_y+remaining_y);
taile_x_de_la_fenetre_fille = $maxx-(begin_x+remaining_x);

Ci-dessus les valeurs begin_y et begin_x correspondent aux valeurs de commencement de la fenêtre qui ont été choisies auparavant (voir section II). Les valeurs y_remaining et x_remaining correspondent quant à elles aux valeurs y et aux valeurs x restantes. Voici concrètement à quoi correspond les valeurs y_remaining et x_remaining :

1
2       |-----------------------|
3       |                       |      x_remaining
4       |                       |<------------------->
5       |                       |
6       |                       |
7       |-----------------------|
8               ^
9               | y_remaining
10              v
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Une fois les calculs finis nous pouvons utiliser les valeurs pour construire la fenêtre comme nous l’avons appris auparavant (voir section II).

b) Si la fenêtre a pour but d'être centrée

Si la fenêtre a pour but d’être centrée les calculs seront encore plus simples :

taille_y_de_la_fenetre_fille = $maxy-(2*y_space);
taile_x_de_la_fenetre_fille = $maxx-(2*x_space);

Ci-dessus y_space représente l’espacement en bas et en haut qu’aura la fenêtre fille avec les bords horizontaux de la fenêtres mère. Et x_space représente l’espacement à gauche et droite qu’aura la fenêtre fille avec les bords verticaux de la fenêtres mère. Voici concretement à quoi ces espacements correspondent :

1                         ^
2                         | y_space
3                         v
4            |-------------------------|
5   x_space  |                         |  x_space
6  <-------> |                         | <------->
7            |                         |
8            |-------------------------|
9                         ^
10                        | y_space
11                        v
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Une fois les calculs finis nous pouvons utiliser les valeurs pour construire la fenêtre comme nous l’avons appris auparavant (voir section II).

2) Centrer un message d'une ligne.

Lorsque l’on souhaite afficher un message d’une ligne dans l’interface il est pratique de pouvoir centrer celui-ci dans la fenêtre afin que cela fasse plus propre. Pour cela nous allons nous servir de la valeur x maximale de la fenêtre qui va contenir le message. Si la fenêtre en question est la fenêtre mère nous allons récuperer la valeur x maximale de stdscr autrement nous récupererons celle de la fenêtre fille en question. Pour récupérer la valeur x maximale nous devons utiliser la fonction getmaxx :

$maxx=Ncurses.getmaxx(fenetre_en_question);

Une fois la valeur x maximale obtenue nous allons nous servir de la fonction mvwaddstr ou alors de la fonction mvaddstr (si la fenêtre qui doit afficher le message est la fenêtre mère) (voir section IV). Et enfin pour illustrer l’exemple nous allons nous servir de la fonction mvwaddstr :

$text="Voici le message";

Ncurses.mvwaddstr(fenetre_fille,emplacement_y_du_message,($maxx-$text.length)/2,$text)

Le calcul ($maxx-$text.length)/2 nous permet de connaître la marge de commencement du message afin que celui-ci soit bien centré dans la fenêtre.

NOTE

Vous pouvez aussi utiliser les fonctions mvwprintw et mvprintw qui permettent de gérer plusieurs types de données (voir section IV).

3) Créer une barre de progression.

Afin de prevenir l’utilisateur de l’avancement d’une action par le biais de l’interface il peut être bien utile d’utiliser une voire plusieurs barres de progressions. Par défaut Ncurses ne propose pas de barre de progression dans ces fonctions, nous allons donc mettre au point une fonction qui va nous permettre de créer à la volée des barres de progression.

Nous allons apprendre à créer une barre de progression qui s’adapte entièrment à sa fenêtre support. La barre de chargement pourra donc avoir n’importe qu’elle taille. Nous allons nommer cette fonction loading (changement en français), elle prendra en arguments principaux la fenêtre qui va devenir le support de la barre de progression, un texte explicatif du chargement et un entier allant de 1 à 100 (pour le pourcentage d’avancement).

a) Comment allons-nous créer "l'effet de chargement" ?

Pour modéliser la barre de chargement nous allons jouer sur la création d’une fenêtre interne à la fenêtre support.

  • La fenêtre_interne sera de hauteur taille_y_fenetre_support-2 et de largeur n.
  • Sa valeur begin_y sera égale à begin_y_fenetre_support+1 et sa valeur begin_x sera égale à begin_x_fenetre_support+1 (voir section II).
  • La largeur n sera dépendante d’un entier compris entre 1 et 100 qui symbolise numériquement le chargement.

Afin de calculer la largeur de la fenêtre interne en fonction de la largeur de la fenêtre support et en fonction de l’entier symbolisant le chargement nous allons procéder au calcul suivant :

n=(i*(taille_x_fenetre_support-2))/100;

ATTENTION: Il peut arriver que ce calcul donne un nombre < 1. Dans ce cas il est préférable d’utiliser le bout de code suivant :

if((i*(taille_x_fenetre_support-2))/100 < 1)
    n=1;
else
    n=(i*(taille_x_fenetre_support-2))/100;
end

A chaque appel de la fonction loading la valeur n sera re-calculée. C’est ce calcul qui va modéliser l’accroîssement de la fenêtre interne et donc l’effet de chargement. Une fois ce calcul terminé nous pouvons créer la fenêtre interne :

fenetre_interne=WINDOW.new(begin_y_fenetre_support+1,begin_x_fenetre_support+1,taille_y_fenetre_support-2,n);

Il ne faudra pas oublier de créer une paire de couleurs spécifique pour la fenêtre_interne et ensuite de l’utiliser comme ci-dessous. Jusqu’a maintenant notre fonction en construction ressemble à ceci :

def loading(fenetre_support,i)
{
    begin_y_fenetre_support=Ncurses.getbegy(fenetre_support);
    begin_x_fenetre_support=Ncurses.getbegx(fenetre_support);

    taille_y_fenetre_support=Ncurses.getmaxy(fenetre_support);
    taille_x_fenetre_support=Ncurses.getmaxx(fenetre_suppor);

    # On calcule l'accroissement.
    if((i*(taille_x_fenetre_support-2))/100 < 1)
        n=1;
    else
        n=(i*(taille_x_fenetre_support-2))/100;
    end

    fenetre_interne=WINDOW.new(taille_y_fenetre_support-2,n,begin_y_fenetre_support+1,begin_x_fenetre_support+1);
    fenetre_interne.bkgd(Ncurses.COLOR_PAIR(X));

    stdscr.refresh;
    fenetre_interne.refresh;
    Ncurses.delwin(fenetre_interne);
}
b) Oû allons nous placer le texte explicatif du chargement ?

Nous allons nous servir de l’espace que nous avons laissé sur la fenêtre support afin d’écrire le texte explicatif. Attention : le texte explicatif ne doit pas être plus grand que la largeur de la fenêtre support ! Il est fortement recommandé de choisir un texte concis et expliquant de maniére claire l’action se déroulant, exemple :

"Unpacking emacs pkg ..."

Nous pouvons mettre ce texte au milieu et en haut ou en bas. Si vous désirez le mettre en haut vous devez utiliser la fonction mvwaddstr (ou mvwprintw) (voir section IV) de la manière qui suit :

Ncurses.mvwaddstr(fenetre_fille, 0, (taille_x_fenetre_support-text.length+2)/2,"mon_texte");

Autement si vous souhaitez le mettre en bas vous devez utiliser la fonction mvwaddstr de la maniére suivante :

Ncurses.mvwaddstr(fenetre_fille, taille_y_fenetre_support, (taille_x_fenetre_support-$text.length+2)/2,"mon_texte");
NOTE

Voir astuce 2).

Maintenant notre fonction en construction ressemble à ceci :

def loading(fenetre_support,i,texte_explicatif)
{
    begin_y_fenetre_support=Ncurses.getbegy(fenetre_suppport);
    begin_x_fenetre_support=Ncurses.getbegx(fenetre_support);

    taille_y_fenetre_support=Ncurses.getmaxy(fenetre_support);
    taille_x_fenetre_support=Ncurses.getmaxx(fenetre_support);

    # On calcule l'accroissement.
    if((i*(taille_x_fenetre_support-2))/100 < 1)
        n=1;
    else
        n=(i*(taille_x_fenetre_support-2))/100;
    end

     
    fenetre_interne=WINDOW.new(taille_y_fenetre_support-2,n,begin_y_fenetre_support+1,begin_x_fenetre_support+1);
    fenetre_interne.bkgd(Ncurses.COLOR_PAIR(X));

    fenetre_support.erase;

    Ncurses.mvwaddstr(fenetre_fille, 0, (taille_x_fenetre_support-texte_explicatif.length+2)/2," " + texte_explicatif + " ");

    stdscr.refresh;
    fenetre_interne.refresh;
    Ncurses.delwin(fenetre_interne);

}
NOTE

Avant chaque écriture sur la fenêtre_support on la “nettoie” des éventuels anciens caractères écrits avec la fonction erase.

c) Oû allons-nous mettre le pourcentage de chargement ?

Là aussi nous allons nous servir de l’espace que vous avons laissé exprès auparavant. Si vous avez placé le texte explicatif en haut nous allons placer le pourcentage de chargement en bas. Par contre si vous avez placé le texte explicatif en bas nous allons placer le pourcentage en haut.

Nous allons afficher le pourcentage de chargement avec la fonction mvwprintw car cette fonction permet d’afficher des entiers (contrairement à la fonction mvwaddstr). Nous allons utiliser la fonction mvwprintw de la façon suivante si vous voulez mettre le pourcentage en bas:

Ncurses.mvwprintw(fenetre_interne,taille_y_fenetre_support,(taille_x_fenetre_support-i.to_s.length+3)/2," %d%s ",i,"%");

Si vous voulez mettre le pourcentage en haut nous allons procéder ainsi :

Ncurses.mvwprintw(fenetre_interne,0,(taille_x_fenetre_support-i.to_s.length+3)/2," %d%s ",i,"%");

Voici au final à quoi ressemble notre fonction :

def loading(fenetre_support,i,texte_explicatif)
{
    begin_y_fenetre_support=Ncurses.getbegy(fenetre_support);
    begin_x_fenetre_support=Ncurses.getbegx(fenetre_support);

    taille_y_fenetre_support=Ncurses.getmaxy(fenetre_support);
    taille_x_fenetre_support=Ncurses.getmaxx(fenetre_support);

    # On calcule l'accroissement.
    if((i*(taille_x_fenetre_support-2))/100 < 1)
        n=1;
    else
        n=(i*(taille_x_fenetre_support-2))/100;
    end

    fenetre_interne=WINDOW.new(taille_y_fenetre_support-2,n,begin_y_fenetre_support+1,begin_x_fenetre_support+1);
    fenetre_interne.bkgd(Ncurses.COLOR_PAIR(X));

    fenetre_support.erase;

    Ncurses.mvwaddstr(fenetre_fille, 0, (taille_x_fenetre_support-texte_eplicatif.length+2)/2," " + texte_eplicatif + " ");
    Ncurses.mvwprintw(fenetre_interne,taille_y_fenetre_support,(taille_x_fenetre_support-i.to_s.length+3)/2," %d%s ",i,"%");

    stdscr.refresh;
    fenetre_interne.refresh;
    Ncurses.delwin(fenetre_interne);
}
NOTE

Par la suite vous pouvez enjoliver la barre de chargement en rajoutant une ombre (faire cela à la création de la fenêtre_support) et un effet box (faire cela dans la fonction) (voir section V).

d) Comment utiliser notre fonction ?

Cette fonction s’utilise après chaque action fait par notre logiciel. Par exemple si notre logiciel à pour but de décompresser tous les fichiers d’un répertoire alors après chaque décompression la fonction loading sera appelée avec comme valeur i une valeur proportionelle au travail déja fourni.

Exemple: si il y a 50 fichiers compressés dans le répertoire, à chaque fichier décompressé la barre de chargement augementera de + 2% jusqu’a 100%. Cette gestion peut se faire automatiquement, il suffit de récupérer le nombre de fichiers présents dans le répertoire, de diviser 100 par le nombre ainsi obtenu, de tranformer le résultat obtenu en entier (si celui-ci est un nombre virgule), et ensuite de passer la valeur multipliée du résultat par le nombre de fichiers traités à la fonction loading. Voici un petit exemple d’utilisation basé sur ce qui est dit au-dessus :

fichiers=`ls .`;
nbr_fichiers=fichiers.count("\n");
j=1;

for i in fichiers
    system("bunzip2  "+i);

    # Ceci nous assure un fonctionnement propre :)
    if((100/nbr_fichiers).round*j<100 && j==nbr_fichiers) || ((100/nbr_fichiers).round*j>100))
        x=100;
    else
        x=(100/nbr_fichiers).round*j;
    end

    loading(fenetre_support,x,i);
    j=j+1;
end

ATTENTION : Il est nécessaire de vérifier avant chaque appel de la fonction loading que la valeur x (en prenant l’exemple ci-dessus) est toujours supérieure ou égale à 1 et toujours inférieure ou égale à 100.

Les liens

ruby

autres langages

autres

La liste de correspondances touche/code ASCII : http://www.asciitable.com

 
ncurses_ruby.txt · Dernière modification: 19/10/2005 17:27
 
Recent changes RSS feed Creative Commons License Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki