© Jean-Pierre ANGHEL - 2004

 

 

FOX + RUBY

 

=

 

FXRuby

 

 

Par l’exemple


Table des matières

Table des matières. 2

Introduction. 4

Chapitre 1. 6

Bref historique de Ruby. 6

Bref historique de Fox. 6

Où trouver Ruby ?. 6

Avec quel éditeur de texte ?. 7

Quelles connaissances faut-il avoir ?. 7

Avantages divers. 7

Présentation des composants visuels. 7

Présentation des aménageurs d'espace. 8

Chapitre 2. 9

Les fenêtres et tout ce qui va avec. 9

Un peu de Ruby. 10

Particularités de FXRuby. 11

Chapitre 3. 12

Le bouton simple. 12

Un peu de ruby (1) : 15

Les commentaires et substitutions. 15

Exemple général sur les boutons. 16

Particularités de FXRuby : 20

Les cadres et séparateurs. 22

Les boîtes à grouper. 22

Les cases  à cocher. 23

Les boutons-radio. 23

Les boutons-bascule. 23

Les boutons avec flèches incorporées. 23

Les bulles d'aide. 24

La barre de statut 24

Un peu de Ruby (2) : 24

Les variables. 24

Les opérateurs. 25

Les méthodes. 26

Chapitre 4. 28

Les canevas. 28

Un peu de Ruby. 32

Les blocs. 32

Les itérateurs. 33

Les  boucles. 33

Les expressions conditionnelles. 34

L'expression 'case' 35

Mots réservés de Ruby. 35

Chapitre 5. 37

Les menus et les dialogues: 37

Un peu de Ruby : 44

Les méthodes singletons. 44

Les tableaux : 45

Les tables de hachage : 46

Chapitre 6. 47

Le composant "Boîte de texte" et divers gadgets : 47

Un peu de ruby : 53

Les chaînes de caractères. 53

Les nombres. 54

Chapitre 7. 55

Les images : 55

Un peu de Ruby : 59

Les classes. 59

Les accesseurs. 59

Les modules. 60

Les mixins. 60

Chapitre 8. 61

Les boîtes à lister, les répertoires : 61

Un peu de Ruby : 65

Les intervalles. 65

Les variables globales prédéfinies. 66

Chapitre 9. 67

Un peu de Ruby : 71

Les délimiteurs. 71

Les expressions rationnelles. 72

Annexe A.. 74

Annexe B.. 165

Notes. 167

 


 

Introduction

 

Encore un livre d'informatique !!!

  Eh oui, encore un. Pourquoi ? parce qu' il y a pléthore de bouquins pour les sujets connus et très peu sur les sujets peu … porteurs, dirons-nous. Parce que le Français est généralement viscéralement fermé à la langue anglaise, ce qui le prive de pas mal de connaissances et d'évolutions (Combien ont arrêté de faire des macros sur le tableur de Monsieur Bill depuis qu'elles sont en anglais ?) et que "le" livre sur le sujet qui nous préoccupe est dans la langue de Shakespeare (c'est comme ça qu'on dit et du coup on dirait que c'est ce brave homme qui a inventé l'anglais !)

 

Quel est le but de ce livre ?

  Faire découvrir un langage puissant et très intéressant : Ruby, facilement mis en œuvre à l'aide d'une librairie graphique également originale: Fox.

 

Qu'est-ce que Ruby ?

  Ruby est un  nouveau (pas tant que ça comme on le verra plus loin) langage de programmation pour écrire  de petits (pas si petits comme on etc…) programmes appelés par les anglais des scripts. C'est un langage interprété. C'est aussi un langage totalement orienté objet. C'est un langage qui a été porté sur de nombreuses plates-formes. Et c'est enfin un langage entièrement gratuit.

 

Qu'est-ce que Fox ?

  Fox est un outil de développement graphique construit en C++. Il offre un large éventail de contrôles et autres composants graphiques. Il a aussi été porté sur nombre de plates-formes et se remarque par sa rapidité et sa facilité d'utilisation. Et c'est de plus une interface entièrement gratuite.

 

A qui s'adresse ce livre ?

  A ceux qui ont tout de même quelques rudiments de programmation. A tous ceux qui veulent écrire de courts programmes, qui veulent apprendre la programmation OO (orientée objet) en utilisant une interface graphique, qui ne veulent pas enrichir encore plus Monsieur Bill,  qui n'ont pas de budget à consacrer à un énième langage "visuel"  ou qui ne veulent pas pirater (si, si , y'en a !) etc… Les chevronnés n'apprendront certainement pas grand-chose, voire rien du tout, mais celui qui est interessé par les "bidouilles" sur son ordinateur et ne sait comment les faire y trouvera une réponse. A propos de bidouille, une question à ceux qui programment : comment feriez-vous pour remplacer la chaîne "truc" par la chaîne "machin" dans tous les fichiers d'un même répertoire, munis des extensions "c" et "h" et sauvegarder les anciens fichiers avec l'extension '.bak' ? Voici le résultat tel qu'il est présenté dans le guide de l'utilisateur Ruby :

ruby -i.bak -pe 'sub "truc","machin"' *.[ch]

 

Ruby ça "marche" sur quoi ?

  Ruby fonctionne à peu près sur toutes les plates-formes. MSDOS, Windows 9x, OS2, Unix, Mac (livré avec l'OS). Et sur Linux . Ce qui est encourageant pour les petits budgets du paragraphe précédent : ils peuvent avoir l' OS et les programmes pour pas un sou. En parlant de petit budget les enseignants vont certainement se reconnaître, mais tant pis (ou tant mieux !). Ce qui ne veut pas dire d'ailleurs que Ruby, Fox  etc.. soient en quelque sorte l'informatique du pauvre. Loin de là cette idée car, comme vous le verrez en l'étudiant, il n'a absolument rien à envier, mais alors rien du tout, aux autres langages payants.

 

 

  Et en conclusion de cette introduction, comme il est certain qu' un  langage dédié uniquement au "script" n'intéresserait que des spécialistes, il est urgent de dire que Ruby est accompagné de deux interfaces graphiques, Tcl/Tk et Fox, qui permettent de faire (presque) du Visual machin-chose. Et comme on ne peut pas tout faire, ce livre est axé sur la programmation Ruby avec Fox,  sous Windows.

 

 


Chapitre 1

 

Bref historique de Ruby

  Les langages Perl, Python, Smalltalk existaient déjà et Ruby est un peu un  mélange de tout ça, en mieux ça va de soi.

  Ruby est né au Japon en 1993 des envies d'un programmeur d'avoir un langage "à sa main" dirons-nous. Yukihiro Matsumoto est son nom (Matz pour les intimes).  Il aura fallu attendre trois ans pour que ce langage prenne forme et se développe au Japon. Il faudra encore attendre quelques années pour qu' il soit traduit en anglais, et l'an 2000 pour un premier livre occidental. Et en français il n'y en a qu'un , celui de chez O'Reilly "Ruby in a nutshell" (encore de l'anglais !, mais uniquement dans le titre heureusement).

Pourquoi le nom de Ruby ? tout simplement parce qu'il existait déjà Perl … astucieux n'est-il pas?

 

Bref historique de Fox

  L'interface graphique Tcl/Tk existait sur de nombreuses plates-formes jusqu'à ce que Jeroen van der Zijp se dise in petto (il y a de l' anglais partout, je peux bien mettre un peu d'italien, non ?), que cette extension graphique avait vieilli et qu'il était temps d'en construire une autre portable sur toutes les plates-formes. Ce qu'il fit et fort bien en créant Fox sur une idée qui naquit au printemps de 1997. Fox est une librairie graphique développée en C++ pour rendre la programmation simple, efficace et indépendante de la  plate-forme. Deux des aspects les plus spectaculaires de cette librairie se situent d'une part dans la communication directe entre composants et d'autre part dans le gestionnaire d'arrangement des composants entre-eux et dans une fenêtre, sans précison de coordonnées. 

Lyle Johnson adapta Fox à Ruby, et continue d'ailleurs à le faire, pour le bonheur de tous.

La plupart des exemples de ce livre sont inspirés des programmes en anglais disponibles sur le Net. Mais comment expliquer le fonctionnement d'un bouton si ce n'est en en créant un tout seul dans une simple fenêtre ? Hormis les titres en français, le code est évidemment le même pour la suite.

 

Où trouver Ruby ?

  Ruby pour Windows est disponible sur Internet sur le site suivant : http://www.pragmaticprogrammer.com/ruby/downloads/ruby-install.html

Dans la version Windows le fichier s'installe automatiquement. A noter que sur NT, 2000 et XP il faut se loger en administrateur pour renseigner le chemin d'accès (le 'path' anglais)

Le site officiel de ruby est http://www.ruby-lang.org

Pour des informations supplémentaires voir sur http://www.rubycentral.com

Comme livre il n'existe à ce jour, en dehors de celui cité plus haut, que la "bible" de David Thomas et Andrew Hunt : "Programming Ruby : The Pragmatic Programmer's Guide" chez Addison-Wesley. Disons aussi que ce livre est en accès libre sur Internet sur http://www.pragmaticprogrammer.com/ruby/downloads/book.html

Tout ceci est évidemment, et malheureusement, en anglais.

 

Avec quel éditeur de texte ?

  Avec celui que l'on veut, mais il vaut mieux en avoir un qui reconnaîsse la syntaxe, les mots clefs de Ruby comme Scite qui fonctionne sous Windows. De plus lui aussi est non seulement gratuit mais dispose d' une multitude de langages. Vraiment inutile de s'en priver.

Scite est d'ailleurs fourni avec la distribution Windows précédemment citée.

  En dehors de l'éditeur de texte pour taper des programmes entiers, il y a  la possibilité de se servir de IRB (Interactive ruby), un utilitaire en ligne de commande. IRB est très utile pour tester quelques lignes de code sans utiliser le mode graphique, Ruby fonctionne alors dans une fenêtre DOS. IRB est bien entendu fourni avec la distribution de Ruby pour Windows.

 

Quelles connaissances faut-il avoir ?

    Il vaut mieux connaître un langage objet classique. Si on programme déjà dans un des langages de "script" cités plus haut, ou bien avec C++,  pas de problèmes d'adaptation . Si on vient de Pascal ou de Delphi, ça sera un peu plus dur mais rien d'insurmontable surtout avec les composants visuels de Fox.

 

Avantages divers.

  Outre le fait que Ruby et Fox soient gratuits, il existe un autre avantage, énorme, qui est celui de la stabilité. En effet sont disponibles sur le Net les versions stables ainsi  que celles à l'essai pour les bêta-testeurs ou les curieux. Les versions en cours de modification sont les versions dont le deuxième chiffre est impair. La version en cours au moment de la rédaction du présent ouvrage est la 1.8 qui est "vieille"  d’un an environ. C'est tout de même un avantage énorme d'avoir un logiciel garanti sans  bogues, ou tout du moins dans le domaine du raisonnable. Evidemment ceci n'est malheureusement possible qu'avec des logiciels libres, les lois du marché étant impitoyables et stupides : achèteriez-vous le dernier modèle de voiture uniquement pour la frime, si vous deviez sortir la boîte à outils à tous les feux rouges à cause d'un bogue dans le moteur ? Mais le monde est ainsi fait et les gens continueront d'acheter la dernière version mise sur le marché par monsieur Bill, parce qu'on leur a fait croire qu'elle est encore meilleure que la précédente. Et les mêmes paieront sans barguigner les rectificatifs et corrections de bogues un mois plus tard…

 

Présentation des composants visuels.

  D'accord, on appelle ça maintenant des "widgets" (de 'Windows' et 'gadgets'). Ca fait bien, très à la page, et ça fait nager le débutant pendant quelque temps jusqu'à ce qu' un initié ou un magazine lui dévoile le secret. De toutes façons ne dites pas à un  bouton qu'il n'est qu' un simple gadget, il ne vous répondra même pas.

Nous verrons donc dans les chapitres suivants les composants visuels de Fox un à un, mais commençons donc par les présentations:

Les fenêtres

Les cadres

Les boutons 

Les cases à cocher

Les boutons radios

Les canevas

Les potentiomètres

Les boîtes de dialogue

Les fenêtres MDI (Multiple Documents Interface)

Les fenêtres modales ou non

Les boîtes à grouper

Les boîtes de saisie

Les onglets

Les feuilles de calcul

Etc...etc...

 

Tout au long de cet ouvrage vont alterner les présentations de composants visuels  et le B.A. BA de Ruby. Car il va de soi que si Ruby peut fonctionner sans FOX, l'inverse n'est pas vrai.

 

Présentation des aménageurs d'espace.

Nous avons dit plus haut qu'il n'était pas nécessaire de préciser les coordonnées des composants dans une fenêtre ou à l'intérieur d'un autre composant, comme c'est le cas dans d'autres langages ou interfaces graphiques. En effet, la programmation visuelle était une révolution mais le programmeur passe son temps à aligner ses composants, le plus souvent au pixel près, et l'utilisateur peut tout démolir en choisissant une autre fonte par exemple. Dans Fox ce sont les gestionnaires d'aspect qui se chargent de tout ce travail, à l'aide de quelques constantes indiquant la position souhaitée, et le résultat visuel est plus que satisfaisant comme vous allez le voir bientôt. Il est bien évident que si l'utilisateur redimensionne l'image, l'aspect restera le même pour tous les composants.

·        FXPacker . L' "emballeur" place ses composants enfants (c'est à dire ceux qui vont dépendre de lui pour l'emplacement et les commandes) dans son rectangle intérieur en se débrouillant pour les placer le long de ses quatre côtés.

·        FXTopWindow . C'est la fenêtre toute simple qui fonctionne comme le composant ci-dessus mais en respectant les commandes de placement des composants. Pour de simples dialogues ou  sous-fenêtres, c'est le composant le plus simple.

·        FXHorizontalFrame . C'est un cadre qui place ses enfants de la gauche vers la droite, ou inversement. Les composants s'afficheront suivant l'ordre de leur déclaration dans le programme.

·        FXVerticalFrame . La même chose que le composant horizontal, mais en vertical.

·        FXMatrix . La matrice place ses enfants en lignes et colonnes.Elle peut travailler soit comme orienté-colonnes, soit orienté-lignes, le deuxième cas étant la normale.

·        FXGroupBox. Il est comme FXPacker, mais offre en plus un élégant cadre autour de ses enfants avec un titre optionnel. Si les enfants sont des boutons-radio il n'en accepte qu'un de coché à la fois.

·        FXSplitter . Le "diviseur" divise un espace en deux parties, horizontalement ou verticalement et permet à l'utilisateur d'agrandir l'une d'elles.

 

On trouvera en annexe B la description des constantes de présentation pour tous ces gestionnaires.

 


Chapitre 2

 

 

Les fenêtres et tout ce qui va avec.

 

A tout seigneur tout honneur, la fenêtre est reine. Où mettre des composants si ce n'est dans une fenêtre. Commençons donc par la fenêtre toute simple et par un exemple tout aussi simple pour ne pas effaroucher le futur utilisateur.

 

***************************************************************************

1- require 'fox'

2- include Fox

3- monApp = FXApp.new

4- maFenetre = FXMainWindow.new(monApp, "Coucou", nil,nil,DECOR_ALL,0,0,200,20)

5- monApp.create

6- maFenetre.show

7- monApp.run

 

***************************************************************************

 

Explications :

Ligne 1 : on indique que la librairie graphique Fox est nécessaire.

Ligne 2 : n'est pas obligatoire, mais permet d'avoir disponibles tous les noms déclarés dans le module Fox.

Ligne 3 : une instance d' application est créée par appel de la méthode new.

Ligne 4 : on crée une instance de fenêtre et on lui passe en paramètre le nom de l'application (une variable pointeur) , le texte entre guillemets devant figurer sur le bandeau supérieur de la dite fenêtre ici, "Coucou" . Viennent ensuite 3 autres paramètres dont nous parlerons plus tard. Puis le point d'ancrage de la fenêtre, ici x=0 et y=0, donc la fenêtre est en haut à gauche de l'écran, les coordonnées débutant effectivement à cet endroit-là. Enfin 2 autres nombres qui sont la largeur et la hauteur de la fenêtre en pixels.

Ligne 5 :  on crée l'application elle-même.

Ligne 6 : par défaut toutes les fenêtres en FXRuby sont invisibles, il nous faut donc montrer maFenetre, ce que fait la méthode .show.

Ligne 7 : il n'y a plus qu'à lancer en appelant la méthode 'run' qui va boucler jusqu'à ce que l'on quitte le programme : et que ça roule !…

 

Si tout a bien fonctionné vous obtenez une image semblable à celle-ci :


 

 

 


Fig 1 - Coucou1.png

 

Vous remarquerez que pour une fois il n'y a pas de "Hello, world", ça change…

Vous remarquerez aussi que la petite fenêtre est ancrée dans le coin supérieur gauche de l'écran, peut-être même l'avez-vous cherchée si la définition de votre écran est importante. Pas de panique, nous verrons ce que l'on peut faire.

  Il n'y a pas grand chose d'autre à dire pour le moment si ce n'est qu' une grosse partie des initialisations de constantes est faite dans la classe FXWindow.

 

La version complète de la méthode 'new' est la suivante :

FXMainWindow.new(app, title, icon=nil, miniIcon=nil, opts=DECOR_ALL,

 x=0, y=0, width=0, height=0, padLeft=0,

padRight=0, padTop=0, padBottom=0, hSpacing=4, vSpacing=4)

 

Les paramètres 'app' et 'title' désignent respectivement un pointeur sur l'application et le titre de la fenêtre. Les autres paramètres sont facultatifs, nous en parlerons plus loin.

 

Un peu de Ruby

 

  Tout d'abord, posons-nous la question de savoir ce qu'est un objet.

"Objets inanimés avez vous donc une âme ?"

En programmation non seulement les objets ne sont pas forcément inanimés mais ils savent faire de naissance beaucoup de choses, comme se déplacer, tourner, se cacher, réapparaître etc.. à condition toutefois qu'on leur dise quoi faire, sinon ils ne font rien…

Imaginez un  bloc de code dans lequel se trouvent des données modifiables (des variables) et du code actif sous forme de procédures ou de fonctions pour agir sur ces données. Et bien le tout forme ce qu'on appelle un "objet". Histoire de changer et bien marquer la différence avec la programmation procédurale, les variables deviennent des "Attributs" en Ruby (et "Propriétés" dans d'autres langages), les procédures et les fonctions se réunissent et deviennent des "Méthodes". Bref tout ce bloc de code se retrouve dans une zone mémoire et n'en bouge plus, mais tout est là prêt à l'action. Imaginons que nous ayons un objet "Tasse". Contrairement à votre tasse du petit déjeuner, qui ne sait pas faire grand chose d'elle même, celle-ci a été dotée de méthodes lui permettant par exemple de se déplacer, de se renverser etc… et d'attributs lui indiquant sa capacité, sa température maximale d'utilisation, sa couleur etc… Mais un tel objet n'est en quelque sorte qu'un modèle, une matrice inopérante, une 'Classe'. Pour utiliser un tel objet il faut passer par son clône, qu'on appelle 'instance', une copie "vivante" en quelque sorte, mise au monde par la méthode 'new'. Pour indiquer l'utilisation de tel ou tel attribut ou bien de telle ou telle méthode, nous indiquons le nom de l'instance suivit d'un point puis du nom de l'attribut ou de la méthode :

                           maTasse = tasse.new                    # création de l'instance

                           maTasse.couleur=rouge               # attribution de la couleur à l'attribut 'couleur'          

                           maTasse.tourne(90)                     # ordre de tourner de 90 degrés.

 

  En Ruby il n'y a pas de déclaration préalable de variables. Ces dernières ne sont pas typées, c'est à dire n'acceptant qu'un seul type de données. La mémoire est gérée automatiquement, si un objet est détruit il n'est pas nécessaire de libérer la mémoire.

En Ruby tout est objet (y compris un simple nombre). Bien entendu on peut créer ses propres objets, et les faire "descendre" d'un objet existant. Le nouvel objet va hériter des attributs et méthodes de son ascendant.

 

  Un module est une partie de code qu'on ne charge en mémoire que si nécessaire, sauf bien entendu les modules qui font tourner la mécanique et sont chargés d'office.

  'require' est une méthode du module 'kernel', c'est à dire du noyau même de Ruby, qui indique que le module dont le nom suit est obligatoire.

 

Particularités de FXRuby

Nous avons vu que pour créer une instance d'objet il fallait faire appel à la méthode 'new'; en FXRuby il y a une distinction faite entre l'instanciation d'un objet et sa création dans le système. Pour créer l'objet Windows associé à l'objet FXRuby déjà construit, nous appelons la méthode 'create'.

 

  Pour ceux qui ont commencé à programmer en Pascal disons qu'une 'Classe' est tout simplement une déclaration de 'Type',  une 'instance' équivaut à une variable ('Var'), un objet c'est un 'Record' dans lequel on a mis des procédures et des fonctions, 'require' remplace la clause 'Uses', et enfin un module ressemble fort à une 'Unit'.

 


Chapitre 3

 

Les boutons et autres composants qui vont avec.

 

Le bouton simple

 

 Une fenêtre sans rien qui bouge à l'intérieur, c'est à dire quelque chose d'actif, ce n'est pas très pratique, ni très utile. Nous allons donc lui rajouter un bouton.

 

***************************************************************************

1- require 'fox'

 

2- include Fox

 

3- monApp = FXApp.new

4- maFenetre = FXMainWindow.new(monApp, "Coucou2",nil,nil,DECOR_ALL,0,0,200,20)

4 bis - monBouton = FXButton.new(maFenetre, "Cliquez donc  ici !")

5- monApp.create

6- maFenetre.show

7- monApp.run

***************************************************************************

 

Il suffit de rajouter une instance de bouton en ligne 4bis en lui passant en paramètres 'maFenetre' et l' intitulé du bouton. Et comme de cliquer dessus ne produit aucun effet, il ne reste qu' à rajouter une commande :

 

4 ter - monBouton.connect(SEL_COMMAND) {exit}

 

Même les plus réfractaires à l' anglais auront compris qu'avec le mot 'exit' , le bouton vous fait tout quitter. Alors contemplez-le bien , avant de cliquer dessus comme il vous y invite.

 

 

 


 


Fig 2 - Coucou2.png

 

 

Ouais, mais moi j' ai l'habitude d'avoir une icône sur mon bouton ! c'est plus joli !

D'accord, d'accord on y va :

 

***************************************************************************

 

1- require 'fox'

2- include Fox

3- monApp = FXApp.new("Coucou3", "FoxTest")

4- main = FXMainWindow.new(monApp, "Hello", nil, nil, DECOR_ALL)

# Charge une icône .PNG qui sera attachée au bouton. Notons que le second

# argument de la méthode .new nécessite seulement un flux d'octets (par

# exemple une chaîne); ici nous lisons les octets à partir d'un fichier sur

# le disque.

 

5- icon = FXPNGIcon.new(monApp, File.open("fox.png", "rb").read)

 

# On construit le bouton en tant qu'enfant de la fenêtre principale.

6- FXButton.new(main, "&Coucou!\tOllé, FOX c'est super!\nCliquez sur l'icone pour quitter 'monApp'.", icon, monApp, FXApp::ID_QUIT, ICON_UNDER_TEXT|JUSTIFY_BOTTOM)

 

7- FXTooltip.new(monApp)

8- monApp.create

9- main.show(PLACEMENT_SCREEN)

10- monApp.run

 

***************************************************************************

 

Ouh là là, tout a changé. Mais non, pas tant que ça, et puis nous ne sommes pas là pour rigoler. Voyons les changements :

 

·        Ligne 3 : on découvre que les paramètres de FXApp.new sont facultatifs. Le premier désigne le nom de l'application et le second la clef vendeur, c'est à dire le copyright ou tout ce que vous voulez. Ces deux chaînes sont en relation avec la partie de la base de registres Windows dédiée à Fox . Nous y reviendrons plus loin.

·        Ligne 4 : là aussi 2 nouveautés : le point d'ancrage et les dimensions de la fenêtre ne sont plus là. La fenêtre va se dimensionner automatiquement  en fonction de son ou ses occupants, ici le bouton qui lui-même s'autodimentionnera en fonction des dimensions de l'image. Les deux 'nil' sont là pour signifier qu'il n'y a ni icône ni petite icône, mais par contre une option : DECOR_ALL qui signifie all of the above.

·        Ligne 5 : on charge une image à partir d'un fichier .png. Le paramètre "rb" n'a rien à voir avec l'extension des fichiers Ruby. Voir plus loin dans "Un peu de Ruby".

·        Ligne 6 : là, on s'accroche :

- 1er paramètre, on attache le bouton à la fenêtre. Le 'main' de la ligne 4.

      - 2ème paramètre le titre et dans la foulée, suivant le " \t" (de tabulation), le texte d' une info- bulle.

       - 3ème paramètre le pointeur de l' icône du bouton.

       - 4ème paramètre le pointeur de l'application,

       - et 5ème paramètre la commande liée au bouton, passée ici directement avec l'objet dont il dépend. Le signe '&' (esperluette pour les gens calés) précédant le 'C' du mot Coucou, introduit un raccourci clavier pour le bouton, et l'appui simultané des touches 'CTRL ' et 'c' équivaudra à un clic de souris sur le bouton.

·        Ligne 7 : C'est ici qu'on fait la bulle, tool tip en anglais. L' info-bulle va apparaître sous le curseur de la souris, à condition évidemment de laisser celui-ci immobile une seconde au dessus du bouton.

Avez-vous remarqué qu'autrefois, lorsque les ordinateurs étaient en mode texte, il y avait des intitulés dans des menus déroulants (déroutants ?). Puis le mode graphique est arrivé avec son cortège d'icônes plus ou moins explicites, plutôt moins d'ailleurs puisqu'on a jugé indispensable de leur donner une explication supplémentaire à l'aide de 26 icônes connues depuis quelques années déjà et qui ont pour nom l'alphabet… Fox résoud ce problème en rendant possible l'affichage d'une info-bulle explicative sur chaque composant visuel et/ou par un affichage au bas de l'écran.

·        Ligne 9 : un paramètre de plus et on a la fenêtre au milieu de l'écran. C'est pas beau ça ?

 Le paramètre en question est déclaré dans FXTopWindow qui a pour héritier FXMainWindow. Voir annexe 'A'  pour les détails.

 

Et comme résultat nous avons la figure suivante :


 

 

 


Fig 3 - Coucou3.png

 

 

A remarquer tout de même que le bouton est en fait l'image toute entière et ce parce que nous n'avons pas donné de dimensions particulières ni à la fenêtre ni au bouton dans leur initialisation.

  Bref vous vous êtes aperçu que certain paramètres sont facultatifs. Bravo, vous avez bien deviné. La version intégrale se presente comme suit :

 

FXButton.new(parent, text, icon=nil, target=nil, selector=0, opts=BUTTON_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD)

 

Les valeurs des paramètres portés dans la présentation ci-dessus sont des valeurs par défaut.

DEFAULT_PAD valant zéro.

 

Allez ! On va décortiquer tout ça  paramètre par paramètre :

1-     'parent' : C'est le parent. Généralement un cadre, lui-même fils d'un autre cadre ou d'une fenêtre.

2-     'text' : Le titre du bouton. En faisant précéder une lettre du caractère"&", on crée un raccourci clavier. Par exemple "&Cliquez ici" souligne la lettre 'C' sur le bouton et permet le raccourci 'Ctrl_C'. La tabulation "\t" en FXRuby permet d'insérer le texte de l' info-bulle d'explication dans la foulée. Une autre tabulation permet d'afficher une aide différente dans la barre d'état.

3-     'icon' : Un pointeur vers un dessin.

4-  'target' : La cible d'un éventuel message.

5-  'selector' : Soit on indique ici le message destiné à être émis par le bouton, par exemple "FXApp::ID_QUIT"  soit on indique '0' (zéro) et on utilise la méthode 'connect', si par exemple il y a un traitement supplémentaire à faire. Voir à ce sujet la description du module Responder2.

6- 'opts' : Les options d'apparence et de positionnement du bouton. Voir les constantes prédéfinies déclarées dans FXWindow.

7 et 8 - coordonnées 'x' et 'y' du coin supérieur gauche.

9 - 'width' : largeur du bouton.

10 - 'height' : hauteur du bouton.

11 à 14 - Comme dans FXMainWindow, il s'agit de la largeur du passe-partout, largeur variable selon le côté. Le moyen le plus efficace pour comprendre "comment ça marche", c'est d'essayer. Ajoutez donc les paramètres d'ancrage et de dimensions dans la fenêtre principale, avec une valeur nulle, et ceux du passe-partout : …DECOR_ALL, 0,0,0,0,10,10,10,10). Si vous avez bien suivi vous obtenez un passe-partout de 10 pixels de large. Nous pouvons faire de même avec le passe-partout de 'main', les deux étant indépendants, les valeurs s'ajoutant entre elles.

 

 

Un peu de ruby (1) :

 

Les commentaires et substitutions

 

  Vous avez sans aucun doute compris que les commentaires en Ruby se font à la suite d'un caractère '#'. C'est exact. Il y a une autre façon de faire, pour les blocs de texte qui comportent plusieurs lignes, qui consiste à commencer une ligne par '=begin' et terminer le texte par une ligne '=end'.

 

  En dehors de la tabulation il existe d'autres substitutions précédées du caractère '\' dans une chaîne de caractères délimitée par l'apostrophe double ("chaîne"). Ils viennent en majorité d'un héritage de l'utilisation autrefois des 20 premiers codes ASCII  sur télétype :

  \a : la sonnerie (Bell)

  \b : retour arrière (Backspace)

  \e : échappement (Escape)

  \f : (Formfeed)

  \n : changement de ligne (Newline ou LineFeed)

  \r : retour chariot (Return)

  \s : espace (Space)

  \t : tabulation (Tab)

  \v : tabulation verticale (Vertical tab)

  \nnn : nnn en octal

  \xnn : nn en hexadécimal

  \cx  : contrôle-x

  \C-x : contrôle-x

  \x : x

 #{expr} : valeur de  expr

 

A noter aussi que pour afficher un caractère de contrôle dans une chaîne, il faut le faire précéder du caractère '\'. Ex : ' " ' qui délimite une chaîne, nécessite pour être imprimé dans une chaîne d'être comme ceci : ' \" '  . Et '\\' affiche donc '\'.

  Il existe aussi une syntaxe particulière en entrée pour les chaînes, tableaux et expressions rationnelles. Tous ces littéraux débutent par le signe '%' suivi d'un simple caractère identifiant le type de littéral.

  %q                           chaîne entre apostrophes simples (').

  %Q                          chaîne entre apostrophes doubles (").

  %w                           tableau de chaînes.

  %r                            expression rationnelle (regular expression).

  %x                           ligne de commande (Shell command)

 

 

  Dans la lecture du fichier .PNG nous avons vu qu'il y avait passage de deux paramètres dans 'file.open(…)'. Le premier est le nom du fichier, le deuxième ("rb") un des modes d'ouverture décrit dans le tableau ci-dessous :

 

 

 

 

Mode

Signification

``r''

En lecture seulement, démarre au début du fichier (mode par défaut).

``r+''

En lecture / écriture, démarre au début du fichier.

``w''

En écriture seulement, tronque le fichier existant à une longueur de zéro ou bien crée un nouveau fichier pour écrire.

``w+''

En lecture/écriture, tronque le fichier existant à une longueur de zéro ou bien crée un nouveau fichier pour lire et écrire.

``a''

En écriture seulement, démarre à la fin du fichier existant, sinon crée un nouveau fichier pour écire.

``a+''

En lecture/écriture, démarre à la fin du fichier existant, sinon crée un nouveau fichier pour lire/écire.

``b''

(DOS/Windows seulement) Mode fichier binaire (peut être joint à un des modes ci-dessus).

 

 

 

Exemple général sur les boutons.

 

  Pour se rendre compte de l'aspect et du fonctionnement des divers boutons, rien de tel qu'un (bon) exemple. Alors allons-y avec "boutons.rbw" dont le listing suit :

 

***************************************************************************

 

require 'fox'

include Fox

class BoutonsWindow < FXMainWindow

 

  def initialize(app)

    # Appelle la classe de base

    super(app, "Bouton Test", nil, nil, DECOR_ALL, 100, 100, 0, 0)

 

    # Crée le gestionnaire de bulles

    FXTooltip.new(self.getApp())

 

    # Barre de statut

    statusbar = FXStatusbar.new(self,

      LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER)

      statusbar.statusline.normalText="Prêt"

 

    # Contrôles à droite

    controls = FXVerticalFrame.new(self,

      LAYOUT_SIDE_RIGHT|LAYOUT_FILL_Y|PACK_UNIFORM_WIDTH)

 

    # Séparateur vertical

    FXVerticalSeparator.new(self,

      LAYOUT_SIDE_RIGHT|LAYOUT_FILL_Y|SEPARATOR_GROOVE)

 

    # Contenant

    contents = FXHorizontalFrame.new(self,

  LAYOUT_SIDE_LEFT|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y|PACK_UNIFORM_WIDTH,

      0, 0, 0, 0, 20, 20, 20, 20)

 

    # Construit une image en la chargeant depuis le disque

    bigpenguin = loadIcon("bigpenguin.png")

 

    # Le bouton

    @button = FXButton.new(contents,

      "&Ceci est un label multi-ligne\nun bouton pour montrer\n" +

      "les possibilités de l'objet bouton.\t" +

      "C'est aussi une info-bulle\n(qui peut aussi être multi-ligne).\t" +

      "Ici un message d'aide pour la ligne de statut.",

      bigpenguin,nil, 0,

FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_X|LAYOUT_CENTER_Y|LAYOUT_FIX_WIDTH|

LAYOUT_FIX_HEIGHT,0, 0, 300, 200)

 

    checkButton = FXCheckButton.new(controls, "Style Toolbar \tCool style boutons \"poppy\"")

    checkButton.connect(SEL_COMMAND) { |sender, sel, checked|

      if checked

        @button.buttonStyle |= BUTTON_TOOLBAR

        @button.frameStyle = FRAME_RAISED

      else

        @button.buttonStyle &= ~BUTTON_TOOLBAR

        @button.frameStyle = FRAME_RAISED|FRAME_THICK

      end

    }

 

    group1 = FXGroupBox.new(controls, "Placement Horizontal",

      GROUPBOX_TITLE_CENTER|FRAME_RIDGE)

    FXRadioButton.new(group1, "Avant le texte").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition|ICON_BEFORE_TEXT) & ~ICON_AFTER_TEXT

    }

    FXRadioButton.new(group1, "Après le texte").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition|ICON_AFTER_TEXT) & ~ICON_BEFORE_TEXT

    }

    FXRadioButton.new(group1, "Centré").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition & ~ICON_AFTER_TEXT) & ~ICON_BEFORE_TEXT

    }

 

    group2 = FXGroupBox.new(controls, "Placement Vertical",

      GROUPBOX_TITLE_CENTER|FRAME_RIDGE)

    FXRadioButton.new(group2, "Au dessus du texte").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition|ICON_ABOVE_TEXT) & ~ICON_BELOW_TEXT

    }

    FXRadioButton.new(group2, "Sous le texte").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition|ICON_BELOW_TEXT) & ~ICON_ABOVE_TEXT

    }

    FXRadioButton.new(group2, "Centré").connect(SEL_COMMAND) {

      @button.iconPosition =

        (@button.iconPosition & ~ICON_ABOVE_TEXT) & ~ICON_BELOW_TEXT

    }

 

    group3 = FXGroupBox.new(controls, "Justification horizontale ",

      GROUPBOX_TITLE_CENTER|FRAME_RIDGE)

    FXRadioButton.new(group3, "Centré").connect(SEL_COMMAND) {

      @button.justify &= ~JUSTIFY_HZ_APART

    }

    FXRadioButton.new(group3, "Gauche").connect(SEL_COMMAND) {

      @button.justify = (@button.justify & ~JUSTIFY_HZ_APART) | JUSTIFY_LEFT

    }

    FXRadioButton.new(group3, "Droite").connect(SEL_COMMAND) {

      @button.justify = (@button.justify & ~JUSTIFY_HZ_APART) | JUSTIFY_RIGHT

    }

    FXRadioButton.new(group3, "A part").connect(SEL_COMMAND) {

      @button.justify |= JUSTIFY_HZ_APART

    }

 

    group4 = FXGroupBox.new(controls, "Justification verticale",

      GROUPBOX_TITLE_CENTER|FRAME_RIDGE)

    FXRadioButton.new(group4, "Centré").connect(SEL_COMMAND) {

      @button.justify &= ~JUSTIFY_VT_APART

    }

    FXRadioButton.new(group4, "Haut").connect(SEL_COMMAND) {

      @button.justify = (@button.justify & ~JUSTIFY_VT_APART) | JUSTIFY_TOP

    }

    FXRadioButton.new(group4, "Bas").connect(SEL_COMMAND) {

      @button.justify = (@button.justify & ~JUSTIFY_VT_APART) | JUSTIFY_BOTTOM

    }

    FXRadioButton.new(group4, "A part").connect(SEL_COMMAND, method(:onCmdJustVerApart))

 

    quitButton = FXButton.new(controls, "&Quitter", nil, nil, 0, FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X)

    quitButton.connect(SEL_COMMAND) {

      getApp().exit(0)

    }

  end

 

  def onCmdJustVerApart(sender, sel, ptr)

    @button.justify |= JUSTIFY_VT_APART

  end

 

  def loadIcon(filename)

    FXPNGIcon.new(getApp(), File.open(filename, "rb").read)

  end

 

  def create

    super

    show(PLACEMENT_SCREEN)

  end

end  # fin d'initialize

 

def run

  # Construit une application

  application = FXApp.new("Bouton", "FoxTest")

 

  # Construit la fenêtre principale

  BoutonsWindow.new(application)

 

  # Crée l'application

  application.create

 

  # Lance l'application

  application.run

end

 

run

 

***************************************************************************

 

 

  Décortiquons un peu tout cela. L'exemple, dont on voit le résultat ci-après, montre une fenêtre partagée verticalement en deux parties. Celle de gauche contient un gros bouton test qui va changer d'allure suivant l'état des boutons radios situés dans la partie droite. En un seul exemple nous allons donc voir toutes (ou presque) les possibilités des boutons sous FXRuby.

 

·        On crée tout d'abord une nouvelle classe qui descend de 'FXMainWindow'.  Toute fenêtre un tant soit peu compliquée est plus facilement exploitable en dérivant d'une classe ancêtre.

·        La méthode 'initialize' est une méthode qui est appelée automatiquement après  l'appel de 'new', avec les paramètres éventuels du dit 'new'. Elle permet donc d'y inclure tous les traitements que l'on souhaite, ici pratiquement toute la description de la fenêtre. Cette méthode doit être redéfinie dans les sous-classes.

·        A l'intérieur du corps d'une méthode, un appel à 'super' agit exactement comme un appel à la méthode portant le même nom de l'objet ancêtre. S'il n'y a pas de paramètres fournis, ce seront les valeurs par défaut des paramètres de l'ancêtre qui seront utilisés.

super [ ( [

                param

           ]*

                    [*array

                 ] ) ]  [

              block

                                                                                                      ]

 

·        FXToolTip.new  crée le gestionnaire des info-bulles. Remarquons le passage du paramètre : 'self' représente la classe elle-même et renvoie donc le pointeur de l'application par la méthode 'getApp'.

·        Sont ensuite créées les variables représentant les divers composants nécessaires à la construction de notre fenêtre exemple. A savoir dans l'ordre d'apparition dans le programme :

Statusbar              : la ligne de statut munie d'un coin de dragage (STATUSBAR_WITH_DRAGCORNER).  Après sa création, on modifie le texte par défaut qui s'affiche dans la barre de statut. En lieu et place du "Ready" anglais, nous mettons le "Prêt" français.

Controls               : le cadre à droite contenant les boutons de réglage.

Contents              :  le cadre à gauche contenant le gros bouton de test.

bigpenguin          :  l'icône du gros bouton. Ici il est fait appel à la méthode 'loadIcon()'. Pour une icône à charger, cela ne nécessite pas en fait de faire une méthode spécifique, mais comme nous le verrons plus loin, s'il y en a plusieurs, la création d'une telle méthode évite la répétition de code.

@button                  : le gros bouton de test(voir plus loin "Un peu de Ruby").

Checkbutton       :  la case à cocher indiquant si le gros bouton doit avoir une apparence normale ou bien l'apparence d'un bouton de barre d'outils c'est à dire sans cadre apparent, le cadre devenant visible lorsque le curseur de la souris passe dessus. A noter la condition 'if… else…  end' dans la méthode 'connect'. A remarquer aussi le raccourci d'affectation (voir aussi plus loin "Un peu de Ruby") :

@button.buttonStyle |= BUTTON_TOOLBAR

qui équivaut à       @button.buttonStyle = @button.buttonStyle |BUTTON_TOOLBAR

On voit pour la première fois l'utilisation de fonctions logiques et de comparaison de nombres bit à bit :

&    pour le "ET" logique .

 |     pour le "OU" logique.

                                ~    pour obtenir le complément d'un nombre.

 

Group1, group2, group3, group4  : les boîtes de groupe, FXGroupBox, englobant les différents boutons radio, FXRadioButton.

QuitButton          : le bouton habituel pour quitter l'application.

·        Remarquez dans le groupe 4, la puissance des raccourcis avec Ruby dans la création du dernier bouton :

   FXRadioButton.new(group4, "Apart").connect(SEL_COMMAND,

                  method(:onCmdJustVerApart))

      Dans la foulée on fait "Objet.new.connect". Le passage de paramètres se faisant par

      l'appel  d'une méthode déclarée plus loin dans la définition de la classe.

·        Quelques précisions sur la façon de faire passer les messages :

      La plupart des objets FOX envoient des messages qui ont quatre éléments importants :

1.      L'expéditeur du message est l'objet qui envoie le message. Dans ce cas l'instance de FXButton est l'expéditeur 'sender'.

2.      Le type de message est une constante entière prédéfinie qui indique quelle sorte d'événement a eu lieu (c'est à dire le but de l'envoi du message). Dans ce cas le type du message est 'SEL_COMMAND', qui indique que la commande associée à ce composant doit être invoquée.

3.      L'identifiant du message est une autre constante entière qui est utilisée pour différencier les messages de même type. Par exemple le message qui rend visible une fenêtre est un message 'SEL_COMMAND' avec l'identifiant  FXWindow::ID_SHOW (où ID_SHOW est une constante définie dans la classe FXWindow ). Un identifiant différent,  FXWindow::ID_HIDE, appelle une instance de FXWindow pour la rendre invisible.

4.      La donnée du message est un objet contenant une information spécifique. Si l'expéditeur n'est pas utile, ni les données, on peut alors utiliser le raccourci que nous avons vu dans l'exemple en début de chapitre.

 

Particularités de FXRuby :

FXRuby utilise une syntaxe inspiré de GTK (l'autre interface graphique de Ruby). On peut attacher, comme nous venons de le voir ci-dessus, un bloc à un composant en utilisant la méthode 'connect'. Par exemple :

  unBouton = FXButton.new(parent, "Cliquer ici")

  unBouton.connect(SEL_COMMAND) { |sender, sel, ptr|

   puts "Ouf !"

  }

Une autre forme de la méthode 'connect' utilise soit une méthode ('method') soit une instance de procédure ('proc') comme second argument (sans lier un bloc), par exemple :

 def pousse(sender, sel, ptr)

   puts "Ouf !"

 end

 unBouton = FXButton.new(parent, "Cliquer ici")

 unBouton.connect(SEL_COMMAND, method(:pousse))

 

Et enfin la forme raccourcie déjà vue :

 unBouton = FXButton.new(parent, "Cliquer ici")

 unBouton.connect(SEL_COMMAND) { puts "Ouf !" }

 

  Tout cela rappellera aux anciens programmeurs Pascal les déclarations de 'TurboVision' en beaucoup plus simples puisqu' il n'est nul besoin, même si cela est prévu, d'indiquer les positions du composant dans la fenêtre parent, FOX se chargeant de l'arrangement lui-même. L'astuce consiste à placer des cadres verticaux ou horizontaux dans les fenêtres, ceux-ci allant se placer automatiquement en fonction des paramètres.

-         1er cadre vertical à droite (LAYOUT_SIDE_RIGHT). Il occupe toute la place disponible en hauteur (LAYOUT_FILL_Y).

-         2ème cadre à gauche (LAYOUT_SIDE_LEFT). Il occupe toute la place restante en largeur comme en hauteur (LAYOUT_FILL_X | LAYOUT_FILL_Y)

 

Les boîtes à grouper vont se positionner automatiquement dans l'ordre de leur déclaration. Remarquons qu'elles n'ont pas de dimensions précisées. Le tout va décider de la largeur et de la hauteur du cadre parent. Il en est de même du gros bouton qu'on décide de centrer en largeur et en hauteur (LAYOUT_CENTER_X | LAYOUT_CENTER_Y) et de dimensions fixes de 300 X 200 pixels.Il impose donc une largeur au cadre de gauche qui se retrouve avec une hauteur imposée par le cadre de droite !

 

Et voici ce que l'on obtient à l'écran :

 

 


 

 


Fig 2 - boutons.rbw

 

Remarquez la bulle d'aide affichée en surimpression du bouton, et le texte d'aide dans la ligne de statut.

 

Nous avons donc découvert dans cet exemple les composants suivants :

 

Les cadres et séparateurs

 

  FXFrame.new(parent, opts=FRAME_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD,padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD) {|theFrame| ...}

Les cadres servent à remplir les fenêtres et à héberger à leur tour d'autres composants visuels.

Il y en a pour tous les goûts, et cela se décide dans le paramètre 'opts' qui se décline en huit possibilités du 'sans cadre' au 'cadre normal'. Voir la liste en annexe dans FXWindow.

 

FXHorizontalFrame et FXVerticalFrame sont des cadres horizontaux (resp. verticaux) qui placent les fenêtres enfants horizontalement (verticalement)  selon les renseignements apportés par les fenêtres enfants. Si ces composants ne possèdent pas de cible ('target') ni de sélecteur, ils ont en plus des paramètres de dimensionnement, deux paramètres indiquant la distance minimale souhaitée entre les composants enfants déclarés dans la méthode 'new' ci-dessous par 'hSpacing' et 'vSpacing'. La valeur par défaut est égale à '0'.

FXHorizontalFrame.new(parent, opts=0, x=0, y=0, width=0, height=0, padLeft=DEFAULT_SPACING, padRight=DEFAULT_SPACING,

 padTop=DEFAULT_SPACING, padBottom=DEFAULT_SPACING, hSpacing=DEFAULT_SPACING, vSpacing=DEFAULT_SPACING)

 

FXVerticalSeparator et FXHorizontalSeparator sont, comme leur nom le laisse deviner, de simples traits de séparation. Les paramètres de positionnement, de largeur et hauteur sont à zéro, par défaut. Par défaut aussi, le nombre de pixels minimum du tracé : 'pl', 'pr', 'pt',, 'pb' signifiants "left", "right", "top" et "bottom". Dans le séparateur vertical ce sont les pixels de haut en bas qui ont un minimum de 1 pixel.

FXVerticalSeparator.new (parent, opts=SEPARATOR_GROOVE|LAYOUT_FILL_Y, x=0, y=0, w=0, h=0, pl=0, pr=0, pt=1, pb=1)

 

 

Les boîtes à grouper

 

FXGroupBox.new(parent, text, opts=GROUPBOX_NORMAL, x=0, y=0, width=0,height=0,  padLeft=DEFAULT_SPACING, padRight=DEFAULT_SPACING, padTop=DEFAULT_SPACING, padBottom=DEFAULT_SPACING,

hSpacing=DEFAULT_SPACING, vSpacing=DEFAULT_SPACING)

 

FXGroupBox produit un cadre surélevé ou en puits autour d'un groupe de composants visuels. Généralement un titre est placé à gauche pour clarifier les choses. Des radios boutons placés à l'intérieur d'une boîte à grouper ont automatiquement le comportement attendu, c'est à dire qu'un seul d'entre eux est coché à la fois. L'option 'opts' permet de justifier le titre à gauche, à droite ou au milieu. Voir détails en annexe.

 

Les cases  à cocher

 

FXCheckButton.new(parent, text, target=nil, selector=0,

 opts=CHECKBUTTON_NORMAL, x=0, y=0,width=0, height=0,

 padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD,

 padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD)

 

Pas de surprises, les paramètres sont les mêmes à part celui des options qui devient CHECKBUTTON_NORMAL, ce qui paraît tout de même logique.

 

 

Les boutons-radio

 

On devine facilement que les boutons-radio sont identiques aux cases à cocher à ceci près qu'ils sont reliés ensemble afin qu'un seul d'entre eux soit coché à un instant donné. Il suffit pour cela de les regrouper dans une boîte de groupe FXGroupBox.

 

FXRadioButton.new(parent, text, target=nil, selector=0, opts=RADIOBUTTON_NORMAL,

 x=0, y=0, width=0, height=0,

 padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD,

 padBottom=DEFAULT_PAD)

 

Les boutons-bascule

Les boutons-bascule sont des boutons à deux états, qui permettent de mettre un bouton au lieu de deux, lorsqu'ils ne peuvent être ensemble sans que l'un d'eux soit désactivé. Par exemple si l'on veut ouvrir un fichier, il n'est pas nécessaire d'avoir le bouton de fermeture.

 

FXToggleButton.new(p, text1, text2, icon1=nil, icon2=nil, tgt=nil, sel=0, opts=TOGGLEBUTTON_NORMAL, x=0, y=0, w=0, h=0,

 pl=DEFAULT_PAD, pr=DEFAULT_PAD, pt=DEFAULT_PAD, pb=DEFAULT_PAD)

 

A part le fait que les textes et les icônes sont en double, le reste des paramètres est le même que dans un bouton normal. Voir les détails en annexe.

 

Les boutons avec flèches incorporées

FXArrowButton.new(parent, target=nil, selector=0, opts=ARROW_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD)

 

Ils ont l'avantage d'être prévus. Hormis cela ils n'ont rien de bien particulier et fonctionnent comme des boutons normaux munis d'une icône.

 

La figure 3 montre quelques exemples de boutons, avec de gauche à droite un bouton normal, un bouton flèche, un bouton style barre à outils (sans le curseur souris dessus), un bouton normal désactivé, un bouton normal sans cadre et enfin un bouton-bascule.

 

 


Fig. 3 stylbout.png

 

 


Les bulles d'aide

FXToolTip.new(app, opts=TOOLTIP_NORMAL, x=0, y=0, width=0, height=0)

 

 Rien de bien spécial si ce n'est l'option éventuelle pour rendre l'info-bulle visible, tant que le bouton est survolé par le curseur souris ou bien dépendant de la longueur du texte, pour en permettre plus facilement la lecture. Voir détails en annexe.

 

La barre de statut

Située en bas de la fenêtre, elle a pour fonction principale d'afficher le texte d'aide supplémentaire des info-bulles..

Sa déclaration est des plus simples :

FXStatusBar.new(p, opts=0, x=0, y=0, w=0, h=0, pl=3, pr=3, pt=2, pb=2, hs=4, vs=0)

Remarquons toutefois l'initialisation du passe-partout à une valeur non-nulle (2 et 3 pixels), ainsi que l'espace entre composants enfants (4 pixels).

Par ses attributs, la barre de statut nous donne accès à :

- FXDragCorner qui est le "coin de dragage", visible ou non, et dont on peut modifier la couleur.

  - FXStatusLine qui est la ligne de statut proprement dite, c'est à dire celle qui contient le texte à afficher. Dans l'exemple ci dessus, nous modifions le texte de repos pour qu'il soit en français et nous pouvons aussi changer, de la même façon, la couleur et la fonte. Voir les attributs en Annexe A.

 

 

Un peu de Ruby (2) :

 

Les variables.

Les types de variables avec Ruby sont au nombre de 5 :

-         variables globales : elles débutent par le signe '$'.

-         variables  locales  : elles débutent par une minuscule.

-         variables  d'instance  : elles débutent par un '@'.

-         variables  de classes  : elles débutent par un double arobase '@@'.

-         constantes  : elles débutent par une majuscule.

 

Cette façon de faire permet donc de reconnaître instantanément à quel type de variable on a à faire.

La portée des variables.

On entend par là de quel endroit on peut les appeler pour les lire ou bien y écrire.

-         Les variables globales sont, comme leur nom l'indique, accessibles de n'importe quel endroit d'un programme. Elles valent 'nil' si non initialisées.

-         Les variables locales ont comme vision l'endroit où elles ont été déclarées. Ce qui peut être dans une 'proc{...}', dans une méthode 'def...end,' une classe 'class...end'.

-         Les variables d'instance ont une portée limitée aux objets qui se réfèrent à 'self'. Elles ont la valeur 'nil' jusqu'à leur initialisation. Elles ne sont accessibles de l'extérieur que par des accesseurs. Chaque instance d'un objet possède les mêmes variables d'instance et elles peuvent avoir des valeurs différentes.

-         Les variables de classe sont uniques à l'intérieur d'une classe. Elles peuvent être modifiées, mais sont communes à toutes les instances de la classe.

-         Les constantes doivent bien sûr être initialisées et sont accessibles en dehors de la classe.

 

Les opérateurs

Dans les paramètres on remarque le signe '|'. Il s'agit du 'ou' de comparaison bit à bit.

Voici la liste des autres opérateurs de Ruby classés par ordre de précédence :

 

 

Méthode

Opérateur

Description

OUI

[ ] [ ]=

Référence d'éléments, ensemble d'éléments

OUI

**

Exponentiation

OUI

! ~ +  --

Négation, complément, plus et moins unaire (les noms de méthodes pour les deux derniers sont  +@ and -@)

OUI

*  /  %

Multiplication, division, et modulo

OUI

+  --

Plus et moins

OUI

>> <<

Décalage de bits droit et gauche

OUI

&

'Et' bit à bit

OUI

^  |

`ou' exclusif  et  `ou' régulier  bit à bit

OUI

<= < > >=

Operateurs de comparaison, inf. ou égal, inf, sup., sup. ou égal.

OUI

<=>

 

  ==

  ===

 !=

  =~

 

 !~

Retourne '–1' si l'argument à gauche est <= à celui de droite, '0' si égalité, '1'  si >=.

Teste l'égalité.

=== teste l'égalité  sans  'when'ou  'case'.

 

Expression rationnelle. Retourne la position de la première occurrence dans une chaîne.

 (!= et !~ ne peuvent être définis comme méthodes)

 

&&

'ET' logique

 

||

'OU' logique

 

..    ...

Intervalle (inclusif et exclusif)

 

? :

 'if-then-else' ternaire

 

= 0%= 0{ /= 0--= 0+= 0|= &= 0>>= 0<<= 0*= 0&&= 0||= 0**=

Assignement

 

Defined?

Teste si  le  symbole est defini

 

Not

Négation logique

 

or  and

Composition logique (ou, et)

 

if  unless  while until

Expressions conditionnelles et boucles (si, si non, tant que, jusqu'à )

 

Begin/end

Bloc (partie de code)

 

La colonne "Méthode" indique si l'opérateur est une méthode. Rappelons que tout (ou presque) dans Ruby est objet et possède donc des méthodes.

Notons enfin le raccourci syntaxique d'affectation qui est fait du signe '=' précédé de l'opérateur idoine :

 'variable += constante ' équivaut à 'variable = variable + constante'.

Les opérateurs concernés sont  :  + , - , * , / , **,  %, <<,>>, &, |, ^, &&, ||.

 

Les méthodes.

Les autres langages ont des fonctions, des procédures, des méthodes ou des routines mais en Ruby il n'y a que des méthodes qui retournent une valeur.

Une méthode est définie en utilisant les mots clé 'def' et 'end'. Les noms de méthodes doivent débuter par une lettre minuscule. On peut bien entendu déclarer des paramètres dans une méthode et même y affecter des valeurs par défaut :

 Ex:

  def  mamethode        # sans paramètre.

       # traitement

  end

 

  def mamethode (arg1, arg2, arg3)     #  3 paramètres

      # traitement

  end

 

 def mamethode (arg1="Jean", arg2="Pierre", arg3="Paul")     #  3 paramètres initialisés

      # traitement

  end

 

La valeur de retour d'une méthode est la valeur de la dernière expression calculée.

Cependant il existe une expression 'return' qui fait quitter la méthode avec la valeur qui lui est passée en paramètre, par exemple 'return 1' retourne la valeur '1', ou 'nil' s'il n'y a pas de paramètre, ou bien encore un tableau s'il y a plusieurs paramètres.

Les noms des méthodes qui fonctionnent comme des questions sont souvent terminés par un '?'. ( ex. : '.toto?'). Les noms de méthodes "dangereuses" ou qui modifient le destinataire peuvent être terminés par un '!' (ex. : '.titi!').

Par exemple, si a = ['chat','chien'] et  b = ['chat','chien']

a.eql?(b)      è  true   (teste la valeur)

a.equal?(b) è  false (teste l'identifiant 'ID')

 

Par exemple dans la classe 'string', la méthode '.chop' enlève le dernier caractère d'une chaîne et retourne une nouvelle chaîne. Tandis que '.chop!' modifie directement la chaîne passée en paramètre et retourne 'nil' si la chaîne est vide. Par exemple :

   "string\n".chop            è "string"

   "string".chop                 è "strin"

   "x".chop.chop                 è ""

   "".chop             è ""

 

tandis que  "".chop!   è nil

 

Peut-être plus facile à comprendre : la méthode 'upcase' transforme les minuscules en majuscules

  ch1="truc"

  ch2=ch1.upcase

donne ch1="truc" et ch2="TRUC"

 

mais ch2=ch1.upcase! 

donne ch1="TRUC" et ch2="TRUC".

 

 


Chapitre 4

 

Les canevas.

 

  On ne devait pas savoir comment appeler une partie de l'écran sur laquelle on peut soit dessiner, soit afficher une image. Le mot feuille étant déjà pris par les tableurs, pour une fois on n'en a pas inventé un nouveau et le mot connu surtout dans le monde de la tapisserie est alors apparu.

Contrairement au composant bouton, le canevas subit plutôt qu'il opère.

 

FXCanvas.new(parent, target=nil, selector=0, opts=FRAME_NORMAL, x=0, y=0, width=0, height=0)

 

Ce composant est l'occasion d'apprendre à dessiner sur un canevas et de voir comment fonctionnent les messages sous Fox. Rien de tel qu'un exemple simple et son décortiquage pour expliquer les divers mécanismes.

Voici donc 'Gribouille', un tableau blanc à l'écran pour dessiner à l'aide de la souris.

 

 

***************************************************************************

require 'fox'

require 'fox/colors'

 

include Fox

 

class GribouilleWindow < FXMainWindow

 

  def initialize(app)

    # Initialise en premier la classe de base.

    super(app, "Gribouillage", nil, nil, DECOR_ALL,

      0, 0, 320, 200)

    # Construction d'un cadre horizontal pour contenir la fenêtre principale.

    @contents = FXHorizontalFrame.new(self,

      LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0, 0, 0, 0, 0, 0, 0, 0)

 

    # Le panneau de gauche contient le canevas.

    @canvasFrame = FXVerticalFrame.new(@contents,

      FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 10, 10)

 

    # Place une étiquette au-dessus du canevas.

    FXLabel.new(@canvasFrame, "Cadre de canevas", nil,

      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

 

    # Ligne horizontale de séparation.

    FXHorizontalSeparator.new(@canvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X)

 

    # Trace le canevas.

    @canvas = FXCanvas.new(@canvasFrame, nil, 0, (FRAME_SUNKEN|FRAME_THICK|

      LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT))

    @canvas.connect(SEL_PAINT) do |sender, sel, event|

      FXDCWindow.new(@canvas, event) do |dc|

        dc.foreground = @canvas.backColor

        dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)

      end

    end

    @canvas.connect(SEL_LEFTBUTTONPRESS) do

      @canvas.grab

      @mouseDown = true

    end

    @canvas.connect(SEL_MOTION) do |sender, sel, event|

      if @mouseDown

        # Récupère un contexte de périphérique pour le canevas.

        dc = FXDCWindow.new(@canvas)

 

        # Indique la couleur de fond.

        dc.foreground = @couleurTrace

 

        # Trace une ligne du point précédent au point actuel de la souris.

        if @mirrorMode.value

          cW = @canvas.width

          cH = @canvas.height

          dc.drawLine(cW-event.last_x, event.last_y,

                      cW-event.win_x, event.win_y)

          dc.drawLine(event.last_x, cH-event.last_y,

                      event.win_x, cH-event.win_y)

          dc.drawLine(cW-event.last_x, cH-event.last_y,

                      cW-event.win_x, cH-event.win_y)

        end

        dc.drawLine(event.last_x, event.last_y, event.win_x, event.win_y)

 

        # On a tracé quelque chose, le canevas est donc sale.

        @dirty = true

 

        # Libère le contexte.

        dc.end

      end

    end

    @canvas.connect(SEL_LEFTBUTTONRELEASE) do |sender, sel, event|

      @canvas.ungrab

      if @mouseDown

        # Récupère un contexte de périphérique pour le canevas.

        dc = FXDCWindow.new(@canvas)

 

        # Indique la couleur de fond.

        dc.foreground = @couleurTrace

 

        # Trace une ligne du point précédent au point actuel de la souris.

        dc.drawLine(event.last_x, event.last_y, event.win_x, event.win_y)

 

        # On a tracé quelque chose, le canevas est donc sale.

        @sale = true

 

        # Le bouton souris est relâché.

        @mouseDown = false

 

        # Libère le contexte.

        dc.end

      end

    end

 

    # Panneau de droite pour les boutons.

    @buttonFrame = FXVerticalFrame.new(@contents,

      FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 10, 10)

 

    # Etiquette au-dessus des boutons.

    FXLabel.new(@buttonFrame, "Cadre boutons", nil,

      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

 

    # Ligne horizontale de séparation.

    FXHorizontalSeparator.new(@buttonFrame,

      SEPARATOR_RIDGE|LAYOUT_FILL_X)

 

    # Active ou désactive le mode miroir.

    @mirrorMode = FXDataTarget.new(false)

    FXCheckButton.new(@buttonFrame, "Miroir", @mirrorMode, FXDataTarget::ID_VALUE, CHECKBUTTON_NORMAL|LAYOUT_FILL_X)

 

    # Bouton pour nettoyer le canevas.

    clearButton = FXButton.new(@buttonFrame, "&Effacer", nil, nil, 0,

      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 5, 5)

    clearButton.connect(SEL_COMMAND) do

      FXDCWindow.new(@canvas) do |dc|

        dc.foreground = @canvas.backColor

        dc.fillRectangle(0, 0, @canvas.width, @canvas.height)

        @sale = false

      end

    end

    clearButton.connect(SEL_UPDATE) do |sender, sel, ptr|

      # Cette procédure contient le message de mise à jour  envoyé par le bouton d'effacement

      # à sa cible. Chaque composant visuel de Fox reçoit un message (SEL-UPDATE) durant la phase de temps mort,

      # lui demandant de se mettre à jour lui-même. Par exemple un bouton peut

      # être actif ou inactif suivant l'état de l'application.

      # Dans ce cas nous désactivons l'expéditeur (le bouton effacer) quand le canevas a été

      # nettoyé, et le réactivons quand il a été utilisé (il est sale).

     

      message = @sale ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE

      sender.handle(self, MKUINT(message, SEL_COMMAND), nil)

    end

 

    # Bouton pour quitter.

    FXButton.new(@buttonFrame, "&Quitter", nil, app, FXApp::ID_QUIT,

      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 5, 5)

 

    # Initialisation des autres variables.

    @couleurTrace = FXColor::Red

    @mouseDown = false

    @sale = false

  end

 

  # Crée et montre la fenêtre principale.

  def create

    super                  # Cré la fenêtre.

    show(PLACEMENT_SCREEN) # Montre la fenêtre.

  end

end

 

if __FILE__ == $0

  # Construit l'objet 'application'.

  application = FXApp.new('Gribouillage', 'FoxTest')

 

  # Construit la fenêtre principale.

  scribble = GribouilleWindow.new(application)

 

  # Crée l'application.

  application.create

 

  # Exécute l'application.

  application.run

end

 

 

***************************************************************************

 

  La partie 'initialize' ressemble en grande partie à celle du programme "boutons.rbw". Le fait nouveau se situe dans la transmission des commandes de la méthode 'connect' du canevas.

  En effet, lors du clic gauche de la souris sur le canevas, la variable @mousedown est positionnée à 'true', et le canevas en position de saisie(méthode 'grab').

  Si la souris glisse, la commande change, et une ligne est traçée.

  Enfin, lors du relâchement de la souris, on trace le dernier trait, et on remet tout dans l'ordre.

  Remarquons le mode miroir qui, s'il est en action, recopie simultanément le dessin en cours de tracé symétriquement par rapport à l'axe des 'x', l'axe des 'y' et le centre du canevas. Donc, à partir d'un tracé on en obtient quatre.

Notons l'initialisation de la variable 'message' dans la méthode du bouton d'effacement :

Il s'agit ici de la version "rapide", appelée opérateur ternaire, de "if.. then .. else":

 

                                           Expression-boooléenne ? expr1 : expr2

Retourne expr1 si expression-booléenne est vrai,  sinon expr2.

 

Dans la création de l'application par la méthode 'new', nous retrouvons ces mystérieux paramètres passés sous forme de chaînes. Il a été dit précédemment qu'ils avaient une relation avec la base de registres de Window. En effet, FXRuby construit automatiquement une base de données d'initialisation de tous ses programmes. Pour retrouver ces données il faut appeler tout d'abord la base de registres en faisant 'Démarrer', puis 'Exécuter'  et entrer 'Regedit' dans la boîte de dialogue. Une fois dans la base de registres choisir 'HKEY_CURRENT_USER' et faire descendre l'arborescence. Cliquer sur 'Software' et vous verrez apparaître le répertoire 'FoxTest' cité dans les exemples précédents. Chaque essai comportant la clef  'FoxTest' aura un sous-répertoire dans le répertoire 'FoxTest', portant le nom que vous aurez donné à votre application. Si vous faites des essais, par exemple d'aspect, avec les programmes exemples, pensez à détruire les fichiers relatifs à l'initialisation sinon vous risquez d'avoir des surprises et penser que votre programmation est inadéquate…

 

Bon ce n'est pas encore PaintShop Pro, d'accord, mais en si peu de lignes ce n'est tout de même pas mal, non ?

 

Et voilà le résultat :

 

Fig 3 . Gribouille.rbw

 

 

Un peu de Ruby.

 

Les blocs.

Après la méthode 'connect' vous avez sans doute remarqué un 'do' mystérieux suivi d'un bloc de code. Les blocs de code peuvent être associés à des appels de méthodes, et implémenter des itérateurs.

Les blocs de code sont  simplement du code compris entre accolades '{', '}' ou bien entre un 'do' …'end', comme vous avez pu le voir dans les exemples précédents, où l'une ou l'autre version a été employée.

 

{puts "Coucou"}                  # ceci est un bloc

 

do                               #             

    objet.faitceci               # et cela aussi 

    objet.faitcela               #

end                              #

 

Une fois qu'un bloc est créé on peut l'associer à l'appel d'une méthode. Cette méthode peut faire appel au bloc passé en paramètre, une ou plusieurs fois en utilisant le mot clé 'yield'.

Par exemple :

 

 Def appelBloc

     yield

     yield

 end

 

appelBloc {puts "Coucou"}  # 'puts ' écrit ce qui le suit entre guillemets sur le périphérique  

                                                    #  de sortie.

 

produit :  Coucou

               Coucou

 

C'est comme si on passait le bloc { puts "Coucou"} en paramètre à la méthode 'appelBloc'.

Le bloc peut débuter par une liste d'arguments entre des barres verticales '|' :

 

    def appelBloc(p1)

      if block_given?

        yield(p1)

      else

        p1

      end

    end

   appelBloc("no Block")                          => "no Block"

   appelBloc("no Block") { |s| s.sub(/no /, '') } => "Block"

 

Dans le second exemple le bloc est appelé par 'yield' et signifie que pour chaque chaîne rencontrée, la première occurence de 'no ' sera remplacée par '', c'est à dire rien. Dans notre exemple il n'y a bien sûr qu'une chaîne, celle passée en paramètre, mais dans le cas de la lecture d'un fichier texte c'est chaque ligne qui serait soumise à l'ablation de 'no '.

 

 

Les itérateurs.

Un itérateur est fait pour répéter (itérer) des parties de code un certain nombre de fois. Contrairement à d'autres langages, Ruby ne possède pas de boucle 'for i=x to y do fairececi end'. Mais pas de panique, il y a les itérateurs.

Les blocs de code sont utilisés en Ruby pour implémenter les itérateurs c'est à dire des méthodes qui retournent des éléments de collections (par exemple des tableaux dont on verra la description au chapitre 6).

   a = %w( chat chien oie boa)               # crée un tableau

   a.each { |animal| puts animal }           # itère le contenu

donne :

  chat

  chien

  oie

  boa

 

La variable d'itération, ici 'animal', est mise entre deux barres verticales.

Et, chose extraordinaire, les itérateurs sont inclus dans nombre d'objets de Ruby et n'apparaissent donc que comme de simples méthodes :

      5.times  { print  "*" }

      3.upto (6) { |i| print i }

      ('a'..'e').each { |char| print char }

 

produit :  *****3456abcde

 

'.each' signifie donc : pour chaque membre de ce qui me précède, je vais faire ceci.

Ruby fournit de très nombreux itérateurs plus interessants les uns que les autres. Notons au passage ceux pour les chaînes de caractères : 'each, each_byte, each_line…'

 

Les  boucles

Bien, il n'y a pas de boucle 'for  do  end' mais il y a les itérateurs, somme toute beaucoup plus pratiques. Restent les boucles conditionnelles au nombre de deux :

 

              expression while expression-booléenne

 

Si  expression est autre chose qu'un bloc begin/end, exécute expression zéro ou plusieurs fois  tant que expression-booléenne est 'true' (vrai).

 

              expression until expression-booléenne

 

Si  expression est autre chose qu'un bloc begin/end, exécute expression zéro ou plusieurs fois  tant que expression-booléenne est 'false' (faux).

Si expression est un bloc de code, le bloc sera toujours exécuté au moins une fois.

 

Pour sortir d'une boucle (infernale ça va de soi !) il y a quatre moyens :

-         'break'  qui fait quitter la boucle immédiatement.

-         'next' qui fait sauter au début de l'itération suivante.

-         'redo' qui répète l'itération courante.

-         'retry' qui fait tout reprendre au début.

 

  Pas de 'for…' c'est vite dit; il y en a un et fort intéressant. Il s'agit du 'for' qui parcourt tous les membres d'une collection ou d'un tableau, par exemple.

for  i in 0..5

                                                                        fairececi

                                                                     end

 

ou bien

for i in ['un','deux','trois']

                                                             fairecela

                                                          end

 

Les expressions conditionnelles

Elles sont au nombre de deux 'if' et 'unless' :

 

                if expression-booléenne [then]

                      faire ceci

                elsif expression-booléenne [then]

                      faire ceci

                else

                      faire ceci

                                       end

 

                unless expression-booléenne [then]

                      faire ceci

                else

                      faire ceci

                                       end

  Les expressions entre crochets, comme ici [then] sont facultatives.

  Le mot clé 'then' sépare le corps du code de la condition. Il n'est pas requis si le corps démarre sur une nouvelle ligne. La valeur d'une expression 'if' ou 'unless' est la valeur de la dernière expression évaluée où le code a été exécuté.

A noter que 'if' et 'unless' peuvent être employés dans une syntaxe de modificateurs:

              expression if expression-booléenne

                                  expression unless expression-booléenne

Evalue expression seulement si expression-booléenne est 'true' ('false' pour 'unless')

 

L'expression 'case'

'case' sert à tester une suite de conditions :

i = 5

case i

  when 1..5

      print "Entre 1 et 5"

  when 6..10

     print "Entre 6 et 10"

  else

     print "> 10"

end

 

Le résultat sera : "Entre 1 et 5".

L'expression 'case' cherche une égalité en commençant la recherche à la première comparaison en utilisant de façon interne l'opérateur : '==='. Si l'égalité est trouvée la recherche est arrêtée et le bloc de code qui suit est exécuté. 'case' retourne alors la valeur de la dernière expression exécutée.Si aucune égalitén'est trouvée et qu' il existe une clause 'else', c'est elle qui est exécutée, s' il n'y en a pas c'est la valeur 'nil' qui est retournée.

 

 

Juste avant la ligne de lancement de l'application de l'exemple précédent, nous trouvons une bizarrerie '__FILE__', qui est un mot réservé. Attention les traits soulignés encadrant le mot 'FILE' sont doublés.

 

 

Mots réservés de Ruby

 

 

__FILE__

and

def

end

in

or

self

unless

 

__LINE__

begin

defined?

ensure

module

redo

super

until

 

BEGIN

break

do

false

next

rescue

then

when

 

END

case

else

for

nil

retry

true

while

 

alias

class

elsif

if

not

return

undef

yield

 

 

Ces mots, puisqu'on les appelle réservés, ne sont pas utilisables comme noms de variables locales, ni de constantes.

Dans cette liste sont comprises les pseudo-variables :

'self'                     l'objet récepteur de la méthode décrite.

'true'                    valeur "vraie"

'false'                   valeur "fause"

'nil'                      valeur indéfinie

'__FILE__ '         nom du fichier source courant 

'__LINE__ '        numéro de la ligne courante dans le fichier source.

       

Quand à '$0' il s'agit d'une variable prédéfinie représentant le "nom du programme en cours d'exécution". Il s'agit donc d'une déclaration de sécurité.

 

Particularités de FXRuby :

La librairie " fox/iterators.rb " ajoute une méthode 'each' pour les classes suivantes :

FXComboBox, FXGLGroup, FXHeader, FXIconList, FXList, FXListBox, FXTable, FXTreeItem, FXTreeList et FXTreeListBox. Le module 'Enumerable' est aussi présent dans chacune de ces classes.

Le bloc de paramètres passé à votre bloc de code dépend de la classe. Par exemple une instance de FXList s'itère en donnant les paramètres de FXListItem :

 aList.each { |aListItem|

     puts "texte pour cet item = #{aListItem.getText()}"

 }

alors qu'une instance de FXComboBox nécessite deux paramètres, le texte de l'item (une chaîne 'string') et les données de l'item :

 

 aComboBox.each { |itemText, itemData|

     puts "texte pour cet item = #{itemText}"

 }

 

Le tableau suivant indique les paramètres pour les itérateurs de ces classes:

 

FXComboBox          :  le texte de l'item (une chaîne 'string') et les données.

FXGLGroup             : une instance  de FXGLObject.

FXHeader                 : une instance de FXHeaderItem.

FXIconList               : une instance de FXIconItem.

FXList                      : une instance de FXListItem .

FXListBox               : le texte de l'item ('string'), une icône (une instance de FXIcon) et les données.

FXTreeItem             : une instance de FXTreeItem.

FXTreeList              : une instance de FXTreeItem.

FXTreeListBox       : une instance de FXTreeItem.

 


Chapitre 5

 

Les menus et les dialogues:

 

Nous arrivons maintenant à la partie vraiment relationnelle d'un programme, c'est à dire la communication avec l'utilisateur. Les menus sont des composants auxquels nous sommes habitués depuis que les programmes évolués existent, et il en est de même des boîtes de dialogues. Pour ne rien changer à nos habitudes voici un nouvel exemple regroupant ces deux concepts.

 

***************************************************************************

 

require 'fox'

 

include Fox

 

# Une petite boîte de dialogue pour nos tests.

class FXTestDialog < FXDialogBox

  def initialize(owner)

    # Invoque en premier la fonction 'initialize' de la classe de base.

    super(owner, "Test de boîte de dialogue", DECOR_TITLE|DECOR_BORDER)

 

    # Boutons du bas.

    buttons = FXHorizontalFrame.new(self,

      LAYOUT_SIDE_BOTTOM|FRAME_NONE|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,

      0, 0, 0, 0, 40, 40, 20, 20)

 

    # Séparateur.

    FXHorizontalSeparator.new(self,

      LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|SEPARATOR_GROOVE)

 

    # Contenu.

    contents = FXHorizontalFrame.new(self,

   LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y|PACK_UNIFORM_WIDTH)

   

    # Sous-menu, sans commande.

    submenu = FXMenuPane.new(self)

    FXMenuCommand.new(submenu, "Un")

    FXMenuCommand.new(submenu, "Deux")

    FXMenuCommand.new(submenu, "Trois")

   

    # Menu.

    menu = FXMenuPane.new(self)

    FXMenuCommand.new(menu, "&Accepter", nil, self, ID_ACCEPT)

    FXMenuCommand.new(menu, "A&nnuler", nil, self, ID_CANCEL)

    FXMenuCascade.new(menu, "Sous-menu", nil, submenu)

    FXMenuCommand.new(menu, "&Quitter\tCtl-Q", nil, getApp(), FXApp::ID_QUIT)

 

    # Menu instantané (PopUp).

    pane = FXPopup.new(self)

    FXOption.new(pane, "Un", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Deux", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Trois", nil, nil, 0,JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Quatre", nil, nil,0,JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Cinq", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Six", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Sept", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Huit", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Neuf", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

    FXOption.new(pane, "Dix", nil, nil, 0, JUSTIFY_HZ_APART|ICON_AFTER_TEXT)

 

    # Menu option.

    FXOptionMenu.new(contents, pane, (FRAME_RAISED|FRAME_THICK|

      JUSTIFY_HZ_APART|ICON_AFTER_TEXT|LAYOUT_CENTER_X|LAYOUT_CENTER_Y))

 

    # Bouton pour le menu instantané.

    FXMenuButton.new(contents, "&Menu", nil, menu, (MENUBUTTON_DOWN|

      JUSTIFY_LEFT|LAYOUT_TOP|FRAME_RAISED|FRAME_THICK|ICON_AFTER_TEXT|

      LAYOUT_CENTER_X|LAYOUT_CENTER_Y))

 

    # Accepter.

    FXButton.new(buttons, "&Accepter", nil, self, ID_ACCEPT,

      FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)

 

    # Annuler.

    FXButton.new(buttons, "A&nnuler", nil, self, ID_CANCEL,

      FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)

  end

end

 

# Sous-classe de la fenêtre principale.

class DialogTester < FXMainWindow

 

  def initialize(app)

    # Invoque en premier la fonction 'initialize' de la classe de base.

    super(app, "Test Dialogues", nil, nil, DECOR_ALL, 0, 0, 400, 200)

 

    # Info-bulles.

    FXTooltip.new(getApp())

 

    # Barre de menu.

    menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)

 

    # Séparateur.

    FXHorizontalSeparator.new(self,

      LAYOUT_SIDE_TOP|LAYOUT_FILL_X|SEPARATOR_GROOVE)

 

    # Menu fichier.

    filemenu = FXMenuPane.new(self)

    FXMenuCommand.new(filemenu, "&Quitter", nil, getApp(), FXApp::ID_QUIT, 0)

    FXMenuTitle.new(menubar, "&Fichier", nil, filemenu)

  

    # Contenu.

    contents = FXHorizontalFrame.new(self,

      LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y|PACK_UNIFORM_WIDTH)

 

    # Bouton pour afficher un dialogue normal.

    nonModalButton = FXButton.new(contents,

      "Dialogue &Non-Modal ...\tAffiche une boîte de dialogue normale.", nil,  nil, 0,FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)

    nonModalButton.connect(SEL_COMMAND, method(:onCmdShowDialog))

 

    # Bouton pour afficher un dialogue modal.

    modalButton = FXButton.new(contents,

      "Dialogue &Modal ...\tAffiche une boîte de dialogue modale", nil, nil, 0,FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)

    modalButton.connect(SEL_COMMAND, method(:onCmdShowDialogModal))

 

    # Construit une instance de boîte de dialogue.

    @dialog = FXTestDialog.new(self)

 

  end

 

  # Montre le dialogue non-modal.

  def onCmdShowDialog(sender, sel, ptr)

    @dialog.show

  end

 

  # Montre le dialogue modal.

  def onCmdShowDialogModal(sender, sel, ptr)

    FXTestDialog.new(self).execute

    return 1

  end

 

  # Démarrage.

  def create

    super

    show(PLACEMENT_SCREEN)

  end

end

 

def run

  # Construit une application.

  application = FXApp.new("Dialog", "FoxTest")

 

  # Construit la fenêtre principale de l'application.

  DialogTester.new(application)

 

  # Crée l'application.

  application.create

 

  # Exécute l'application.

  application.run

end

 

run

 

***************************************************************************

 

A l'exécution  une première fenêtre vous propose le choix entre un dialogue modal et un dialogue non-modal. Qu'ès acò ? .

Réponse : un dialogue modal se trouve dans une fenêtre qui ne se fermera, et même mieux ne laissera la place à aucune autre, que s'il y a abandon de la part de l'utilisateur ou bien réponse à une question posée. La fenêtre en question reste toujours sur le devant de l'écran et attend obstinément une réponse.

Le dialogue non-modal permet d'aller faire un petit tour dans les fenêtres ouvertes alentour sans se formaliser pour autant de votre abandon.

Dans la partie 'initialize' une instance de la fenêtre de dialogue est créée : '@dialog'. C'est cette instance qui va être affichée lors d'un appel à la méthode 'onCmdShowDialog' qui appellera la méthode '.show' de l'instance. Tandis que dans la méthode ' onCmdShowDialogModal' c'est à la méthode '.execute' de l'instance de 'FXDialogBox' qu'il est fait appel.

Hormis cela l'aspect est le même, ce qui explique le code unique des sous-fenêtres dans l'exemple ci-dessus.

En regardant les images suivantes on observe, en partant du haut de celles-ci, la mention 'Fichier'. Avant d'en arriver à sa description il nous faut parler de ses propriétaires.  Le premier est 'menubar, une instance de FXMenuBar dont la déclaration complète est :

   FXMenuBar.new(parent, opts=LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FILL_X,

   x=0, y=0, width=0, height=0, padLeft=3, padRight=3, padTop=2, padBottom=2,   hSpacing=DEFAULT_SPACING, vSpacing=DEFAULT_SPACING)

Le propriétaire 'parent' est ici l'application elle-même ('app'), suivi dans les paramètres des options habituelles et d'aspect.

La barre de menu possède quant à elle un "titre de menu" tel que :

FXMenuTitle.new(parent, text, icon=nil, popupMenu=nil, opts=0)

qui commande par 'popupMenu' une sous-fenêtre de type FXMenuPane :

FXMenuPane.new(owner, opts=0)

qui est propriétaire, pour finir, des commandes qui vont déclencher des actions :

FXMenuCommand.new(parent, text, icon=nil, target=nil, selector=0, opts=0)

et des éventuels séparateurs d'enjolivure :

FXMenuSeparator.new(parent, opts=0)

 

Schématiquement nous avons donc :

 

 

Barre de menu => Titre => Sous-menus => Commandes

                                                                 => Séparateurs

 


 

 

 

 

 


  La figure ci-dessus montre le menu instantané 'pane', cachant le menu propriétaire 'FXOptionMenu'. Ce dernier  gardera la valeur qui sera cliquée dans le menu instantané, son deuxième paramètre étant en effet 'pane' . C'est un exemple qui montre bien la facilité avec laquelle s'opère le passage de données entre composants dans Fox. FXOptionMenu réagit donc comme une boîte à lister, tout en prenant moins de place, en affichant une valeur par défaut qui peut être modifiée par le choix d'une autre valeur choisie dans un menu instantané.

 


 

 

 

 


La figure ci-dessus montre le menu bouton ('FXMenuButton') qui fait apparaître les sous-fenêtres ('FXMenuPane) comprenant les commandes des menus ('FXMenuCommand')  'Accepter' à 'Quitter' et le sous-menu "cascade" de 'Un' à 'Trois'.

 

Faites attention au composant FXMenuCascade qui, bien que déclaré en même temps que les commandes ne passe pas les mêmes paramètres :

       

    FXMenuCommand.new(menu, "A&nnuler", nil, self, ID_CANCEL)

    FXMenuCascade.new(menu, "Sous-menu", nil, submenu)

       

Au lieu de 'self' c'est bien le sous-menu devant apparaître qui est passé en paramètre, ici 'submenu'.

 

Le bouton, situé à droite de la boîte, avec une flèche sur sa droite, déclanche donc l'apparition de sous-menus, qui à leur tour peuvent faire de même, tandis que le bouton de gauche affiche une valeur.

Notre tour d'horizon des menus ne serait pas complet sans les barres déplaçables, contenant des boutons, des menus...bref des composants qui bougent avec la barre et se réorganisent suivant la forme de cette dernière. Commençons donc par le propriétaire principal qu'est FXToolBarShell dont la déclaration ne comporte que deux paramètres principaux : 'owner' et 'opts', vu que c'est lui le proprio... Hop, terminé, passons donc au suivant . Il peut s'agir soit d'un FXMenubar, pour les menus conventionnels, soit d'un FXToolbar  pour les menus composés de boutons à icône. FxMenubar a été vu plus haut, voyons donc FXToolbar dont la déclaration est la suivante :

new(p,q,opts....)

 dans le cas où il est rattaché à un FXToolBarShell, et

new(p,opts...)

 dans le cas où il est non-flottant. Dans le premier cas le paramètre 'p' désigne, comme vous vous en doutez, le propiétaire, et 'q' pointe vers son autre fenêtre d'accueil, généralement un FXToolBarShell. En définitive FXToolBarShell n'est qu'une simple sous-fenêtre, avec certes un comportement particulier, rattachée à la fenêtre principale ...

Dans ces FXToolbar vont prendre place suivant leur rôle, soit des FXMenuCommand, soit des boutons avec une icône. Reste que pour manipuler ces composants il nous faut une poignée (avez-vous tenté d'ouvrir un tiroir sans poignée, pas facile n'est ce pas ?), et c'est FXToolBarGrip qui s'y colle avec la définition suivante :

new(parent, target=nil, selector=0, opts...)

Généralement parent et cible ne font qu'un.

 

Un petit exemple pour concrétiser :

 

*****************************************************************************

require 'fox'

include Fox

 

class ToolWindow < FXMainWindow

  def initialize(app)

    super(app, "FOX barre d'outils", nil, nil, DECOR_ALL,

      0, 0, 340, 200, 0, 0)

    fileopenicon = getIcon("fileopen.png")

    filesaveicon = getIcon("filesave.png")

    paletteicon = getIcon("palette.png")

 

    # Construit la barre de menus.

    dragshell1 = FXToolbarShell.new(self, 0)

    menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|FRAME_RAISED)

    FXToolbarGrip.new(menubar, menubar, FXMenubar::ID_TOOLBARGRIP,

      TOOLBARGRIP_DOUBLE)

 

    # Barre d'outils.

    dragshell2 = FXToolbarShell.new(self, 0)

    toolbar = FXToolbar.new(self, dragshell2, (LAYOUT_SIDE_TOP|

      PACK_UNIFORM_WIDTH|PACK_UNIFORM_HEIGHT|FRAME_RAISED|LAYOUT_FILL_X))

    FXToolbarGrip.new(toolbar, toolbar, FXToolbar::ID_TOOLBARGRIP,

      TOOLBARGRIP_DOUBLE)

 

    # Barre de statut.

    statusbar = FXStatusbar.new(self,

      LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER)

    statusbar.statusline.normalText="Prêt"

   

    # Menu fichier.

    filemenu = FXMenuPane.new(self)

    FXMenuTitle.new(menubar, "&Fichier", nil, filemenu)

     

    # Boutons avec icône.

    openBtn = FXButton.new(toolbar, "&Ouvrir\tOuvrir un fichier\tOuvre un fichier.", fileopenicon,

      nil, 0, ICON_ABOVE_TEXT|BUTTON_TOOLBAR|FRAME_RAISED)

    saveBtn = FXButton.new(toolbar, "&Sauver\tSauvegarde\tSauve un fichier.", filesaveicon,

      nil, 0, ICON_ABOVE_TEXT|BUTTON_TOOLBAR|FRAME_RAISED)

       

    # Couleur.

    FXButton.new(toolbar, "&Couleurs\tCouleurs\tAffiche la boîte de dialogue couleurs.", paletteicon,nil,0,

      ICON_ABOVE_TEXT|BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_RIGHT)

 

    # Entrées du menu "Fichier".

    FXMenuCommand.new(filemenu, "&Ouvrir...\tCtl-O\tOuvrir un fichier.", fileopenicon)

    FXMenuCommand.new(filemenu, "&Sauver...\tCtl-S\tSauver un fichier.", filesaveicon)

    FXMenuCommand.new(filemenu, "&Quitter\tCtl-Q").connect(SEL_COMMAND, method(:onCmdQuit))

 

    FXTooltip.new(getApp(), TOOLTIP_NORMAL)

  end

 

  def getIcon(filename)

    FXPNGIcon.new(getApp(), File.open(filename, "rb").read)

  end

  def onCmdQuit(sender, sel, ptr)

     getApp().exit(0)

  end

  def create

    super   # i.e. FXMainWindow::create()

    show

  end

end # classe

 

def run

    application = FXApp.new("Boite outils", "FoxTest")

    window = ToolWindow.new(application)

    application.addSignal("SIGINT", window.method(:onCmdQuit))

    application.create

    application.run

end

 

run

 

*****************************************************************************

 

Et nous obtenons la fenêtre suivante :

 

 

tooltest.png

 

Remarquez dans les "FXToolBarGrip"  le sélecteur qui correspond à la cible. Si la commande est en fait la même (ID_TOOLBARGRIP), son propriétaire doit être de la même classe que la cible (FXMenuBar et FXToolBar). Ca coule de source, mais cela va mieux en le disant.

 

 

Un peu de Ruby :

 

Les méthodes singletons.

Nous avons vu précédemment que les objets ont des méthodes, et que le descendant d'un objet hérite de ces méthodes. Il peut arriver que l'on ait besoin dans certains cas d'avoir un héritier qui sorte du lot et doive avoir un comportement différent, tout en gardant le même nom de méthode. Dans d'autres langages ceci n'est soit pas possible, soit nécessite des précautions si ce n'est des prévisions à long terme en prévoyant ce cas éventuel dans l'ancêtre lui-même. Ou bien aussi de créer de toutes pièces une nouvelle classe. Avec Ruby, vous vous en doutiez n'est ce pas ?, pas de problème, il y a ce que l'on appelle les méthodes singletons qui permettent de redéfinir une méthode à la volée :

 Class oiseau

   def modeLoco

      print "vole"

   end

end

 

pingouin = oiseau.new

 

manchot = oiseau.new

def  manchot.modeLoco

  print "ne vole pas"

end

 

pingouin.modeLoco   => "vole"

manchot.modeLoco  => "ne vole pas"

 

L'exemple ci-dessus n'est pas innocent, puisqu'il paraît que les Français (pas les Belges ?) confondent les pingouins du pôle Nord avec les manchots du pôle Sud…

Bien entendu, il est tout à fait autorisé de redéfinir une classe :

Class manchot< oiseau

   def modeLoco

      print  "ne vole pas""

   end

end

Ce qui ne fera tout de même pas voler les manchots… Le signe '<' indique que 'manchot' est une nouvelle classe descendant de la classe 'oiseau' et hérite par conséquent de tout ce que possède 'oiseau'.

Vous avez pu voir, si votre sens de l'observation est toujours en éveil, que parfois, dans les exemples précédents, une variable était attribuée à une instance et parfois non. L'explication est simple. Si l'on n'a pas besoin d'accéder ni aux attributs ni aux méthodes, il n'est pas nécessaire d'attribuer une variable à une instance de composant. Seul inconvénient, le ramasse-miettes de Ruby, va détruire l'objet sitôt qu'il ne servira plus. En fait cela n'a aucune importance, puisqu'il se recréera au prochain appel et dans la transparence la plus totale pour l'utilisateur.

 

Les tableaux :

Nous avons déjà parlé des tableaux mais sans nous y attarder. Il est temps de voir plus en détail ce concept très utilisé en programmation.

En Ruby vous pouvez créer des tableaux très simplement en affectant des valeurs entre crochets à une variable :

monTableau = [] (ensemble vide)

monTableau = [1,2,"trois", "quatre"]

Nous voyons que les valeurs peuvent être de plusieurs types. Nous pouvons utiliser les tableaux un peu comme des chaînes de caractères en les concaténant :

monTableau + ["cinq",6]   contiendra  [1,2,"trois", "quatre","cinq",6]

en les multipliant :

monTableau*2 =[1,2,"trois", "quatre","cinq",6,1,2,"trois", "quatre","cinq",6]

On peut se référer aux éléments par un indice (qui commence à '0').

monTableau[0] = 1

monTableau[2,2]  = ["trois","quatre"]

monTableau[-2,2] = ["cinq",6] (les indices négatifs vont de la droite vers la gauche)

monTableau[13] = nil

En Ruby les tableaux, comme le reste d'ailleurs, sont très "puissants" et possèdent une multitude de méthodes. En voici quelques-unes :

·        '|' pour l'union  ["1","2",3"] | ["2","3","4"] = ["1","2","3","4"]

·        '&' pour l'intersection [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] = [1, 3]

·        '<<' ajoute à la fin [ 1, 2 ] << "c" << "d" = [1, 2, "c", "d"]

·        '==" l'égalité [ "a", "b" ] == [ "a", "b", "c" ] = false

·        '.clear' vide le tableau : a = [ "a", "b", "c" ],  a.clear = []

·        '.include?'  booléen : a = [ "a", "b", "c" ]

·        a.include?("b") = true

·        a.include?("z") = false

·        '.index' donne l'indice : a = [ "a", "b", "c" ]

·        a.index("b") = 1

·        a.index("z") = nil

·        '.lenght' donne le nombre d'éléments : [ 1, 2, 3, 4, 5 ].length = 5

·        '.sort' pour trier : a = [ "d", "a", "e", "c", "b" ]  a.sort =  ["a", "b", "c", "d", "e"]

 

 

 

Les tables de hachage :

  Aucun rapport avec votre boucher. L'autre nom connu est le "dictionnaire". Plus simplement c'est comme un tableau à deux colonnes, où se trouvent une clef dans la première et une donnée dans la seconde. Si dans un tableau on accède aux éléments au moyen d'un indice, dans une table de hachage c'est par une clef. Une table de hachage se construit en Ruby avec des groupes de deux éléments entre accolades '{}', les éléments étant séparés par une flèche composée du signe '=' et de '>' soit '=>' :

h = {"a"=>100, "b"=>200}

h["a"] renvoie 100

h["c"] renvoie nil

h["c"] = 300 agrandit la table de la clef "c" et de la donnée 300

Comme pour les tableaux, voici quelques méthodes :

·        '.clear' vide la table.

·        '.default'  donne une valeur à la valeur par défaut : h.default = "Loupé !"h["d"] renvoie "Loupé"

·        '.delete' supprime une clef et sa donnée : h.delete["c"]

·        'has_value?' bolléen, renvoie 'true' si la valeur est présente dans les données : h.has_value(100) renvoie 'true'

·        '.index' retourne la clef pour une valeur donnée : h.index(200) renvoie "b".

·        '.lenght' renvoie le nombre de paires de valeurs de la table : h.lenght renvoie 2.

·        '.sort' trie la table.

·        '.to_s' renvoie une chaîne avec les valeurs de la table : h.to_s renvoie  "a100b200"


Chapitre 6

 

Le composant "Boîte de texte" et divers gadgets :

 

  Gros chapitre que nous entamons là. Mais tout est si simple avec FOX, que nous n'hésitons pas à attaquer de front plusieurs composants, visuels ou non d'ailleurs.

 

La boîte de saisie, appelée parfois "InputBox" ou "TextBox" ou bien encore "EditBox" etc.. , se nomme en FOX "FXTextField". Sa déclaration est la suivante :

FXTextField.new(p, numColumns, tgt=nil, sel=0, opts=TEXTFIELD_NORMAL, x=0, y=0, w=0, h=0, pl=DEFAULT_PAD, pr=DEFAULT_PAD, pt=DEFAULT_PAD, pb=DEFAULT_PAD)

-         'p' est le propriétaire.

-         'numcolums' le nombre de caractères possible à l'affichage.

-         'tgt' la cible des messages éventuels.

-         'sel' le sélecteur

-         etc…

C'est tout bêtement une zone rectangulaire blanche dans laquelle l'utilisateur est amené à entrer du texte et des chiffres, ou bien uniquement à y lire une information non modifiable directement. FXTextField peut cependant se spécialiser par le choix de ses options 'opts'. Il peut être converti en boîte de saisie de nombres entiers, de nombres réels, de texte de longueur fixée ou bien encore en mode secret pour entrer un mot de passe.

 

Pour communiquer avec les autres composants il en existe un, non visuel celui-là, qui sert d'estafette. Il s'agit de FXDataTarget qui permet à un composant comme FXTextField d'être relié directement à une variable ou à un ou plusieurs compères.

  Rien de tel qu' un petit programme pour visualiser tout cela et nous ferons le point après.

 

Voici donc "progress.rbw"

 

***************************************************************************

 

require 'fox'

include Fox

 

class ProgressWindow < FXMainWindow

  def initialize(app)

    # Initialise la classe de base.

    super(app, "Test de progression", nil, nil, DECOR_ALL, 20, 20, 600,100)

 

    # Crée une cible avec une valeur entière.

    @intTarget = FXDataTarget.new(10)

 

    # Barre de menu.

    menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)

   

    # Menu Fichier.

    filemenu = FXMenuPane.new(self)

    FXMenuCommand.new(filemenu, "Dialogue de

     progression...").connect(SEL_COMMAND) do

      @progressdialog.show(PLACEMENT_OWNER)

    end

   FXMenuCommand.new(filemenu,"&Quitter\tCtlQ",nil,getApp(),FXApp::ID_QUIT)

    FXMenuTitle.new(menubar, "&Fichier", nil, filemenu)

 

    # Crée une boîte d'information reliée à la valeur de @intTarget.

    @progressdialog = FXProgressDialog.new(self, "Progression", "%

     effectué...",PROGRESSDIALOG_CANCEL|DECOR_BORDER|DECOR_RESIZE)

    @progressdialog.target = @intTarget

    @progressdialog.selector = FXDataTarget::ID_VALUE

 

    FXHorizontalSeparator.new(self,LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|

       LAYOUT_FILL_X)

 

    # Une matrice pour bien aligner les composants.

    matrix = FXMatrix.new(self, 7,

      MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)

 

  

    FXLabel.new(matrix, "&Entier", nil,

      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)

    FXTextField.new(matrix, 10, @intTarget, FXDataTarget::ID_VALUE,

  LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)

    FXTextField.new(matrix, 10, @intTarget, FXDataTarget::ID_VALUE,

  LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)

    FXSlider.new(matrix, @intTarget, FXDataTarget::ID_VALUE,

      LAYOUT_CENTER_Y|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH, 0, 0, 100)

    FXDial.new(matrix, @intTarget, FXDataTarget::ID_VALUE, (LAYOUT_CENTER_Y|LAYOUT_FILL_ROW|LAYOUT_FIX_WIDTH|DIAL_HORIZONTAL|DIAL_HAS_NOTCH),0, 0, 100)

    FXSpinner.new(matrix, 5, @intTarget, FXDataTarget::ID_VALUE,

      SPIN_CYCLIC|FRAME_SUNKEN|FRAME_THICK|LAYOUT_CENTER_Y|LAYOUT_FILL_ROW)

    FXProgressBar.new(matrix, @intTarget, FXDataTarget::ID_VALUE,

      (LAYOUT_CENTER_Y|LAYOUT_FILL_X|FRAME_SUNKEN|FRAME_THICK|

      PROGRESSBAR_PERCENTAGE|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW))

 

    # Installe un accélérateur clavier de fermeture.

    self.accelTable.addAccel(fxparseaccel("Ctl-Q"), getApp(),

       MKUINT(FXApp::ID_QUIT, SEL_COMMAND))

  end

 

   # Méthode pour Quitter.

    def onCmdQuit(sender, sel, ptr)

      getApp.exit(0)

    end

 

  # Début.

  def create

    # Création de la fenêtre.

    super

 

    # Montre la fenêtre principale.

    show(PLACEMENT_SCREEN)

  end

end

 

if __FILE__ == $0

  # Construit une application.

  application = FXApp.new("Progression", "FoxTest")

 

  # L'implémentation de "threads' actuels peut causer des problèmes avec cet exemple. On les désactive donc.

  application.threadsEnabled = false

 

  # Crée la fenêtre principale.

  window = ProgressWindow.new(application)

 

 # Poignée d'interruption pour quitter l'application d'une manière gracieuse

  application.addSignal("SIGINT", window.method(:onCmdQuit))

 

  # Création de l'application.

  application.create

 

  # Et roulez jeunesse !

  application.run

end

 

*****************************************************************************

 


Et voici ce que vous devez obtenir à l'écran :

 

 


Progress.png

 

 

Nous voyons donc dans l'ordre et de gauche à droite : une étiquette, suivit de deux boîtes de saisie, puis un potentiomètre, un cadran, un compteur et enfin une barre de progression.

En cliquant sur le menu 'Fichier', puis sur la sous-commande 'Dialogue de progression', vous verrez apparaître la fameuse fenêtre d'information anti-stress  vous annonçant que tel programme se déroule normalement mais qu'il est un tout petit peu long à s'exécuter…

En modifiant la valeur d'un seul composant on modifie tous les autres, grâce à celui qui est invisible : FXDataTarget.

Dans l'ordre d'entrée en scène nous avons donc :

·        L'appel, auquel nous sommes maintenant habitués, à 'super'.

·        La création de FXDataTarget initialisé à la valeur '10' , ce qui va par la même occasion initialiser les autres composants à cette valeur, vu qu'ils ont tous '@intTarget' comme cible.

·        Vient ensuite la description de la barre de menu  (Fichier à Dialogue… et Quitter)

·        L'initialisation de la boîte d'information de la progression. Si vous avez la curiosité de regarder dans l'appendice A, afin de rechercher les attributs de FXProgressDialog initialisés dans les lignes suivantes ('.target' et '.selector'), vous ne les trouverez pas tout de suite. N'oubliez pas que dans Ruby et FOX tout est objet, et qu'un objet possède la particularité d'hériter de ses ancêtres. Il va donc falloir remonter assez haut pour trouver l'origine de ces attributs : FXProgressDialog => FXDialogBox => FXTopWindow => FXShell => FXComposite => FXWindow.

·        Le séparateur horizontal, pour faire joli.

·        Un nouveau composant : FXMatrix à qui il suffit d'indiquer le nombre de colonnes désiré, les options habituelles de positionnement et le tour est joué, FXMatrix va ranger ses enfants dans l'ordre et la discipline. Il faudrait un de ces composants pour chaque classe de collège…

·        Et voici les 7 composants qui seront tous enfants de 'matrix'. Blanche Neige et les 7 nains quoi… Pour commencer FXLabel, déjà vu et revu, passons donc au suivant.

·        FXTextField. Le composant actif par excellence, puisqu'on peut y écrire dedans. Comme c'est un composant générique capable de faire une foultitude de choses, il ressemble, en plus petit, à un traitement de texte complet. Vous en trouverez la description complète en Annexe A. Tout ce que nous allons en dire pour le moment consiste en la description de son initialisation dans le programme. Donc en dehors des paramètres de positionnements facultatifs nous avons dans new(p, numColumns,tgt=nil,sel=0,pts=TEXTFIELD_NORMAL) le paramètre 'p' du propriétaire (ici 'matrix' en personne), le nombre de colonnes ('numColumns') ici '10' pour faire un peu large, la cible 'tgt' représentée par l'instance de FXDataTarget qu'est '@intTarget'. La cible va se comporter comme un miroir et renvoyer à tous les composants dont elle est la cible tout changement qui sera accompagné du message 'ID_VALUE' qui est partie prenante du paramètre suivant, le sélecteur 'sel'. Les options de 'pts' sont là pour l'arrangement habituel du composant, plus LAYOUT_FILL_ROW qui va remplir la cellule de la matrice. Encore plus intéressants sont les attributs qui vont de la précision de la couleur du texte, sa justification, sa longueur en passant par la position du caret (curseur texte). Précisons pour terminer qu'il supporte les opérations couper, copier, coller du presse-papier.

·        Les autres composants ont tous la même présentation  de la méthode 'new' et le même ordre d'entrée des paramètres. Nous allons donc uniquement décrire quelques-unes de leurs possibilités, les plus curieux se réfèreront à l'Annexe A. (Finalement je n'aurais dû écrire que l'Annexe A, diront certains). Bref le suivant est le potentiomètre FXSlider.

·        Le prochain sur la liste est le cadran, FXDial. Un vu-mètre serait plus exact. Les attributs accessibles ne sont pas aussi nombreux que ceux de FXTextField mais citons tout de même celui définissant l'intervalle de valeurs prise en compte :'.range', et celui renseignant la valeur à prendre en compte pourl'affichage : '.value'.

·        Le compteur, en nombre entier, FXSpinner, peut voir sa valeur augmenter ou diminuer suivant la flèche cliquée, ou bien la valeur qu'il reçoit de FXDataTarget, ou bien encore par le changement de la valeur contenue dans sa fenêtre, laquelle est sélectionnable comme dans un FXTextField.

·        Arrive enfin le dernier de la liste, la barre de progression FXProgressBar. La barre peut être horizontale ou verticale, ou bien encore se transformer en cadran circulaire grâce à son option 'opts'. Elle affiche aussi, toujours en fonction des options, soit la valeur seule soit la valeur suivie du signe pourcentage.

·        Continuons par un composant non visuel qui permet de programmer aisément la sortie du programme, ou d'autres commandes, par l'intermédiaire du clavier. Il s'agit de FXAccelTable. C'est une table qui contient  : des "messages" à envoyer à des "cibles" lorsque une certaine combinaison  de "touches" est frappée. En bref nous avons utilisé la déclaration suivante : addAccel(hotKey, target=nil, seldn=0, selup=0), 'hotKey' étant "Ctrl-Q", 'target' étant l'application elle-même appelée par la fonction 'getApp', et le sélecteur transformé par MKUINT qui est une méthode  du module 'FOX' qui transforme les deux paramètres de 16 bits en un seul de 32.

·        Les 'threads'. Ce sont les différentes tâches lancées durant l'exécution d'un processus et gérées, tant bien que mal, par le système d'exploitation qui se veut du coup "multi-tâches". Au moment de l'écriture de ces lignes , le créateur de FXRuby pour Windows n'est pas satisfait du support de l'actuel multi-tâches et par conséquent préfère prévenir toute panne éventuelle en le déconnectant  dans certains programmes.

 

 

 

 

·        Terminons en beauté avec le bizarre "SIGINT". Les méthodes FXApp#addSignal ainsi que FXApp#removeSignal, ont été améliorées pour accepter une chaîne ou un entier comme premier argument. Si c'est une chaîne (par ex. "SIGINT' ou juste "INIT") le code déterminera le numéro du signal correspondant pour vous (comme la méthode Process.kill de la librairie standard de Ruby). Il existe 3 formes d'appel pour intercepter les signaux du système d'information. Chaque version possède aussi deux arguments optionnels (voir dans FXApp en appendice).La plus ancienne spécifie la cible et l'identifiant du message :

                      aSignal = getApp().addSignal("SIGINT", signalHandlerObj, ID_SIGINT)

      La seconde prend soit une méthode , soit une instance de procédure ('Proc') comme second

      argument :

                      aSignal = getApp().addSignal("SIGINT", method(:signalHandlerMethod))

      La dernière forme utilise un bloc de code comme poignée pour le signal :

                      aSignal = getApp().addSignal("SIGINT") { |sender, sel, ptr|

                                           # traite le signal

                                        }

 

Puisque nous en sommes aux boîtes d'entrée de texte et à la communication, essayons de simplifier, largement, l'exemple ci-dessus et de faire des tests variés. Après un traitement spartiate nous arrivons au code suivant de test1.rbw :

 

*****************************************************************************

 

require 'fox'

include Fox

 

class Test1Window < FXMainWindow

  def initialize(app)

    super(app, "Test_1", nil, nil, DECOR_ALL)

 

    FXHorizontalSeparator.new(self,

      LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)

 

    matrix = FXMatrix.new(self, 3,

      MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)

 

    FXLabel.new(matrix, "&Integer", nil,

      LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)

    @t1 = FXTextField.new(matrix, 10, nil,0,

    LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)

    @t2 = FXTextField.new(matrix, 10, nil,0,

    LAYOUT_CENTER_Y|LAYOUT_CENTER_X|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_ROW)

    @t1.connect(SEL_COMMAND){

      @t2.text=@t1.text*2

    }

    @t1.setFocus

 

  # Démarrage

  def create

    super

    show(PLACEMENT_SCREEN)

  end

end

 

if __FILE__ == $0

  application = FXApp.new("Test", "FoxTest")

  window = Test1Window.new(application)

  application.create

  application.run

end

*****************************************************************************

 


Ici plus question d'échange de données évoluée et instantanée, mais de communication de données d'un composant à un autre après un traitement quelconque. @t1 envoie un message SEL_COMMAND et multiplie la valeur de sa zone de texte par 2. Si vous êtes un tant soit peu curieux, vous avez déjà parcouru l'Appendice A et vu que le message SEL_COMMAND de l'objet FXTextField est envoyé lors de l'appui de la touche 'Enter' ou de la touche 'Tab'. Et nous obtenons le résultat suivant :

 

 


Test1.png

 

La réponse doit vous paraître bizarre non ? comme c'est bizarre. L'explication vient du fait que Ruby traite les mots comme des nombres, et donc '* 2' signifie pour lui que l'on veut doubler l'entrée; il répète donc  ce que l'on a tapé. Donc si l'entrée est 'AaBb' le texte de '@t2' sera  'AaBbAaBb'. Pour effectuer de vrais calculs il suffit de transformer la donnée chaîne en donnée nombre. Remplaçons donc la ligne suivant celle de '@t1.connect' par

                                        '@t2.text = (@t1.text.to_i * 2).to_s

qui va tout faire rentrer dans l'ordre en transformant d'abord le texte en nombre entier ('.to_i'), en le multipliant par 2 et finalement en retransformant le tout en chaîne ('.to_s). Voici le résultat plus sympathique pour les matheux :


 

 


test2.png

 

Et pour que tout soit vraiment en ordre, ajoutons aux options de '@t1'  : TEXTFIELD_INTEGER|, ce qui obligera à entrer uniquement des chiffres. Pour améliorer encore la saisie, nous pouvons indiquer que nous voulons des réels en ajoutant 'TEXTFIELD_REAL|'  ( au lieu de TEXTFIELD_INTEGER|, bien sûr) aux options et changer la méthode '.to_i' en '.to_f' (float). Nous aurons donc accès à la lettre 'e' (ou'E'), au point '.' (notation anglaise, la virgule ne fonctionnant pas), et bien sûr à tous les chiffres.

La ligne @t1.setFocus force le focus sur '@t1' afin d'avoir le caret sur ce composant dès l'apparition de la fenêtre.

 Histoire de voir toutes les possibilités du composant FXTextField, ajoutons dans les options de '@t1' le mot 'TEXTFIELD_PASSWD|' ce qui permet de cacher l'entrée lors de la frappe.


 


Test3.png

 

Nous pouvons encore améliorer le traitement et appeler une méthode :

@t2.text = (mul(@t1.text.to_i)).to_s

 

                                                 def mul(n)

                                                     n *= 2

                                                 end

 

Afin d'empêcher la saisie dans '@t2', il suffit de fixer son attribut 'editable' à 'false', après son initialisation ça va de soi, donc suite à la ligne '@t1.setFocus' par exemple :

'@t2.editable = false'

 

 

Un peu de ruby :

 

Les chaînes de caractères

Nous avons vu au chapitre 3 que les chaînes de caractères pouvaient recevoir des modifications en fonction de la présence du caractère '\'. Ruby possède de nombreuses autres méthodes de traitement des chaînes. Nous en avons déjà vu dans l'exemple précédent.

-                               Si mot = "to" alors 'mot * 3'   ==> "tototo"

- Pour concaténer des chaînes (ajouter l'une à l'autre), le signe '+' est de rigueur :

                                             "to" + "to" ==> "toto"

  si a = "to" alors a << "tem" ==> a sera égal à "totem"; '<<' modifie donc la variable.

- Pour extraire un élément de mot, on utilise les crochets '[]', Ruby traitant les chaînes comme     des tableaux. S'il y a un seul chiffre, c'est le code du caractère qui est retourné :

 a = "Bonjour'

 a[1] = 111 c'est à dire le code du caractère 'o', l'indice débutant à zéro !

mais par contre a[1,1] ="o"  et a[1,3] ="onj"

a[-3,2]  = "ou" dans le cas de la marche arrière on part de la droite et on débute à 1 et non à 0.

a[-5..-3] = "njo" ici on utilise l'intervalle grâce au deux points '..'

a["on"] retourne "on" parce que la correspondance existe.

a["zut"] retourne 'nil'.

-         pour les comparaisons il y a la méthode '<=>' qui retourne '1', '0', '-1'

   "abcdef" <=> "abcde"        ==>  1

    "abcdef" <=> "abcdef"        ==>  0

    "abcdef" <=> "abcdefg"      ==> -1

    "abcdef" <=> "ABCDEF"        ==> 1

-         le test d'égalité '==' retourne un booléen 'true' ou 'false'

-         le remplacemnt de caractères s'effectue par '[]=' :

      a = "Bonjour"

      a[2,1]="xyz"   ==> "Boxyzjour"

      a[-4,2]="xyz"  ==> "Bonxyzur"

-         la méthode 'capitalize' retourne une chaîne débutant par une majuscule le reste en minuscule :

     "bonjour".capitalize   => "Bonjour"

     "BONJOUR".capitalize  =>"Bonjour"

-         la méthode 'downcase'  transforme toutes les majuscules en minuscules.

-         la méthode 'upcase' transforme toutes les minuscules en majuscules.

-         'to_i' convertit une chaîne en un entier (i pour integer).

-         'to_f' convertit une chaîne en réel (f pour float)

-         'length' retourne la longueur de la chaîne.

 

et de nombreuses autres méthodes dont l'intégralité se trouve dans "Programming Ruby"

 

Les nombres

Les nombres sont répartis en deux grandes classes qui sont 'Fixnum' pour les entiers et 'Bignum' pour les réels. Les entiers sont dans un intervalle allant normalement de -230 à 230-1 ou -262 à

262-1. Hors de cet intervalle ils sont stockés comme des objets de la classe Fixnum.

Les nombres aussi ont leurs méthodes, de naissance si l'on peut dire, puisque tout est objet dans Ruby, répétons-le.

-  Tout d'abord les signes d'opérations élémentaires : +, --, *, /, **, %. Attention la soustraction nécessite deux signes '-'.

- 'to_s' transforme un nombre en une chaîne.

- La méthode de comparaison '<=>' qui retourne '-1', '0', ou '1' suivant que l'argumeznt de gauche est <, = ou > à celui de droite.

- Les méthodes d'opérations bit à bit :

                   ~ a  Inversion  bit à bit (complément)

                   a | n  OU bit à bit

                   a & n    ET bit à bit

                   a ^ n   OU EXCLUSIF bit à bit

                   a << n  décalage à gauche de n bits

                   a >> n  décalage à droite de n bits

 

Etc... même remarque que pour les chaînes ci-dessus.

 


Chapitre 7

 

Les images :

 

 C'est bien beau de faire des gribouillages sur un canevas, comme au chapitre 4,  mais il y a certainement mieux à faire, charger des images d'artistes plus "inspirés", par exemple. Les composants à la base des affichages d'images et des opérations éventuelles sont FXImage et FXImageView dont les déclarations sont les suivantes :

 

FXImage.new(app, pixels=nil, options=0, width=1, height=1)

et

FXImageView .new(p, img=nil, tgt=nil, sel=0, opts=0, x=0, y=0, w=0, h=0)

(voir les autres détails, comme d'habitude, à l'appendice A)

 

Pour nous entraîner à manipuler les images nous allons partir du petit programme de "gribouillage" et le transformer pour effectuer différentes manipulations.

 

 

testimg.rbw

 

***************************************************************************

require 'fox'

require 'fox/colors'

 

include Fox

 

class ImageWindow < FXMainWindow

  def initialize(app)

    super(app, "Test image", nil, nil, DECOR_ALL,0,0,340,200)

    @contents = FXHorizontalFrame.new(self,

      LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)

    @canvasFrame = FXVerticalFrame.new(@contents,

      FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 10, 10)

    FXLabel.new(@canvasFrame,"Cadre de l'image",nil,

                JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    FXHorizontalSeparator.new(@canvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X)

 

# Composant visionneuse d'image.

    @imageview = FXImageView.new(@canvasFrame, nil, nil, 0,

      LAYOUT_FILL_X|LAYOUT_FILL_Y)

 

    @buttonFrame = FXVerticalFrame.new(@contents,

      FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 10, 10)

    FXLabel.new(@buttonFrame, "Cadre boutons", nil,

      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    FXHorizontalSeparator.new(@buttonFrame,SEPARATOR_RIDGE|LAYOUT_FILL_X)

# Le bouton de test.

    testButton = FXButton.new(@buttonFrame, "&Test", nil, nil, 0,

      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 5, 5)

    testButton.connect(SEL_COMMAND, method(:onCmdChange))

 

    FXButton.new(@buttonFrame, "&Quitter", nil, app, FXApp::ID_QUIT,

      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,

      0, 0, 0, 0, 10, 10, 5, 5)

    

    loadImage("BigPenguin.png")

  end   # initialize

 

  # Chargement de l'image.

  def loadImage(file)

    img = nil

    img = FXPNGImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

    FXFileStream.open(file, FXStreamLoad) { |stream| img.loadPixels(stream) }

    img.create

    @imageview.image = img

  end

 

 # Ce qui change dans le test.

  def onCmdChange(sender, sel, ptr)

     img = @imageview.image

     img.rotate(90)

     @imageview.image = img

  end

 

  def create

    super                 

    show(PLACEMENT_SCREEN)

  end

 

end  # class

 

def run

  application = FXApp.new("TestImage", "FoxTest")

  window = ImageWindow.new(application)

  application.create

  application.run

end

 

run

 

***************************************************************************

 

Nous avons tout simplement remplacé le canevas par un visionneur FXImageView et modifié le bouton d'effacement de telle sorte qu'il communique les ordres à l'image, laquelle est chargée par la méthode 'loadImage'. Nous allons attribuer au bouton de test une commande pour effectuer l'opération de notre choix par l'intermédiaire de SEL_COMMAND. Nous avons déjà eu l'occasion de charger des icônes, voilà le tour des images. Le format n'étant pas le même, le principe pour les charger ne l'est pas non plus. Nous attribuons à la variable 'img' la création par 'new' d'une image 'PNG' et nous la chargeons par l'intermédiaire d'un flux. 'FXFileStream.open' ouvre un fichier 'file' avec la directive 'FXStreamLoad', qui indique un chargement (contrairement à FXStreamSave qui sauvegarde).

 

 

Ce qui donne l'image ci-dessous :


 

 


testimg1.png

 

 

C'est dans la méthode onCmdChange que nous effectuerons nos essais proprement dits en changeant la méthode de la variable intermédiaire 'img'. Comme premier essai nous avons choisi de faire tourner l'image de 90 degrés :

'img.rotate(90)'

Cliquez sur le bouton 'Test' et le pingouin bascule sur le côté. Vous pouvez, sans aucun risque pour sa vie, le faire tourner de divers angles en modifiant le paramètre de 'rotate'.

D'autres méthodes  sont définies dans la classe FXImage, à vous de les essayer. Par exemple pour modifier la taille de l'image, remplacez 'img.rotate()' par

'img.scale(96,114)'

qui va grossir l'image(la taille d'origine est de 48 X 57 pixels).

La méthode 'crop' va vous donner la photo d'identité du pingouin en faisant

'img.crop(12,0,25,25)'

Il s'agit de la méthode permettant de recadrer une image, en extraire une partie.

Dernière méthode disponible, 'mirror' permet d'effectuer une symétrie soit par rapport à la verticale soit par rapport à l'horizontale.

'img.mirror(true,false)'

permet de faire regarder le pingouin dans l'autre direction.


 

 

 


testimg2.png

 

D'où l'explication de la présence d'un pingouin dans Windows : ce n'est pas un clin d'oeil à Linux mais tout simplement la nécessité de faire tourner la tête d'une façon qui ne passe pas inaperçue...

Le chargement d'images n'est pas limité aux fichiers ".PNG". FXRuby est capable, au moment où ces lignes sont écrites (c'est à dire pour la version 1.6.8 de Ruby et 1.0.26 de FXRuby) de lire les formats d'images suivants  : BMP, ICO, GIF, JPG, PNG, PCX, TIF, TGA, XPM avec plus où moins de bonheur il faut le dire (mais cela est peut-être - je n'ose pas dire certainement -  dû à l'âge canonique de mon matériel !). L'appel se faisant de manière identique (heureusement !) pour tous les formats :

FXxxxImage.new(app, pixels=nil, opts=0, width=1, height=1)

-         App désigne l'application.

-         Pixels  désigne un pointeur vers un tableau de données représentant l'image.

-         Opts désigne les options éventuelles déclarées dans FXImage.

-         Width désigne la largeur de l'image.

-         Height désigne la hauteur de l'image.

 

Puisque nous sommes en train de voir comment afficher une image et la faire tourner dans tous les sens, pourquoi ne pas s'occuper aussi de la couleur. Au prix de quelques modifications, nous allons avoir accès à la boîte de couleur de Fox.

Rajoutons dans la méthode 'initialize' la déclaration de la dite boîte :

 

     # Création boîte de dialogue couleur.

colordlg = FXColorDialog.new(self, "Boîte de couleurs")

 

puisque nous avons déjà un bouton de test autant nous en servir en modifiant les paramètres de cible et de sélecteur, et en désactivant la méthode 'connect' par un '#' en début de ligne :

 

testButton = FXButton.new(@buttonFrame, "&Test", nil, colordlg, FXWindow::ID_SHOW,...

#    testButton.connect...

 

Et, oh miracle de FXRuby, voilà ce qui enchante nos yeux :

 

 

 

couleurs.png

 

Si ses joues sont bien colorées, la pauvre petite manque de renseignements, n'est ce pas ? Pour remédier à cette absence il suffit de rajouter les info-bulles :

FXTooltip.new(getApp(), TOOLTIP_NORMAL)

dans la méthode 'initialize' bien entendu.

Dans la partie gauche de la fenêtre se trouve un bouton muni d'une icône représentant une pipette. Ce bouton commande... une pipette, représentée par une croix qui sert à pointer un endroit de l'image. Vous pouvez ainsi ausculter le pingouin sous toutes ses coutures. La croix disparaît après chaque clic souris et affiche les composantes de la couleur choisie dans les divers volets représentants les diverses descriptions : RGB , HSV etc...

 

 

Un peu de Ruby :

 

Les classes

Depuis le début de cet ouvrage nous utilisons dans chaque programme, le mot 'class' et nous avons brièvement abordé le sujet dans le premier chapitre. Il est temps de développer les explications. Nous avons vu qu'une classe était en fait la description du type d'un objet, un bloc de code compris entre les mots 'class' et 'end', et dans lequel se trouvent des variables (attributs) et des méthodes. A partir d'une classe on peut créer des sous-classes qui hériteront de la classe supérieure. Ruby est suffisamment souple pour permettre de redéfinir une méthode héritée si le besoin s'en fait sentir ou bien de la compléter en appelant la méthode de la classe supérieure par 'super' et en rajoutant du code (cf. toutes les méthodes 'initialize' des composants FOX).

class nomdeclasse [< classesuperieure ]

                     code interne

                    end

Les classes peuvent par exemple avoir des constantes, qui seront communes à tous les descendants, et accessibles de l'extérieur par le nom de la classe suivie d'un double deux-points :  '::'

class Exemple

                                   CONST = 123

                                 end

 

a = Exemple::CONST

 

Par contre les variables propres aux instances sont accessibles par ce que l'on appelle des accesseurs.

 

Les accesseurs

Nous savons que les variables appartenant à des instances débutent par un '@'. Pour y accéder, soit en lecture soit en écriture, nous passons par des accesseurs d'attributs.

class Truc

  def EcritVarxyz(n)   # pour écrire dans la variable @xyz.

      @xyz=n

  end

  def LitVarxyz          # pour lire la variable @xyz.

    @xyz

  end

end

Mais Ruby propose, évidemment, des raccourcis pour ces méthodes :

class Truc

  def Varxyz=(n)   # pour écrire dans la variable @xyz.

      @xyz=n

  end

  def Varxyz          # pour lire la variable @xyz.

    @xyz

  end

end

 

Les modules

Un module est une classe qui ne peut être instanciée. Un module peut contenir des classes et des méthodes d'instances qui peuvent être invoquées en utilisant l'objet 'Module' comme un destinataire et les constantes sont accessibles par l'opérateur deux-point  : '::'. Un module ne peut pas avoir de sous-classes. Un module est définit par le bloc : module .... end.

Soit la définition :

 

module Truc

 CONST = 1

 def Truc.methode1

   CONST + 1

 end

end

 

Truc::CONST  donnera  1

Truc.methode1  donnera   2

 

 Un module permet de concentrer des constantes et des méthodes d'un même centre d'intêret. Par exemple le module 'Math' possède deux constantes ('E' et 'PI') et des méthodes se rapportant à l'exercice mathématique.

Math.sqrt(4)  donnera 2

Math::PI  donnera 3.141592654

 Nous avons vu que nous pouvons déclarer un module dans un programme par 'include'. Une autre manière consiste à inclure le module dans une définition de classe ce qui permet d'avoir en quelque sorte un héritage "multiple". C'est ce que l'on appelle, en anglais, les "Mixins" de "mixed in".

 

Les mixins

Un exemple valant souvent mieux qu'un long discours, supposons que votre classe possède une méthode '<=>' . Si on inclut le module 'Enumerable' on hérite de méthodes telles que 'find' pour chercher, 'sort' pour trier, 'max' pour une valeur maxi etc...Si de plus la méthode 'each' fait partie de la classe, c'est alors d'une quinzaine de méthodes dont on dispose.

Par exemple la méthode '.collect' donnera pour un intervalle :

(1..4).collect {|i| i*i }  ------>  [1, 4, 9, 16]

(1..100).detect {|i| i % 5 == 0 and i % 7 == 0 } ------> 35


Chapitre 8

 

Les boîtes à lister, les répertoires :

 

FXListBox

Entrées depuis longtemps dans les moeurs, les boîtes à lister, modifiables ou non, sont désormais inévitables et c'est une bonne chose, tant elles facilitent aussi bien le travail du programmeur que celui de l'utilisateur. Elles servent à choisir une valeur dans une liste, d'où leur nom.

Pour tester ces composants, nous allons continuer à utiliser notre petite fenêtre déjà vue au chapitre 6, et modifier au minimum son code. Commençons par la boîte à lister toute simple appelée FXListBox, dont la déclaration est la suivante :

   new(p, nvis, tgt=nil, sel=0, opts=FRAME_SUNKEN|FRAME_THICK|LISTBOX_NORMAL,

   x=0, y=0, w=0, h=0, pl=DEFAULT_PAD, pr=DEFAULT_PAD, pt=DEFAULT_PAD,

   pb=DEFAULT_PAD)

'p' est le parent, 'nvis' le nombre d'éléments visibles dans la fenêtre du composant. Les autres paramètres sont habituels.

 

*****************************************************************************

require 'fox'

include Fox

 

FAMILLE = ["PAPA", "MAMAN", "LA BONNE", "ET MOI"]

 

 

class ListWindow < FXMainWindow

  def initialize(app)

    super(app, "Test de liste", nil, nil, DECOR_ALL,0,0,200,40)

    matrix = FXMatrix.new(self, 2,

    MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)

     FXLabel.new(matrix, "Choix : ", nil,

    LAYOUT_CENTER_Y|LAYOUT_CENTER_X|JUSTIFY_RIGHT|LAYOUT_FILL_ROW)

    @List = FXListBox.new(matrix, 3, nil, 0,

      LISTBOX_NORMAL|FRAME_SUNKEN|FRAME_THICK)

    for i in 0..3

      @List.appendItem(FAMILLE[i])

    end

  end

 

  def onCmdQuit(sender, sel, ptr)

    getApp.exit(0)

  end

  def create

     super

     show(PLACEMENT_SCREEN)

  end

end

 

if __FILE__ == $0

   application = FXApp.new("Listes", "FoxTest")

   window = ListWindow.new(application)

   application.addSignal("SIGINT", window.method(:onCmdQuit))

   application.create

   application.run

end

 

*****************************************************************************

 

Ce qui donne l'image suivante :

 

 

list.png

 

Première remarque : le rectangle de visualisation se dimensionne automatiquement suivant la longueur du texte affiché, et les items défilent horizontalement, en suivant le curseur souris, permettant ainsi leur lecture. Deuxième remarque : le texte n'est pas modifiable et ne se met donc pas en surbrillance dans le rectangle habituel de saisie.

La déclaration n'appelle pas de remarque particulière. Sont disponibles également les textes d'aides habituels dans les info-bulles ou bien la ligne de statut. Le nombre d'items visibles dans la fenêtre peut être modifié par l'entremise de l'atttribut  'numVisible'. De la même manière les attributs permettent de modifier la fonte, la couleur du fond etc... voir l'appendice A.

Nouveauté en début de programme, la déclaration de tableau et l'insertion de ses éléments dans la liste par la méthode 'append'. La méthode 'prepend' les aurait insérés dans l'ordre inverse en les rajoutant au début de liste à chaque insertion.

Notons aussi que FXListBox offre en option la possibilité de stocker une icône et un pointeur vers une donnée, pour chaque élément de la liste.

 

FXComboBox

L'autre composant classique est la boîte "combo" dont la déclaration est la suivante :

new(parent, numColumns, numVisible, target=nil, selector=0, opts=COMBOBOX_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD)

La différence principale se situe dans le nombre de colonnes ('numColumns') à afficher, c'est à dire en fait le nombre de caractères. L'aspect est visuellement identique à FXListBox.

 

Remplaçons la ligne

'@List = FXListBox.new...'

par

'FXComboBox.new(matrix,10,3,nil,0,COMBOBOX_STATIC|FRAME_SUNKEN|FRAME_THICK)'

 

Avec l'option 'COMBOBOX_STATIC' le texte ne peut être modifié, mais il existe d'autres options qui permettent non seulement de le faire , mais aussi d'insérer le nouveau texte à l'endroit voulu ou bien encore de remplacer le texte mis en surbrillance. Voir les détails en appendice A.

FXComboBox admet outre son texte normal, un pointeur vers une donnée (facultatif).

 

FXListBox et FXComboBox possèdent tous deux les attributs nécessaires à la modification de couleurs des items, du nombre visible d'entre eux et des textes d'aide.

 

FXDirList

Composant visuel indispensable dès que l'on a à travailler avec des fichiers, FXDirList est aussi simple d'emploi que les composants que nous avons déjà vus. Sa déclaration le montre immédiatement :

FXDirList.new(p, nvis, tgt=nil, sel=0, opts=0, x=0, y=0, w=0, h=0)

Comme son nom l'indique il s'agit d'une liste graphique de répertoires, le deuxième paramètre indiquant le nombre d'items visibles. Si celui-ci n'est pas fixé, la liste remplira la fenêtre autant que faire se peut.

 

Encore un petit programme pour concrétiser tout cela :

 

***************************************************************************

require "fox"

include Fox

 

class DirListWindow < FXMainWindow

 

  def initialize(app)

    super(app, "Liste répertoires", nil, nil, DECOR_ALL, 0, 0, 400, 500)

 

    # Construit la barre de menu.

    menubar = FXMenubar.new(self, LAYOUT_FILL_X)

    filemenu = FXMenuPane.new(self)

    FXMenuCommand.new(filemenu, "&Quitter\tCtl-Q", nil,

      getApp(), FXApp::ID_QUIT)

    FXMenuTitle.new(menubar, "&Fichier", nil, filemenu)

    helpmenu = FXMenuPane.new(self)

    FXMenuCommand.new(helpmenu, "&A propos de FOX...").connect(SEL_COMMAND) {

      FXMessageBox.information(self, MBOX_OK, "A propos de FOX",

        "FOX est une excellente librairie en C++...\n" +

        "et FXRuby est une interface GUI très simple pour Ruby!")

    }

    FXMenuTitle.new(menubar, "&Aide", nil, helpmenu, LAYOUT_RIGHT)

 

    # Construit la liste des répertoires.

    dirlist = FXDirList.new(self, 0, nil, 0, (HSCROLLING_OFF|

      TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|FRAME_SUNKEN|FRAME_THICK|

      LAYOUT_FILL_X|LAYOUT_FILL_Y))

  end

 

  def create

    super

    show(PLACEMENT_SCREEN)

  end

end

 

def run

  application = FXApp.new("DirList", "FoxTest")

  DirListWindow.new(application)

  application.create

  application.run

end

 

run

 

***************************************************************************

 

Ce qui nous donne la fenêtre ci-dessous :

 

 

Fig 4 - Dirlist.rbw

 

Encore une fois rien de bien particulier à dire au sujet de la ligne qui nous intéresse, à savoir celle de la déclaration de FXDirList, si ce n'est sur les constantes employées dans le paramètre des options : TREELIST_SHOWS_LINES et TREELIST_SHOWS_BOXES. Elles sont déclarées dans l'ancêtre FXTreeList et imposent respectivement l'affichage des lignes pointillées de liaison et celui des petites boîtes qui contiennent le signe '+'. Soit dit en passant, on peut se demander quelle est leur utilité, étant donné que, sous Windows, ces "boîtes" sont présentes qu'il y ait ou non des sous-répertoires. Pour ceux auprès de qui ce genre de "machin" trouve grâce, il existe aussi TREELIST_ROOT_BOXES qui affiche une "boîte" à côté de la racine...

La constante HSCROLLING_OFF  désactive le défilement horizontal.

 

FXFileList

Après les répertoires - pardon, depuis Win 95 on appelle ça des dossiers et je ne suis pas encore habitué - vient l'affichage des fichiers. FXFileList est le composant chargé de cette besogne et possède la déclaration suivante :

FXFileList.new(p, tgt=nil, sel=0, opts=0, x=0, y=0, w=0, h=0)

Toujours pas de surprises dans les paramètres : le parent, la cible des messages, le sélecteur, les options et les dimensions. Rien à dire, FOX est vraiment très simple d'emploi, je ne le dirai jamais assez. Pour tester ce composant remplaçons la ligne :

 

dirlist = FXDirList.new(...

par la suivante :

 

dirlist = FXFileList.new(self,nil,0, (HSCROLLING_OFF|FILELIST_SHOWHIDDEN |FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y))

 

Par défaut c'est le répertoire racine qui est choisi et la constante FILELIST_SHOWHIDDEN permet l'affichage des fichiers cachés. Pour changer de répertoire on utilise l'attibut 'directory' :

dirlist.directory = "C:\WINDOWS"

 

Nous voyons que le composant FXFileList affiche tout ce que l'on désire savoir sur les fichiers : le nom, la taille, le type, la date et les attributs comme le montre la figure ci-dessous.

 

 

filelist.png

 

Les colonnes sont bien entendu réglables en largeur et un clic sur le fronton de l'une d'entre elle change l'ordre de tri.

A l'aide de ces deux derniers composants il est donc relativement facile de se fabriquer un "Explorateur" digne de ce nom... Alors à vos claviers !

 

 

Un peu de Ruby :

 

Les intervalles

Nous avons vu dans l'itération d'affectation  de données du petit programme sur les listes, la ligne 'for i in 0..3' ce qui signifie en langage courant : "pour i variant de la valeur 0 à la valeur 3, faire ceci..."

Il s'agit là de la forme habituelle de la déclaration d'un  intervalle, constituée de deux points. Il existe aussi une forme "3 points" excluant le dernier élément. Les intervalles peuvent être déclarés pour plusieurs types.

1..3 avec des nombres

'a'..'z' avec des lettres

et tous les types qui possèdent la méthode de comparaison '<=>'.

Les intervalles possèdent eux aussi leurs méthodes. Si l'intervalle n'est pas représenté par une variable il doit être entouré de parenthèses pour utiliser ses méthodes. En voici un échantillon :

'.begin' renvoie le premier élément de l'intervalle.

'.each' l'itérateur :(10..15).each do |n|

                         print n, ' '

                       end

                      produit 10 11 12 13 14 15

'.end' retourne le dernier élément.

'.lenght' retourne le nombre d'éléments : 

'(1..10).length'  renvoie  '10'

'(1...10).length'  renvoie  ' 9 '

 

Les variables globales prédéfinies

Nous avons vu que Ruby possède plusieurs sortes de variables parmi lesquelles figurent les variables globales, commençant par le signe '$'.  Et parmi ces variables globales figurent des variables spéciales dont le caractère '$' est suivi d'un seul autre caractère. Ces variables ont chacune une signification particulière énoncée dans le tableau suivant :

 

$.

numéro de la dernière ligne lue dans le fichier d'entrée.

$0

le nom du programme en cours.

$?

valeur de retour du dernier processus.

$:

tableau des répertoires pour 'require'.

$!

dernier objet exception.

$@

la pile d'appel de la dernière exception.

$$

l'identificateur de processus du programme.

$&

le caractère (ou la chaîne) trouvé lors d'une comparaison.

$`

la chaîne précédant le caractère trouvé lors d'une comparaison.

$'

la chaîne suivant le caractère trouvé lors d'une comparaison.

 

Il y en a beaucoup d'autres concernant :

-         les exceptions

-         les correspondances de forme

-         les entrées/sorties

-         l'environnement

 


Chapitre 9

 

Les fenêtres multiples

 

Pour obtenir des fenêtres multiples il y a un ensemble de composants interdépendants dont les deux principaux sont FXMDIChild et FXMDIClient. Les autres ne sont en fait que des descendants spécialisés de FXButton pour agrandir, mettre en icône etc… MDI signifie Multiple Documents Interface.

Nous allons examiner tout cela une fois de plus avec un exemple dans lequel nous découvrirons  aussi les fenêtres avec ascenseurs et les fontes (c'est-à-dire les polices de caractères).

 

***************************************************************************

require 'fox'

require 'fox/colors'

 

include Fox

 

RATON= <<POEME

Inventaire

 

 

Une pierre

deux maisons

trois ruines

quatre fossoyeurs

un jardin

des fleurs

un raton laveur

une douzaine d'huîtres un citron un pain

un rayon de soleil

une lame de fond

six musiciens

une porte avec son paillasson

un monsieur décoré de la légion d'honneur

un autre raton laveur...

 

-Jacques Prévert

POEME

 

class MDITestWindow  < FXMainWindow

 

  def initialize(app)

    super(app, "Composant Multi Fenêtre ", nil, nil, DECOR_ALL, 0, 0, 340, 200)

 

    # Création de la fonte.

    @font = FXFont.new(getApp(), "courier", 15, FONTWEIGHT_BOLD)

 

    menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)

    statusbar =FXStatusbar.new(self,

      LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER)

    statusbar.statusline.normalText = "Prêt" 

 

    # Client MDI.

    @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)

 

    # Icône pour les fenêtres enfants.

    @mdiicon = FXPNGIcon.new(getApp(), File.open("penguin.png", "rb").read)

 

    # Création du menu MDI.

    @mdimenu = FXMDIMenu.new(self, @mdiclient)

 

    # Les boutons MDI du menu - notez les messages 'ID'.

    # Normalement les commandes MDI sont soit activées soit désactivées.

    # Sous la barre de menu, cependant,il y en a de cachées si le client MDI

    # n'est pas maximisé. Pour gérer tout cela des ID différents sont utilisés :

    # Il s'agit des icônes des fenêtres Windows, avec dans l'ordre de

    # déclaration :

    # - L'icône située à gauche et contenant les sous-menus de déplacement et

    # fermeture.

    # - Les icônes situées à droite des fenêtres à savoir celles de

    # fermeture, de restauration et de mise en icône.

 

    FXMDIWindowButton.new(menubar, @mdiclient, FXMDIClient::ID_MDI_MENUWINDOW,

      LAYOUT_LEFT)

    FXMDIDeleteButton.new(menubar, @mdiclient, FXMDIClient::ID_MDI_MENUCLOSE,

      FRAME_RAISED|LAYOUT_RIGHT)

    FXMDIRestoreButton.new(menubar, @mdiclient, FXMDIClient::ID_MDI_MENURESTORE,

      FRAME_RAISED|LAYOUT_RIGHT)

    FXMDIMinimizeButton.new(menubar, @mdiclient,

      FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)

 

    # Création de quelques fenêtres de test pour le démarrage.

    mdichild = createTestWindow(10, 10, 200, 150)

    @mdiclient.setActiveChild(mdichild)

    createTestWindow(20, 20, 200, 150)

    createTestWindow(30, 30, 200, 150)

 

    # Menu fichier.

    filemenu = FXMenuPane.new(self)

    newCmd = FXMenuCommand.new(filemenu, "&Nouveau\tCtl-N\tCréation d'un nouveau document.")

    newCmd.connect(SEL_COMMAND, method(:onCmdNew))

    FXMenuCommand.new(filemenu, "&Quitter\tCtl-Q\tQuitter l'application.", nil,

      getApp(), FXApp::ID_QUIT, 0)

    FXMenuTitle.new(menubar, "&Fichier", nil, filemenu)

     

    # Menu des fenêtres.

    windowmenu = FXMenuPane.new(self)

    FXMenuCommand.new(windowmenu, "&Horizontales", nil,

      @mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)

    FXMenuCommand.new(windowmenu, "&Verticales", nil,

      @mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)

    FXMenuCommand.new(windowmenu, "&Cascade", nil,

      @mdiclient, FXMDIClient::ID_MDI_CASCADE)

    FXMenuCommand.new(windowmenu, "&Fermer", nil,

      @mdiclient, FXMDIClient::ID_MDI_CLOSE)

    FXMenuCommand.new(windowmenu, "&Tout fermer", nil,

      @mdiclient, FXMDIClient::ID_CLOSE_ALL_DOCUMENTS)

    sep1 = FXMenuSeparator.new(windowmenu)

    sep1.setTarget(@mdiclient)

    sep1.setSelector(FXMDIClient::ID_MDI_ANY)

    FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)

    FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)

    FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)

    FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)

    FXMenuTitle.new(menubar,"Fe&nêtres", nil, windowmenu)

   

    # Menu d'aide.

    helpmenu = FXMenuPane.new(self)

    FXMenuCommand.new(helpmenu, "&Au sujet de FOX...").connect(SEL_COMMAND) {

      FXMessageBox.information(self, MBOX_OK, "Au sujet du test multi fenêtres",

        "Test du composant MDI (Multi Document Interface)\nEcrit par Jeroen van der Zijp")

    }

    FXMenuTitle.new(menubar, "&Aide", nil, helpmenu, LAYOUT_RIGHT)

  end

 

  # Méthode de création d'une nouvelle fenêtre enfant.

  def createTestWindow(x, y, w, h)

    mdichild = FXMDIChild.new(@mdiclient, "Fille", @mdiicon, @mdimenu,

      0, x, y, w, h)

    scrollwindow = FXScrollWindow.new(mdichild, 0)

    scrollwindow.verticalScrollbar.setLine(@font.fontHeight)

    btn = FXButton.new(scrollwindow, RATON, nil, nil, 0,

      LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 250, 200)

    btn.font = @font

    btn.backColor = FXColor::White

    mdichild

  end

 

  # Appel pour une nouvelle fenêtre.

  def onCmdNew(sender, sel, ptr)

    mdichild = createTestWindow(40, 40, 200, 150)

    mdichild.create

    return 1

  end

 

  # Démarrage.

  def create

    super

    @font.create

    show(PLACEMENT_SCREEN)

  end

end

 

if __FILE__ == $0

  application = FXApp.new("MDIApp", "FoxTest")

  MDITestWindow.new(application)

  application.create

  application.run

end

 

***************************************************************************

 

new(p, opts=0, x=0, y=0, w=0, h=0) Allons-y pour les remarques, dans l'ordre de déroulement du programme :

·        Appel du module 'colors' à cause de la couleur du fond, dans la méthode de création des fenêtres : FXColor::White

·        Insertion d'un texte complet en utilisant un délimiteur de chaîne. Voir "Un peu de Ruby" plus loin dans ce chapitre.

·        Nouveauté avec l'apparition des fontes, dont la déclaration est la suivante :

       new(app, face, size, weight=FONTWEIGHT_NORMAL, slant=FONTSLANT_REGULAR,

       encoding=FONTENCODING_DEFAULT, setWidth=FONTSETWIDTH_DONTCARE,

       hints=0)

       'app' représente le parent, 'face' une chaîne décrivant la fonte, 'size' un entier représentant la 

       taille de la fonte et le reste des paramètres des options particulières aux polices de caractères

       et dont la description se trouve en AppendiceA.

·        La déclaration de la barre de menu et de la barre de statut comprenant la traduction du message par défaut.

·        Apparition de FXMDIClient qui agit en quelque sorte comme les aménageurs d'espace vus depuis le début, en s'occupant de tout. FXMDIClient reçoit les messages en premier avant de les répercuter sur les fenêtres enfants. C'est ce composant qui gère aussi l'espace occupé par les composants enfants. Sa déclaration ne suscite aucun commentaire tant elle est habituelle aux composants de Fox :

 

      Les paramètres se limitent au parent et aux options classiques.

·        Chargement d'une icône. Du déjà vu...

·        Création d'un menu MDI. Il s'agit de l'icône située en haut à gauche des fenêtres Windows, et qui lors d'un clic font apparaître un menu contenant les commandes de changement de focus des fenêtres (Next =suivante, Previous = précédente) et de minimisation, maximisation et fermeture.

·        Création des différents boutons de gestion des fenêtres enfants. Remarquez que ce sont des boutons spécialisés, chacun ayant une fonction bien définie. Leur cible, pour les messages, est, comme attendu , le gestionnaire  '@mdiclient' .

·        Création de 3 fenêtres enfants par appel de la méthode 'createTestWindow( )' que nous découvrirons plus loin. Appel de la méthode 'setActiveChild( )' afin de rendre la première fenêtre enfant active.

·        Création classique des menus de la barre de menus où l'on révise les différents modes de passage de commandes : soit par appel d'une méthode créatrice de nouvelles fenêtres ('onCmdNew'), soit par appel direct des constantes de la classe ('FXMDIClient'), soit enfin par la méthode 'connect' (pour le menu d'aide). A noter le passage des commandes d'arrangement des fenêtres (horiz., vert., cascade...)  par appel aux constantes de la classe FXMDIClient.

·        La méthode de création d'une nouvelle fenêtre reçoit en paramètres les dimensions de la fenêtre à créer. Nous sommes obligés de tout définir dans chaque fenêtre puisqu'il s'agit ici de fenêtres MDI qui ont un comportement inhabituel régi par un composant FXMDIClient. Une variable locale ('mdichild')  sert à instancier un composant FXMDIChild dont la définition est la suivante :

new(p, name, ic=nil, mn=nil, opts=0, x=0, y=0, w=0, h=0)

      'p' désignant le propriétaire, 'name' une chaîne pour le nom de la fenêtre, 'ic' un pointeur vers

      une icône, 'mn' un pointeur vers un menu MDI, 'opts' un entier pour les options.

      Cette fenêtre va être incorporée dans un composant FXScrollWindow qui est un ascenseur

      contenant  un autre objet plus grand qu'il permet de déplacer dans son environnement.

     Ce composant est un descendant de FXScrollArea qui  possède l'attribut 'verticalScrollbar'

      pointant vers une barre d'ascenseur FXScrollBar dont on fixe l'incrément de la hauteur de

      ligne par la méthode 'setLine'. La définition de  FXScrollWindow est la suivante :

new(p,opts=0, x=0, y=0, w=0, h=0)

      Ensuite c'est tout simplement un bouton qui joue le rôle d'afficheur de texte. Pas besoin de

      sortir l'artillerie lourde pour un texte qui ne sera pas modifié, du moins dans cet exemple…  

    On déclare une nouvelle fonte pour l'affichage et on fixe la couleur du fond. Pour finir on

        passe la variable 'mdichild' en valeur de retour de la méthode 'createTestWindow( )'.

        Rappelez-vous, 'createTestWindow( )' a été appelée par une variable, aussi nommée

        'mdichild' mais locale elle, à la méthode 'initialize'.

·        La méthode 'onCmdNew' de création de nouvelles fenêtres est le point de chute de la commande 'Nouveau' du bouton 'Fichier' de la barre de  menu. Encore une variable locale 'mdichild' qui sert à la création de fenêtres par appel à la méthode 'createTestWindow'. Comme la fenêtre est en fait créée dans cette méthode, 'onCmdNew' n'a pas de valeur, représentant une fenêtre, à retourner et renvoie donc la valeur '1' pour dire que tout s'est bien passé.

·        Dans la méthode 'create' nous créons véritablement la fonte déclarée en début de méthode 'initialize'. Comme dans d'autres ressources la construction d'une fonte passe par deux étapes: la construction -la partie côté-client- par la méthode 'new', suivie par la création par le système - côté-serveur- par la méthode 'create'. Dans la plupart des cas les fontes sont créées lors de la création des composants qui les utilisent, mais en cas de création plus tardive de fonte, un appel à la méthode 'create' permet d'affirmer que l'objet fonte est complètement initialisé.

 

 

 

mditest.png

 

 

Un peu de Ruby :

 

Les délimiteurs

Le texte du poème est entouré par les signes '<< POEME' et 'POEME', ceci sert à délimiter des textes sur plusieurs lignes.  Les deux signes '<<' doivent précéder un nom en majuscule et le même nom servira de signal de fin de bloc et devra être au début d'une ligne. Pour obtenir une indentation, faire suivre '<<' par un tiret : '<<-', la fin du bloc pourra alors être décalée sur la ligne. Si le nom attribué est mit entre guillemets : "POEME", les règles définies plus haut pour les chaînes de caractères sont appliquées.

 

Les expressions rationnelles

Le meilleur pour la fin. Les expressions rationnelles ("Regular expressions" en anglais, mais l'auteur préfère l'autre dénomination !) permettent de faire une foule de choses dans les chaînes de caractères en définissant des formats au moyen de caractères et de combinaison de caractères. La première fois qu'on aborde ce système on a l'impression  d'entrer dans le monde du sorcier Gargamel manipulant un grimoire rempli de formules mystérieuses. Mais l'enjeu en vaut la chandelle, car les expressions rationnelles permettent des raccourcis de programmation spectaculaires. Nous ne ferons que survoler le sujet tant il est vaste, au point qu'un ouvrage entier lui a été consacré : "Maîtrise des expressions régulières" de Jeffrey E.F. Friedl aux éditions O'Reilly. Les expressions rationnelles sont entourées par le caractère '/'. Tous les caractères correspondent  à eux-mêmes à l'exception de ., |, (, ), [, \, ^, {, +, $, *, et ? . Pour utiliser un de ces caractères il faut le faire précéder d'un  '\'.

 

 

 

 

 

Le tableau ci-dessous indique la signification de quelques caractères ou combinaisons :

 

[]

spécifie un intervalle : [a-z] représente tout l'alphabet en minuscule.

\d

spécifie un chiffre

\s

spécifie un espace

\w

spécifie une lettre ou un chiffre

*

spécifie zéro ou plusieurs occurence(s) de ce qui précède

+

spécifie une ou plusieurs occurence(s) de ce qui précède

{m,n}

spécifie au moins m occurences et au plus n, de ce qui précède

?

spécifie au plus une occurence de ce qui précède

|

spécifie soit ce qui précède soit ce qui suit

^

indique le début d'une ligne

$

indique la fin d'une ligne

\A

indique le début d'une chaîne

\z

indique la fin d'une chaîne

( )

groupe

 

 

Voici quelques exemples extraits de"Programming Ruby" :

Supposons que nous voulions savoir si une chaîne contient les mots "Perl" ou "Python", l'expression s'écrira :                                  /Perl|Python/

ou bien en utilisant des parenthèses :         /P(erl|ython)/

    /Ruby (Perl|Python)/   correspond à : "Ruby", un espace, et soit "Perl" soit "Python"

  /\d\d:\d\d:\d\d/   peut repésenter une heure  :  12:34:56

Les expression rationnelles servent aussi à remplacer des chaînes par d'autres :

   line.sub(/Perl/, 'Ruby')   remplace la première occurence de  'Perl' par 'Ruby'

   line.gsub(/Python/, 'Ruby')   remplace toutes les occurences de 'Python' par 'Ruby'

 

Signalons aussi l'opérateur '=~' qui peut être utilisé pour comparer une chaîne à une expression rationnelle, et qui retourne la position de départ de la première occurence trouvée ou 'nil' si aucune :

a = "Fats Waller"

a =~ /a/                  retourne 1

a =~ /z/                  retourne nil

a =~ "ll"                 retourne 7

 

Un exemple plus compliqué mettant en oeuvre une méthode et utilisant des variables globales prédéfinies:

 

def showRE(a,re)

   if a =~ re

          "#{$`}<<#{$&}>>#{$'}"

      else

           "pas de correspondance"

   end

end

 

showRE('very interesting', /t/)             retourne  --->   very in<<t>>eresting

showRE('Fats Waller', /ll/)                        retourne --->   Fats Wa<<ll>>er

 

Un dernier exemple pour la route. Supposons que nous voulions un chiffre en hexadécimal entouré par '<' et '>'  :  /<0[Xx][\dA-Fa-f]+>/


Annexe A

 

Pour chaque composant visuel il est décrit les déclarations de constantes, d'attributs, de méthodes, d' événements. Ces descriptions sont celles élaborées par Lyle Johnson et s'adressent particulèrement à FXRuby; même s'il n'y a que peu de différences avec celles de FOX, il y en a tout de même.

Pour les attributs les mentions entre crochets [RW] signifient respectivement en lecture pour 'R' et en écriture pour 'W'.

 

 

Fox

C'est le module de base, qui contient la liste de toutes les classes et en plus des méthodes générales publiques de classes.

 

Les méthodes :

MKUINT(lo, hi) : transforme deux entiers courts (16 bits) non signés en un entier long (32 bits) non signé. 'lo' représente la partie de poids faible et 'hi' la partie de poids fort de l'entier long.

SELTYPE(sel) : retourne le type de message pour le sélecteur 'sel'.

SELID(sel) : retourne l'identifiant de message du sélecteur 'sel'.

FXRGB(r, g, b) : construit une couleur à partir des composantes rouge, verte et bleue.

FXRGBA(r, g, b, a) : idem plus la composante 'alpha' de transparence.

FXREDVAL(color) : retourne la valeur du rouge  de la couleur 'color'.

FXGREENVAL(color) : retourne la valeur du vert  de la couleur 'color'

FXBLUEVAL(color) : retourne la valeur du bleu de la couleur 'color'

FXALPHAVAL(color) : retourne la valeur de transparence de la couleur 'color'

FXRGBACOMPVAL(color, component) : retourne la valeur de la composante 'component' de la couleur 'color', où 'component' peut prendre la valeur 0, 1, 2 ou 3.

fxparseaccel(s) : cherche un code d'accélération dans une chaîne 's'.

fxparsehotkey(s) : cherche un code de raccourci dans une chaîne 's'.

fxfindhotkeyoffset(s) : localise le code de raccourci à partir du début de la chaîne 's'.

makeHiliteColor(clr) : retourne la couleur de surbrillance.

makeShadowColor(clr)  : retourne la couleur de l'ombre.

fxcolorfromname(colorName)  : retourne la valeur RGB de la couleur 'colorName'

fxnamefromcolor(color) : retourne le nom de la couleur la plus proche de la valeur RGB.

fxrgb_to_hsv(r, g, b) : converti les valeurs RGB en HSV.

fxhsv_to_rgb(h, s, v) : converti les valeurs HSV en RGB.

fxversion() : retourne le numéro de version de FOX sous forme de chaîne (par ex. "1.0.34").

fxTraceLevel() : niveau de trace des contrôles.

fxloadICO(store) : charge un fichierICO depuis une instance 'store' de FXStream. En cas de succés retourne un tableau dont les éléments sont les données de l'image (une chaîne), la couleur de transparence, la largeur et la hauteur de l'icône. Si l'opération n'abouti pas, la méthode retourne 'nil'.

fxsaveICO(store, pixels, transp, width, height) : sauve un fichier 'ICO' dans un flux FXStream. Retourne 'true' en cas de succés et 'false' sinon.

fxloadJPG(store) : charge une image JPEG d'un flux 'store'. En cas de succés retourne un tableau dont les éléments sont les données de l'image (une chaîne), la couleur de transparence, la largeur et la hauteur de l'icône et la qualité de l'image. Si l'opération n'abouti pas, la méthode retourne 'nil'.

fxsaveJPG(store, data, transp, width, height, quality) : sauve une image JPEG dans un flux 'store', avec les données 'data', la couleur 'transp' de transparence, la largeur 'width', la hauteur 'height', et le degré de compression 'quality'. Retourne 'true' en cas de succés, 'false' sinon.

fxloadPCX(store) : charge une image PCX d'un flux 'store'.Retourne, en cas de succés,  un tableau contenant les données des pixels, la couleur de transparence, la largeur et la hauteur de l'image. Retourne 'nil' en cas d'échec.

fxsavePCX(store, pixels, transp, width, height) : sauve une image PCX dans un flux 'store', avec les données 'data', la couleur 'transp' de transparence, la largeur 'width', la hauteur 'height'. Retourne 'true' en cas de succés, 'false' sinon

fxdecodeColorData(data) : les données utilisées pour les couleurs sont une séquence d'entiers courts, en ordre d'octets natifs. Ici, nous utilisons la directive 'S' pour String#unpack (qui traite deux caractères successifs comme un entier court non signé dans l'ordre natif) pour décoder les valeurs RGB et A.

fxencodeColorData(rgba) : l'inverse de la méthode ci-dessus.

fxrubyversion()  : retourne le numéro de version de FXRuby sous forme de chaîne, par ex; "1.0.19""

 

Méthodes d'instances publiques.

fxloadPNG(store) : charge une image PNG d'un flux 'store'.Retourne, en cas de succés,  un tableau contenant les données des pixels, la couleur de transparence, la largeur et la hauteur de l'image. Retourne 'nil' en cas d'échec.

fxsavePNG(store, pix, transp, width, height) : sauve une image PCX dans un flux 'store', avec les données 'data', la couleur 'transp' de transparence, la largeur 'width', la hauteur 'height'. Retourne 'true' en cas de succés, 'false' sinon.

 

 

FXAccelTable

La table d'accélération envoit un message à un objet cible spécifique, quand la touche indiquée,  ou la combinaison de touches, est frappée.

 

Ancêtre : FXObject.

 

Déclaration :

FXAccelTable.new() {|acceleratorTable| ...}

Construit une table vide.

 

 

addAccel(hotKey, target=nil, seldn=0, selup=0) : ajoute un accélérateur à la table.

  Parametres :

     hotKey : la touche associée à cet accélérateur [Integer]

     tgt : la cible du message [FXObject]

     seldn : sélecteur pour l'événement SEL_KEYPRESS [Integer]

     selup : sélecteur pour l'événement SEL_KEYRELEASE [Integer]

hasAccel?(hotKey) : retourne 'true' si 'hotKey' est  l'accélérateur spécifié.

targetOfAccel(hotKey) : retourne l'objet cible de l'accélérateur 'hotKey'.

removeAccel(hotKey)  : supprime l'accélération de 'hotKey'.

 

 

FXApp

L'objet application.

 

Ancêtre : FXObject.

 

Déclaration :

FXApp.new(appName="Application", vendorName="FoxDefault") {|theApp| ...}

 

Les constantes prédéfinies :

Ces constantes représentent les différentes formes de curseur utilisés par les applications FOX et peuvent être utilisées en tant qu'argument 'which' des méthodes 'getDefaultCursor' et  'setDefaultCursor'.

DEF_ARROW_CURSOR : curseur flèche.

DEF_RARROW_CURSOR : curseur flèche inversée.

DEF_TEXT_CURSOR : curseur texte.

DEF_HSPLIT_CURSOR : curseur de fractionnement horizontal.

DEF_VSPLIT_CURSOR: curseur de fractionnement vertical.

DEF_XSPLIT_CURSOR : curseur de fractionnement en croix.

DEF_SWATCH_CURSOR : curseur de montre en couleur.

DEF_MOVE_CURSOR : curseur de déplacement.

DEF_DRAGH_CURSOR : curseur de redimensionnement horizontal de bordure .

DEF_DRAGV_CURSOR : curseur de redimensionnement vertical de bordure .

DEF_DRAGTL_CURSOR : curseur de redimensionnement coin supérieur gauche .

DEF_DRAGBR_CURSOR : curseur de redimensionnement coin inférieur droit .

DEF_DRAGTR_CURSOR : curseur de redimensionnement coin supérieur droit .

DEF_DRAGBL_CURSOR : curseur de redimensionnement coin inférieur gauche .

DEF_DNDSTOP_CURSOR : curseur de stop de tirer-lacher (Drag and drop)

DEF_DNDCOPY_CURSOR : curseur de copie de tirer-lacher (Drag and drop)

DEF_DNDMOVE_CURSOR : curseur de déplacement de tirer-lacher (Drag and drop)

DEF_DNDLINK_CURSOR : curseur de liaison de tirer-lacher (Drag and drop)

DEF_CROSSHAIR_CURSOR : curseur croix fine.

DEF_CORNERNE_CURSOR : curseur nord-est.

DEF_CORNERNW_CURSOR : curseur nord-ouest

DEF_CORNERSE_CURSOR : curseur sud-est.

DEF_CORNERSW_CURSOR : curseur sud-ouest.

DEF_ROTATE_CURSOR : curseur de rotation.

 

Modes pour la méthode  addInput()

INPUT_NONE : inactif.

INPUT_READ : fd en lecture.

INPUT_WRITE : fd en écriture.

INPUT_EXCEPT : fd en except.

 

Pour la modalité :

MODAL_FOR_NONE : boucle d'événement non modal (diffusion normale).

MODAL_FOR_WINDOW : dialogue modal (bip si à l'extérieur du dialogue)

MODAL_FOR_POPUP : modal pour les "popup" (toujours diffusés vers les "popup")

 

Identifiants de message

ID_QUIT : termine l'application normalement.

ID_DUMP : vide l'arbre des composants.

 

 

Les attributs :

 

animSpeed [RW] : vitesse d'animation en millisecondes [Integer]

appName [R] : nom de l'application [String]

backColor [RW] : couleur du fond par défaut [FXColor]

baseColor [RW] : couleur du fond des contrôles GUI [FXColor]

blinkSpeed [RW] : vitesse de clignotement en millisecondes [Integer]

borderColor [RW]  : couleur du bord [FXColor]

clickSpeed [RW] : vitesse du clic en millisecondes [Integer]

cursorWindow [R] : la fenêtre sous le curseur, si elle existe [FXWindow]

defaultVisual [RW] : aspect par défaut [FXVisual]

display [R] : affichage [Integer]

dragDelta [RW] : delta de dragage, en pixels [Integer]

focusWindow [R] : la fenêtre au sommet de la chaîne des focus, si elle existe [FXWindow]

foreColor [RW] : couleur d'avant plan par défaut [FXColor]

hiliteColor [RW] : couleur de surbrillance des contrôles GUI [FXColor]

mainWindow [R] : la fenêtre principale, si elle existe [FXWindow]

menuPause [RW] : pause des menus en millisecondes [Integer]

modalModality [R]  : mode de la boucle modale courante [Integer]

modalWindow [R] : la fenêtre de la boucle modale courante [FXWindow]

monoVisual [R] :  aspect monochrome [FXVisual]

normalFont [RW] : fonte par défaut. [FXFont]

reg [R] : registre pour cette application [FXRegistry]

root [R] : fenêtre racine [FXRootWindow]

scrollDelay [RW] : délai de défilement en millisecondes [Integer]

scrollSpeed [RW]  : vitesse de défilement en millisecondes [Integer]

selbackColor [RW]  : couleur de fond pour les composants sélectionnés [FXColor]

selforeColor [RW] : couleur d'avant plan pour les composants sélectionnés [FXColor]

shadowColor [RW] : couleur des ombres pour les contrôles GUI [FXColor]

sleepTime [RW] : temp (en milliseconde) avant prise de contrôle par l'ordonnanceur de processus de Ruby [Integer]

tipbackColor [RW] : couleur de fond par défaut des info-bulles [FXColor]

tipforeColor [RW] : couleur d'avant plan par défaut des info-bulles [FXColor]

tooltipPause [RW]  : temps de pause des info-bulles en millisecondes [Integer]

tooltipTime [RW] : temps d'affichage des info-bulles en millisecondes [Integer]

typingSpeed [RW] : vitesse de frappe utilisée pour les composants FXIconList, FXList et FXTreeList en millisecondes. La valeur par défaut est 1000.

vendorName [R] : nom du vendeur [String]

waitCursor [RW] : curseur sablier [FXCursor]

wheelLines [RW] : nombre de lignes de défilement [Integer]

 

Les méthodes :

copyright() : méthode interne, renvoie le copyright de la librairie.

instance() : retourne l'instance de l'application.

openDisplay(dpyname = ":0") : appellé par 'init', ouvre une connection pour l'affichage.

closeDisplay() :  ferme la connection avec l'affichage.

addTimeout(ms, tgt, sel) : ajoute un message de pause à envoyer à la cible en millisecondes; l'horloge démarrre une fois l'intervalle de temps expirée.

removeTimeout(timer) : supprime le message 'timer', retourne 'nil'. 

addChore(tgt, sel) : ajoute un message de temps mort à envoyer à la cible quand le système n'a plus d'événement à traiter.

removeChore(chore) : supprime le message de temps mort.

addSignal(sig, tgt, sel, immediate=false, flags=0) : ajoute un message de processus à envoyer à l'objet cible quand le signal 'sig' est atteint, les drapeaux sont à mettre comme des définitions POSIX. Quand 'immediate' est à 'true', le message est envoyé à la cible immédiatement, ce qui doit être utilisé avec beaucoup de précautions si l'application risque d'être interrompue à une position inconnue dans son exécution.

removeSignal(sig) : supprime le message pour le signal 'sig'.

addInput(fd, mode, tgt, sel) : ajoute un descripteur de fichier de surveillance déterminé par 'mode', où 'mode' est une combinaison logique "OU"  de INPUT_READ,

INPUT_WRITE et INPUT_EXCEPT. Un message de type SEL_IO_READ, SEL_IO_WRITE, ou SEL_IO_EXCEPT sera envoyé à la cible quand l'activité spécifiée est détectée dans le descripteur de fichier.

removeInput(fd, mode) : supprime le message d'entrée et l'objet cible pour le descripteur de fichier spécifié ('fd') et le mode ('mode'), qui est une combianison logique "OU" de  (INPUT_READ,INPUT_WRITE, INPUT_EXCEPT).

create() : crée la fenêtre de l'application.

destroy() : détruit la fenêtre de l'application.

detach() : détache les fenêtres de l'application.

peekEvent() : teste l'existence d'un événement.

runOneEvent() : exécute une diffusion d'événement.

run() : éxecute la boucle événement de l'application principale jusqu'à l' appel de 'stop', et retourne le code de sortie passé comme argument à 'stop'.

runUntil(condition) : exécute une boucle événementielle jusqu'à ce qu'un drapeau devienne non nul.

runWhileEvents(window=nil) : déclenche une boucle événementielle tant qu'il y a des événements dans la file d'attente. Retourne '1' si la file est vide, et '0' quand la boucle est terminée. Excepté pour la fenêtre modale et ses enfants, toutes les entrées vers les autres fenêtres sont bloquées; si la fenêtre modale est à 'nil' toutes les entrées sont bloquées.

runModal() : déclenche la boucle d'événement modal, bloquant le clavier et les événements souris jusqu'à ce que 'stopModal' soit appelé.

runModalFor(window) : déclenche une boucle événementielle pour la fenêtre 'window' donnée, jusqu'à ce que 'stop' ou 'stopModal' soit appelé. Excepté pour la fenêtre modale et ses enfants, toutes les entrées vers les autres fenêtres sont bloquées; si la fenêtre modale est à 'nil' toutes les entrées sont bloquées.

runModalWhileShown(window) : déclenche une boucle événementielle pour la fenêtre 'window' donnée tant que la fenêtre est visible ou jusqu'à ce que 'stop' ou 'stopModal' soit appelé. Excepté pour la fenêtre modale et ses enfants, toutes les entrées vers les autres fenêtres sont bloquées; si la fenêtre modale est à 'nil' toutes les entrées sont bloquées.

runPopup(window) : active un menu furtif tant qu'il est visible ou jusqu'à ce que 'stop' ou 'stopModal' soit appelé.

modal?(window) : retourne 'true' si la fenêtre est modale.

stop(value=0) : termine la boucle d'événement la plus externe, et toutes les modales internes; les boucles d'événements les plus profondes sont terminées avec un code égal à '0', tandis que la plus externe retourne un code égal à 'value'.  

stopModal(window, value=0) : arrête la boucle modale en retournant un code égal à 'value'. Toutes les boucles interne sont terminées avec un code égal à '0'. 

stopModal2(value=0) : arrête la boucle d'événements la plus profonde et retourne un code égal à 'value'.

forceRefresh() : force le rafraichissement GUI.

refresh() : ordonnance un rafraîchissement.

flush(sync=false) : termine las actions de dessins en attente.

repaint() : peint toutes les fenêtres à redessiner. Au retour toutes les fenêtres de l'application sont repeintes.

init(argv, connect=true) : initialise l'application. Parcourt et relève les arguments de la ligne de commande, lit le registre. Finallement, si 'connect' est à 'true', ouvre l'affichage.

exit(code=0) : quitte l'application. Ferme l'affichage et écrit les registres.

registerDragType(name) : enregistre un nouveau type de dragage-largage.

getDragTypeName(type) :retourne le nom du type de dragage.

beep()  : émet un bip sonore.

endWaitCursor() : termine le bloc le plus profondément imbriqué du curseur d'attente. Voir 'beginWaitCursor'.

getDefaultCursor(which) : envoit un curseur par défaut.

setDefaultCursor(which, cursor) : change le curseur par défaut.

dumpWidgets() : information sur les composants.

enableThreads() : active le support des applications multitaches.

disableThreads() : désactive le support d'application multi-taches.

threadsEnabled?() : teste si les applications multi-taches sont supportées.

beginWaitCursor() {|| ...}  change le curseur par défaut en un sablier, pour indiquer une attente. Pour revenir à l'ancien curseur appeler la méthode 'endWaitCursor'. Par ex;

getApp().beginWaitCursor()

... opération  ...

getApp().endWaitCursor()

Des appels à 'beginWaitCursor' peuvent être imbriqués, et dans ce cas, un appel à 'endWaitCursor' se rapporte au plus récent appel de 'beginWaitCursor'.

Si un bloc de code est présent, 'endWaitCursor' est automatiquement appelé à la fin du bloc, par ex :

getApp().beginWaitCursor() {

  ... operation ...

                        ... endWaitCursor() est automatiquement appellé ...

}

 

Les événements :

L'objet FXApp ne possède pas lui même une cible de message comme les autres objets FOX, mais il peut envoyer tout de même des messages aux objets pour quelques événements spéciaux.

Timers : quand un événement d'attente est enregistré par la méthode 'addTimeout', un message SEL_TIMEOUT est envoyé à la cible.

Chores : quand un événement de routine est enregistré par la méthode 'addChore', un message

SEL_CHORE est envoyé à la cible.

Inputs : quand un événement d'entrée est enregistré par la méthode 'addInput' un message

SEL_IO_READ, SEL_IO_WRITE ou  SEL_IO_EXCEPT peut être envoyé à la cible.

Signals : quand un objet signalétique est enregistré par la méthode 'addSignal', un message

 SEL_SIGNAL peut être envoyé à la cible.

 

 

 

FXArrowButton

Bouton muni d'une flèche pointant dans une direction donnée.

 

Ancêtre : FXFrame.

 

Déclaration :

FXArrowButton.new(parent, target=nil, selector=0, opts=ARROW_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD) {|theArrowButton| ...}

 

Les constantes prédéfinies :

D'aspect :

ARROW_NONE : pas de flèche.

ARROW_UP : flèche vers le haut.

ARROW_DOWN : flèche vers le bas.

ARROW_LEFT : flèche vers la gauche.

ARROW_RIGHT : flèche vers la droite.

ARROW_REPEAT : répétition si le bouton reste enfoncé.

ARROW_AUTOGRAY : automatiquement grisé si non mis à jour.

ARROW_AUTOHIDE : automatiquement caché si non mis à jour.

ARROW_TOOLBAR : bouton de style barre d'outils.

ARROW_NORMAL : identique à FRAME_RAISED|FRAME_THICK|ARROW_UP

Message

 ID_REPEAT : identifiant de message utilisé en interne par le 'timer' qui  gère l'autorépétition (activé par l'option ARROW_REPEAT).

 

Les attributs :

arrowColor [RW] : couleur de remplissage pour la flèche [FXColor]

arrowSize [RW] : dimension par défaut de la flèche, en pixels [Integer]

arrowStyle [RW]  : style de la flèche.

helpText [RW] : texte d'aide de la ligne de statut pour le bouton [String]

justify [RW]  : mode de justification.

state [RW] : état du bouton, où 'true' signifie que le bouton est pressé [Boolean]

tipText [RW] : message de l' info-bulle d'aide pour le bouton [String]

 

Les événements :

  Les messages suivants sont envoyés à ses cibles par FXArrowButton.

 

SEL_COMMAND : envoyé lors d'un clic sur le bouton.

SEL_KEYPRESS : envoyé lors de l'appui d'une touche

SEL_KEYRELEASE : envoyé lors du relâchement d'une touche

SEL_LEFTBUTTONPRESS : envoyé lors de l'appui de la touche gauche de la souris

SEL_LEFTBUTTONRELEASE : envoyé lors de l'appui de la touche droite de la souris

SEL_COMMAND : envoyé quand le bouton est cliqué (ou répétitivement lorsque le bouton est maintenu enfoncé, si l'option ARROW_REPEAT est en fonction)

Les messages sont des instances de FXEvent.

 

 

FXButton

Un FXButton produit un bouton poussoir, avec une icône 'icon' optionnelle et /ou l'étiquette 'text'.

 

Ancêtre : FXLabel.

 

Déclaration :

FXButton.new(parent, text, icon=nil, target=nil, selector=0, opts=BUTTON_NORMAL,

x=0, y=0, width=0, height=0,padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD)

 {|theButton| ...}

 

Les constantes prédéfinies :

 Pour l'apparence :

BUTTON_AUTOGRAY = 0x00800000,  automatiquement grisé si non mis à jour.

BUTTON_AUTOHIDE  = 0x01000000,  automatiquement caché si non mis à jour.

BUTTON_TOOLBAR   = 0x02000000,  bouton style  barre d'outils (sans cadre)

BUTTON_DEFAULT    = 0x04000000,  peut devenir le bouton initial en recevant le focus.

BUTTON_INITIAL      = 0x08000000,  bouton initial par défaut.

BUTTON_NORMAL     =(FRAME_RAISED|FRAME_THICK|JUSTIFY_NORMAL|

                                        ICON_BEFORE_TEXT)

Pour l'état :

STATE_UP                  = 0, Bouton relevé

STATE_DOWN           = 1,  Bouton enfoncé

STATE_ENGAGED   = 2,   Bouton occupé

STATE_UNCHECKED = STATE_UP,  Identique à  STATE_UP (utilisé pour les cases à cocher et les Boutons-radio)

STATE_CHECKED   = STATE_ENGAGED , identique à  STATE_ENGAGED (utilisé pour les cases à cocher et les boutons-radio)

 

Les attributs :

buttonStyle  [RW]  :  définit le style du bouton  ( Combinaisons de BUTTON_AUTOGRAY,

                                                                                 BUTTON_AUTOHIDE, etc.) [Integer]

state [RW] : état du bouton (Combinaisons de STATE_UP, STATE_DOWN, etc.) [Integer]

 

Les événements :

  Les messages suivants sont envoyés à la cible par FXCheckButton.

 

SEL_COMMAND : envoyé lors d'un clic sur le bouton.

SEL_KEYPRESS : envoyé lors de l'appui d'une touche

SEL_KEYRELEASE : envoyé lors du relâchement d'une touche

SEL_LEFTBUTTONPRESS : envoyé lors de l'appui de la touche gauche de la souris

SEL_LEFTBUTTONRELEASE : envoyé lors de l'appui de la touche droite de la souris

 

Les messages sont des instances de FXEvent

 

FXCanvas

 

Ancêtre : FXWindow.

 

Déclaration :

FXCanvas.new(parent, target=nil, selector=0, opts=FRAME_NORMAL, x=0, y=0, width=0, height=0) {|theCanvas| ...}

 

Les événements :

SEL_KEYPRESS : envoyé lors de l'appui d'une touche

SEL_KEYRELEASE : envoyé lors du relâchement d'une touche.

SEL_MOTION : envoyé quand la souris bouge.

SEL_PAINT : envoyé lorsque le canevas doit être redessiné.

 

Les messages sont des instances de FXEvent

 

 

FXCheckButton

Un composant FXCheckButton est un bouton à trois états. Normalement il s'agit des états 'TRUE' ou 'FALSE' , et alterne entre 'TRUE' et 'FALSE' quand il est pressé. Un troisième état peut être utilisé pour indiquer que la sélection n'a pas encore été faite par l'utilisateur, ou bien quand l'état est ambiguë.

 

Ancêtre : FXLabel.

 

Déclaration :

FXCheckButton.new(parent, text, target=nil, selector=0, opts=CHECKBUTTON_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD) {|theCheckButton| ...}

 

Les constantes prédéfinies :

Pour l'apparence ('opts') :

CHECKBUTTON_AUTOGRAY : Automatiquement grisé quand non actualisé.

CHECKBUTTON_AUTOHIDE   : Automatiquement caché quand non actualisé.

CHECKBUTTON_NORMAL      :  JUSTIFY_NORMAL|ICON_BEFORE_TEXT

 

Les attributs :

checkState  [RW]  : état de la case à cocher,TRUE, FALSE ou MAYBE [Integer]

checkButtonStyle   [RW]  : style de la case à cocher [Integer]

boxColor [RW]  : couleur de fond de la case à cocher [FXColor]

 

Les méthodes :

checked?()  : Retourne 'true' si la case est cochée (état à vrai).

unchecked?()  : Retourne 'true' si la case n'est  pas cochée (état à faux).

maybe?() : Retourne 'true' si l'état de la case est MAYBE (état à peut-être)

 

Les événements :

  Les messages suivants sont envoyés à la cible par FXRadioButton.

 

SEL_COMMAND : envoyé lors d'un clic sur le bouton.

SEL_KEYPRESS : envoyé lors de l'appui d'une touche.

SEL_KEYRELEASE : envoyé lors du relâchement d'une touche

SEL_LEFTBUTTONPRESS : envoyé lors de l'appui de la touche gauche de la souris

SEL_LEFTBUTTONRELEASE : envoyé lors de l'appui de la touche droite de la souris.

 

Les messages sont des instances de FXEvent

 

 

FXColorDialog

FXColorDialog est une boîte de dialogue pour éditer les couleurs. Elles peuvent être éditées via

RGB (Rouge, Vert, Bleu en mode de couleurs additives), via HSV (Teinte, Saturation, Valeur), via CMY (Cyan, Magenta, Jaune en mode de couleurs soustractives), ou par noms. Les couleurs habituelles peuvent être tranportées dans de petits godets pour être utilisées répétitivement; ces couleurs sont automatiquement sauvegardées dans la base de registres pour les futures utilisations.

 

Ancêtre : FXDialogBox.

 

Déclaration :

FXColorDialog .new(owner, title, opts=0, x=0, y=0, width=0, height=0) {|theColorDialog| ...}

 

 

Les constantes prédéfinies :

 ID_COLORSELECTOR : usage interne pour identifier les messages de FXColorSelector.

 

 Les attributs :

 opaqueOnly [W] : couleurs opaques seulement. [Boolean]

 rgba [RW] : la couleur [FXColor]

 

 Les méthodes :

 opaqueOnly?() : retourne 'true' si les couleurs opaques sont seules permises.

 

Les événements :

SEL_CHANGED : envoyé continuellement tant que le sélecteur de couleurs est utilisé.

SEL_COMMAND : envoyé lors de la sélection définitive.

 

 

 

FXComboBox

Zone de liste modifiable.

 

Ancêtre : FXPacker.

 

Déclaration :

FXComboBox.new(parent, numColumns, numVisible, target=nil, selector=0, opts=COMBOBOX_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD) {|theComboBox| ...}

 

Les constantes prédéfinies :

Pour l'apparence :

COMBOBOX_INSERT_AFTER : le texte frappé est inséré après le texte courant.

COMBOBOX_INSERT_BEFORE : le texte frappé est inséré avant le texte courant.

COMBOBOX_INSERT_FIRST : le texte frappé est inséré au début de la liste.

COMBOBOX_INSERT_LAST : le texte frappé est inséré à la fin de la liste.

COMBOBOX_NO_REPLACE : laisse la liste inchangée.

COMBOBOX_NORMAL : options par défaut.

COMBOBOX_REPLACE : remplace l'item courant par le texte frappé.

COMBOBOX_STATIC : boîte inchangeable.

Les messages :

 ID_LIST : identifiant associé à l'instance intégrée de FXList.

 ID_TEXT : identifiant associé à l'instance intégrée de FXTextField.

 

Les attributs :

  backColor [RW] : couleur du fond [FXColor]

  comboStyle [RW] : style de la boîte à lister [Integer]

  currentItem [RW] : index de l'item courant, ou '-1' s'il n'y en a pas [Integer]

  editable [W] : état éditable [Boolean]

  font [RW] : fonte du texte [FXFont]

  helpText [RW] : texte d'aide de la ligne de statut [String]

  numColumns [RW] : nombre de colonnes [Integer]

  numItems [R] : nombre d'items dans la liste [Integer]

  numVisible [RW] : nombre d'items visibles [Integer]

  selBackColor [RW] : couleur du fond pour les items sélectionnés [FXColor]

  selTextColor [RW] : couleur du texte pour les items sélectionnés [FXColor]

  text [RW] : texte [String]

  textColor [RW]  : couleur du texte [FXColor]

  tipText [RW] : message info-bulle [String]

 

Les méthodes :

editable?() : renvoie 'true' si la boîte est éditable.

itemCurrent?(index) : retourne 'true' si l'item situé à l'index 'index' est l'item courant. Renvoie  un IndexError si l'index est hors limites.

retrieveItem(index) : retourne le texte de l'item situé à 'index'. Renvoie un IndexError si l'index est hors limites.

replaceItem(index, text, data=nil) : remplace l'item situé à l'index 'index' par un nouvel item de texte 'text' et de données 'data'. Renvoie un IndexError si l'index est hors limites.

insertItem(index, text, data=nil) : insère un nouvel item à l'index 'index', de texte 'text' et de données 'data'. Renvoie un IndexError si l'index est hors limites.

appendItem(text, data=nil) : ajoute un nouvel item à la liste, de texte 'text' et de données 'data'.

prependItem(text, data=nil) : ajoute un item au début de la liste avec le texte 'text' et les données 'data'.

removeItem(index) : supprime de la liste l'item situé à l'index 'index'. Renvoie un IndexError si l'index est hors limites.

clearItems() :  supprime tous les items de la liste.

setItemText(index, text) : place le texte 'text' dans l'item situé à l'index 'index'. Renvoie un IndexError si l'index est hors limites.

getItemText(index) : renvoie le texte de l'item situé à l'index 'index'. Renvoie un IndexError si l'index est hors limites.

setItemData(index, data) : place les données 'data' dans l'item situé à l'index 'index'. Renvoie un IndexError si l'index est hors limites.

getItemData(index) : renvoie un pointeur pour les données de l'item situé à l'index 'index'. Renvoie un IndexError si l'index est hors limites.

paneShown?() : retourne 'true' si la sous-fenêtre est visible.

sortItems() : tri les items en utilisantla fonction de tri en cours.

each() {|itemText, itemData| ...} : itérateur. Appelle le bloc pour chaque item de la liste, en passant le texte de l'item et les données en tant que paramètres.

 

Les événements :

Les messages suivants sont envoyés par FXComboBox à ses cibles :

SEL_CHANGED : envoyé lors d'un changement de texte dans le champ texte; les données du message sont une chaîne contenant le nouveau texte.

SEL_COMMAND : envoyé quand un nouvel item est sélectionné dans la liste, ou quand un message de commande est envoyé à partir du champ texte; Les données du message sont une chaîne contenant le nouveau texte.

 

 

FXCommand

FxCommand est une classe abstraite de base pour vos commandes. Au minimum vos sous-classes doivent implémenter les méthodes 'undo', 'redo', 'undoName', et  'redoName'.

 

Ancêtre : Object.

 

Les méthodes :

undo() : annule cette commande; doit sauvegarder suffisamment d'informations pour rétablir.

redo() : rétabli la commande; doit sauvegarder suffisamment d'informations pour annuler.

undoName() : nom de la commande d'annulation à afficher sur un bouton ou un menu de commande; par exemple, "Annuler suppression".

redoName() : Nom de la commande de rétablissement, à afficher sur un bouton sous un menu de commande; par exemple "Rétablir suppression".

size() : retourne la taille de l'information dans l'enregistrement d'annulation, c'est à dire le nombre d'octets nécessaire au stockage en mémoire. Ceci est utilisé seulement par la méthode FXUndoList#trimSize, qui peut être appelée pour réduire l'utilisation de la mémoire de la liste d'annulation à une certaine dimension.

 

 

FXComposite

Composant composite, contenant d'autres composants, par opposition au composant individuel. FXWindow descend de ce composant.

 

Ancêtre : FXWindow.

 

Déclaration :

FXComposite.new(parent, opts=0, x=0, y=0, width=0, height=0) {|theComposite| ...}

 

Les méthodes :

maxChildWidth() : retourne la largeur de la fenêtre enfant la plus large.

maxChildHeight() : retourne la largeur de la fenêtre enfant la plus haute.

 

Les événements :

Les messages suivants sont envoyés par FXComposite à ses cibles.

SEL_KEYPRESS : envoyé lors de l'appui sur une touche, mais seulement si aucun autre composant n'a le focus (ou bien si le composant possédant le focus refuse le message).

SEL_KEYRELEASE : envoyé lors du relâchement d'une touche, mais seulement si aucun autre composant n'a le focus (ou bien si le composant possédant le focus refuse le message).

Les données sont une instance de FXEvent.

 

 

FXCURCursor

Classe des curseurs CUR.

 

Ancêtre : FXCursor.

 

Déclaration :

FXCURCursor.new(app, pixels) {|theCursor| ...}

Construit un curseur CUR à partir d'un flux mémoire dans le format CUR de Microsoft.

 

 

FXCursor

Classe des curseurs

 

Ancêtre : FXId.

 

Déclaration :

FXCursor.new(app, stockCursorId=CURSOR_ARROW) {|theCursor| ...}

Construit un curseur où 'stockCursorId' est un des curseurs disponibles (CURSOR_ARROW, CURSOR_RARROW, etc.)

Ou bien

 

FXCursor.new(app, source, mask, width=32, height=32, hotX=-1, hotY=-1) {|theCursor| ...}

Construit un curseur à partir de 'source' et de 'mask'. Les dimensions doivent être de 32X32 pixels pour la portabilité.

 

Les constantes prédéfinies :

Curseurs courants :

CURSOR_ARROW : flèche vers la gauche, par défaut.

CURSOR_RARROW : flèche vers la droite.

CURSOR_IBEAM : curseur texte (caret).

CURSOR_WATCH : sablier.

CURSOR_CROSS : curseur en forme de croix.

CURSOR_UPDOWN : curseur flèches haut, bas.

CURSOR_LEFTRIGHT : curseur flèches gauche, droite.

+CURSOR_MOVE : curseur de déplacement haut,bas,gauche, droite.

 

Les attributs :

height [R] : hauteur du curseur en pixels [Integer]

hotX [R] : Point d'attaque du curseur,coordonnée x [Integer]

hotY [R] : Point d'attaque du curseur,coordonnée y [Integer]

width [R] : largeur du curseur en pixels [Integer]

 

Les méthodes :

savePixels(stream) : sauve les données en pixels seulement.

loadPixels(stream) : charge les données en pixels seulement.

 

 

FXDataTarget

 Un composant FXDataTarget permet à un composant tel qu'un FXSlider ou un FXTextField d'être directement connecté à une variable dans le programme. Même si le contrôle change, la variable connectée par l'intermédiaire de FXDataTarget est automatiquement mise à jour ; réciproquement, même si le programme change une variable, tous les composants connectés seront mis à jour pour afficher la nouvelle valeur. FxDataTarget permet aussi de connecter des FXRadioButtons, FXMenuCommands etc.. à une variable. Dans ce cas la nouvelle valeur de la variable est calculée en soustrayant : +FXDataTarget::ID_OPTION+ de l'identifiant du message

 

Ancêtre : FXObject.

 

Module inclus : Responder2

 

Déclaration :

FXDataTarget.new(value=nil, tgt=nil, sel=0) {|theDataTarget| ...}

 

Les constantes prédéfinies :

ID_VALUE : fait demander par FXDataTarget la valeur à l'expéditeur.

ID_OPTION :  ID_OPTION+i met la valeur à i, où -10000 <= i <= 10000.

 

Les attributs :

selector [RW] : l'identifiant du message pour cette donnée [Integer]

target [RW]  : l'objet cible pour cette donnée [FXObject]

value [RW] : la valeur courante de la donnée de la cible [Object]

 

Les méthodes :

to_s() : retourne la représentation sous forme de chaîne de la valeur de cette donnée

 

Les événements :

Les messages suivants sont envoyés par FXDataTarget à ses cibles :

SEL_COMMAND : envoyé après que la cible est elle-même produit un message SEL_COMMAND.

SEL_CHANGED : envoyé après que la cible est elle-même produit un message SEL_CHANGED.

 

 

FXDC

 

Un contexte de périphérique est utilisé pour maintenir l'état du système graphique. Définir votre code de dessin en terme de contexte de périphérique abstrait permet d'afficher sur différents types de surfaces, par exemple les fenêtres et les images (FXDCWindow), ou bien sur papier (FXDCPrint) avec un code identique.

 

Ancêtre : Object.

 

Déclaration :

FXDC.new(app)

 

Les constantes prédéfinies :

Fonctions déterminant le mode de tracé (BITBLT) : D=Destination, S=Source.

BLT_CLR : D := 0

BLT_SRC_AND_DST : D := S & D

BLT_SRC_AND_NOT_DST : D := S & ~D

BLT_SRC : D := S

BLT_NOT_SRC_AND_DST : D := ~S & D

BLT_DST : D := D

BLT_SRC_XOR_DST : D := S ^ D

BLT_SRC_OR_DST : D := S | D

BLT_NOT_SRC_AND_NOT_DST : D := ~S & ~D == D := ~(S | D)

BLT_NOT_SRC_XOR_DST : D := ~S ^ D

BLT_NOT_DST : D := ~D

BLT_SRC_OR_NOT_DST : D := S | ~D

BLT_NOT_SRC: D := ~S

BLT_NOT_SRC_OR_DST : D := ~S | D

BLT_NOT_SRC_OR_NOT_DST : D := ~S | ~D == ~(S & D)

BLT_SET : D := 1

 

Style de lignes :

LINE_SOLID : ligne continue.

LINE_ONOFF_DASH : ligne en pointillés.

LINE_DOUBLE_DASH : ligne en pointillés doubles.

 

Styles de terminaison des lignes :

CAP_NOT_LAST : sans

CAP_BUTT : la ligne se termine au dernier point et le bout est carré.

CAP_ROUND : bout arrondi.

CAP_PROJECTING : le bout dépasse.

 

Styles de jonction des lignes :

JOIN_MITER : pointu.

JOIN_ROUND : arrondi.

JOIN_BEVEL : tronqué.

 

Style de remplissage :

FILL_SOLID : avec couleur unie.

FILL_TILED : avec mosaïque.

FILL_STIPPLED : remplissage avec masque de pointillé 1.

FILL_OPAQUESTIPPLED : remplissage avec l'avant-plan où le masque est 1, sinon avec l'arrière-plan.

 

Règles de remplissage :

RULE_EVEN_ODD : pair impair.

RULE_WINDING : suivit de contour.

 

Formes de trame :

+STIPPLE_0+:: STIPPLE_NONE::

 STIPPLE_BLACK : tout à 1.

 

+STIPPLE_1+:: +STIPPLE_2+:: +STIPPLE_3+:: +STIPPLE_4+:: +STIPPLE_5+:: +STIPPLE_6+:: +STIPPLE_7+::+STIPPLE_8+::

 

 STIPPLE_GRAY : 50% gris.

 

+STIPPLE_9+:: +STIPPLE_10+:: +STIPPLE_11+:: +STIPPLE_12+:: +STIPPLE_13+:: +STIPPLE_14+::+STIPPLE_15+:: +STIPPLE_16+::

 

 STIPPLE_WHITE : tout à zéro.

 STIPPLE_HORZ : trame horizontale.

 STIPPLE_VERT : trame verticale.

 STIPPLE_CROSS : trame croisée.

 STIPPLE_DIAG : trame en diagonale // .

 STIPPLE_REVDIAG : trame en diagonale inverse \\

 STIPPLE_CROSSDIAG : trame en diagonale croisée.

 

Les attributs :

app [R] : l'application.

background [RW] : couleur de tracé du fond [FXColor]

clipHeight [R] : hauteur du rectangle de clippage, en pixels.

clipRectangle [R] : Clip rectangle [FXRectangle]

clipRegion [W] :  région de clippage[FXRegion]

clipWidth [R] : largeur du rectangle de clippage, en pixels[Integer]

clipX [R] : coordonnée X du rectangle de clippage [Integer]

clipY [R] : coordonnée Y du rectangle de clippage  [Integer]

dashLength [R] : longueur des traits [Integer]

dashOffset [R] : décalage [Integer]

dashPattern [R] : forme des traits [String]

fillRule [RW]  :règle de remplissage, soit RULE_EVEN_ODD soit RULE_WINDING

                         [Integer]

fillStyle [RW]  : style de remplissage, soit  FILL_SOLID, FILL_TILED, FILL_STIPPLED ou FILL_OPAQUESTIPPLED  [Integer]

foreground [RW] : couleur de tracé de l'avant plan. [FXColor]

function [RW] : fonction du mode de tracé : BLT_CLR, BLT_SRC, BLT_DST, etc. [Integer]

lineCap [RW] : style de terminaison des lignes, parmi CAP_NOT_LAST, CAP_BUTT, CAP_ROUND ou CAP_PROJECTING [Integer]

lineJoin [RW] : style de jonction des lignes, parmi JOIN_MITER, JOIN_ROUND ou JOIN_BEVEL [Integer]

lineStyle [RW] : style des lignes, parmi LINE_SOLID, LINE_ONOFF_DASH oo LINE_DOUBLE_DASH [Integer]

lineWidth [RW] : largeur de ligne, une ligne de largeur zéro est la plus mince possible

et la plus rapide [Integer]

stipple [RW] : forme de trame [FXBitmap or Integer]

textFont [RW] : fonte pour les textes [FXFont]

tile [RW] : image mosaïque [FXImage]

 

Les méthodes :

readPixel(x, y) : retourne le pixel situé à la position (x,y)

drawPoint(x, y) :trace un point en (x,y)

drawPoints(points) : trace plusieurs points, où ' points' est un tableau d'instances de FXPoint.

drawPointsRel(points) : trace plusieurs points, relativement au point précédent, où 'points' est un tableau d'instances FXPoint.

drawLine(x1, y1, x2, y2)  : trace une ligne de (x1,y1) à (x2,y2).

drawLines(points) : trace des lignes reliées, où 'points' est un tableau d'instances de FXPoint.

 Le nombre de lignes tracées est égal à la taille du tableau de points moins un. Traite toutes

 les coordonnées de points relativement à l'origine.

drawLinesRel(points) : trace des lignes reliées, où 'points' est un tableau d'instances de

FXPoint. Le nombre de lignes tracées est égal à la taille du tableau de points moins un.

Traite toutes les coordonnées de points (après le premier) relativement au point précédent.

drawLineSegments(segments) : trace des segments de droite, où les segments sont des

instances de FXSegment.

drawRectangle(x, y, w, h) : trace un rectangle à la coordonnée (x,y) en haut à gauche, de

largeur 'w' et de hauteur 'h'.

drawRectangles(rectangles) : trace plusieurs rectangles, où 'rectangles' est un tableau

d'instances de FXRectangle.

drawArc(x, y, w, h, angle1, angle2) : trace des arcs de cercle. Les arguments 'angle1' et

'angle2' spécifient les points de départ (par rapport la position "3 heures") et d'arrivée (relativement au point de départ) de l'arc en unité de "degré*64" . Les arguments 'x,y,w et h' représentent le rectangle frontière.

drawArcs(arcs)  : trace des arcs, où 'arcs' est un tableau d'instances de FXArc.

fillRectangle(x, y, w, h) : trace un rectangle plein, ancré en haut à gauche en (x,y) et de 'w' de

large et 'h' de haut.

fillRectangles(rectangles) : trace des rectangles, où 'rectangles' est un tableau d'instances de

FXRectangle.

fillArc(x, y, w, h, angle1, angle2) : trace des arcs pleins. Même description que 'drawArc'.

fillArcs(arcs) : trace des arcs pleins, où 'arcs' est un tableau d'instances de FXArc.

fillPolygon(points) : trace des polygones pleins, où 'points' est un tableau d'instances de

FXPoint.

fillConcavePolygon(points) : trace des polygones pleins, où 'points' est un tableau d'instances

de FXPoint.

fillComplexPolygon(points) : trace des polygones complexes, où 'points' est un tableau

d'instances de FXPoint.

fillPolygonRel(points) : trace des polygones pleins avec des points "relatifs", où 'points' est un tableau d'instances de FXPoint.

fillConcavePolygonRel(points) : trace des polygones pleins avec des points "relatifs", où

 'points' est un tableau d'instances de FXPoint.

fillComplexPolygonRel(points) : trace des polygones pleins avec des points "relatifs", où

 'points' est un  tableau d'instances de FXPoint.

drawHashBox(x, y, w, h, borderWidth=1) : Trace une boîte ancrée en (x,y) de largeur 'w'

et hauteur 'h'.

drawFocusRectangle(x, y, w, h) : Trace un rectangle ancré en (x,y) de largeur 'w'

et hauteur 'h'.

drawArea(source, sx, sy, sw, sh, dx, dy) : trace une zone à partir de 'source'.

drawImage(image, dx, dy) : trace une image.

drawBitmap(bitmap, dx, dy) :trace un bitmap.

drawIcon(icon, dx, dy) : trace une icône.

drawIconShaded(icon, dx, dy) : trace une icône ombrée (utilisée pour les étiquettes et les boutons non valides).

drawIconSunken(icon, dx, dy)  : trace des icônes en puit.

drawText(x, y, string)  : trace un texte à la position (x,y).

drawImageText(x, y, string) : trace la chaîne 'string' à la position (x,y)

setDashes(dashOffset, dashPattern, dashLength) : établi forme et décalage de pointillés. Une forme de pointillés de [1 2 3 4] est une forme répétitive de 1 pixel en avant-plan, 2 en arrière-plan, 3 en avant-plan et 4 en arrière-plan. Le décalage commence là où le système commence à compter. La longueur maximale du tiret est de 32.

setClipRectangle(x, y, w, h) : établi le rectangle de clippage.

setClipRectangle(rectangle) : Change le rectangle de clippage.

clearClipRectangle()  : efface le rectangle de clippage.

setClipMask(bitmap, dx=0, dy=0) : applique le masque de clippage à 'bitmap'.

clearClipMask() : efface le masque de clippage.

clipChildren(yes) : découpe de nouveau les fenêtres enfants (où 'yes' est soit 'true' soit 'false')

 

 

FXDCWindow

FXDCWindow hérite de FXDC.

Un contexte de périphérique permet de tracer dans un composant 'FXDrawable', tel qu'une fenêtre visible (par exemple un  FXWindow et ses dérivés) ou une image non visible (FXImage et ses dérivés). Du fait des ressources restrictives de certains matériels, un seul FXDCWindow peut-être utilisé à la fois.

 

Ancêtre : FXDC.

 

Déclaration :

FXDCWindow. new(drawable, event) {|dc| ...}

Construct for painting in response to expose; this sets the clip rectangle to the exposed rectangle. If an optional code block is  provided, the new device context will be passed into the block as an argument and end will be called automatically when the block terminates.

 

FXDCWindow. new(drawable) {|dc| ...}

Construct for normal drawing; this sets clip rectangle to the whole drawable. If an optional code block is provided, the new device context will be passed into the block as an argument and end will be called automatically when the block terminates.

 

Les méthodes :

begin(drawable) : Lock in a drawable surface.

end()  : Unlock the drawable surface.

 

 

FXDial

Le cadran.

 

Ancêtre :FXFrame

 

Déclaration :

FXDial.new(p, tgt=nil, sel=0, opts=DIAL_NORMAL, x=0, y=0, w=0, h=0, pl=DEFAULT_PAD, pr=DEFAULT_PAD,pt=DEFAULT_PAD, pb=DEFAULT_PAD) {|theDial| ...}

 

Les constantes prédéfinies :

DIAL_VERTICAL : orientation verticale.

DIAL_HORIZONTAL : orientation horizontale.

DIAL_CYCLIC : valeur cyclique.

DIAL_HAS_NOTCH : le cadran a une encoche centrale.

DIAL_NORMAL : = DIAL_VERTICAL.

 

Les méthodes :

dialStyle [RW] : style du cadran [Integer]

helpText [RW] : texte d'aide de la ligne de statut [String]

notchColor [RW] : couleur de l'encoche centrale [FXColor]

notchOffset [RW] : le décalage de l'encoche est la position du centre de cette dernière. La valeur doit être en dixième de degrés dans un intervalle de [-3600,3600]. [Integer]

notchSpacing [RW] : les espaces pour les petites encoches. Ils doivent être en dixième de degrés dans un intervalle de [1,3600],  et la valeur doit être un diviseur de 3600, de façon à faire apparaître les encoches régulièrement [Integer]

range [RW] : intervalle [Range]

revolutionIncrement [RW] : l'incrément de révolution est le nombre qui permet de parcourir tout l'intervalle en restant perceptible [Integer]

tipText [RW] :texte de l'info-bulle

value [RW] : valeur du cadran [Integer]

 

Les messages :

Les messages suivants sont envoyés par FXDial à ses cibles :

SEL_LEFTBUTTONPRESS : envoyé lors de l'appui du bouton gauche de la souris. La donnée du message est une instance de FXEvent.

SEL_LEFTBUTTONRELEASE : envoyé lors du relâchement du bouton gauche de la souris. La donnée du message est une instance de FXEvent.

SEL_CHANGED : envoyé quand la valeur du cadran change. La donnée du message est la nouvelle valeur.

SEL_COMMAND : envoyé lorsque l'utilisateur arrête le changement de valeur en relachant le bouton de la souris. La donnée du message est la nouvelle valeur.

 

 

FXDialogBox

Quand une boîte de dialogue recoit un message SEL_COMMAND avec l'identifiant ID_CANCEL ou ID_ACCEPT la boîte de dialogue se ferme  et retourne '0' ou '1' respectivement.                                                                              

Pour fermer une boîte de dialogue non modale il suffit d'appeler la méthode 'hide' (FXDialogBox#hide) ou d'envoyer une commande ID_HIDE.

 

Ancêtre : FXTopWindow.

 

Déclaration :

FXDialogBox.new(app, title, opts=DECOR_TITLE|DECOR_BORDER, x=0, y=0, w=0, h=0, padLeft=10, padRight=10, padTop=10, padBottom=10, hSpacing=4, vSpacing=4) {|theDialogBox| ...}

Construit une boîte de dialogue indépendante (rattachée directement à 'app').

 

ou

 

FXDialogBox.new (owner, title, opts=DECOR_TITLE|DECOR_BORDER, x=0, y=0, w=0, h=0, padLeft=10, padRight=10, padTop=10, padBottom=10, hSpacing=4, vSpacing=4) {|theDialogBox| ...}

Construit une boîte de dialogue sur la fenêtre propriétaire (owner).

 

Les constantes prédéfinies :

ID_CANCEL : ferme le dialogue, annule l'entrée.

ID_ACCEPT : ferme le dialogue et accepte l'entrée.

 

Les méthodes :

execute(placement=PLACEMENT_CURSOR) : exécute une boîte de dialogue modale à l'emplacement indiqué.

 

 

FXDirBox

Boîte de répertoire.

 

Ancêtre : FXTreeListBox.

 

Déclaration :

FXDirBox.new(p, nvis, tgt=nil, sel=0, opts=FRAME_SUNKEN|FRAME_THICK|TREELISTBOX_NORMAL, x=0, y=0, w=0,h=0, pl=DEFAULT_PAD, pr=DEFAULT_PAD, pt=DEFAULT_PAD, pb=DEFAULT_PAD) {|theDirBox| ...}

 

Les attributs :

directory [RW] : répertoire courant [String]

 

Les messages :

SEL_CHANGED : envoyé quand l'item courant change; la donnée du message est le nouveau répertoire courant.

SEL_COMMAND : envoyé quand l'item courant change; la donnée du message est le nouveau répertoire courant

            

 

 

FXDirDialog

Dialogue de sélection de répertoire.

 

Ancêtre : FXDialogBox.

 

Déclaration :

FXDirDialog.new(owner, name, opts=0, x=0, y=0, w=500, h=300) {|theDirDialog| ...}

Retourne une instance initialisée de FXDirDialog.

 

Les attributs :

dirBoxStyle [RW] : style de la liste des répertoires [Integer]

directory [RW] : répertoire [String]

 

 

FXDirItem

Item de répertoire.

 

Ancêtre : FXTreeItem.

 

Déclaration :

FXDirItem.new(text, oi=nil, ci=nil, data=nil) {|theDirItem| ...}

 

Les attributs :

date [R]: date du fichier [Integer]

assoc [R] : association de fichier [FXFileAssoc]

size [R] : taille du fichier [Integer]

 

Les méthodes :

directory?() : renvoie 'true' si c'est un répertoire.

executable?()  : renvoie 'true' si c'est un exécutable.

symlink?() : renvoie 'true' si c'est un lien symbolique.

chardev?() : renvoie 'true' si c'est un

Return true if this is a character device

blockdev?()

Return true if this is a block device

fifo?()

Return true if this is a FIFO (a named pipe)

socket?() : renvoie 'true' si c'est un socket.

 

 

FXDirList

Liste de répertoire.

 

Ancêtre : FXTreeList.

 

Déclaration :

FXDirList.new(p, nvis, tgt=nil, sel=0, opts=0, x=0, y=0, w=0, h=0) {|theDirList| ...}

 

Les constantes prédéfinies :

DIRLIST_SHOWFILES : Montre les répertoires et les fichiers.

DIRLIST_SHOWHIDDEN : montre les fichiers et les répertoires cachés.

DIRLIST_NO_OWN_ASSOC : ne crée pas d'association pour les fichiers.

Pour les identifiants de message :

ID_REFRESH : x

ID_SHOW_FILES : x

ID_HIDE_FILES : x

ID_TOGGLE_FILES : x

ID_SHOW_HIDDEN : x

ID_HIDE_HIDDEN : x

ID_TOGGLE_HIDDEN : x

ID_SET_PATTERN : x

ID_SORT_REVERSE : x

 

Les attributs :

associations [RW] : associations des fichiers [FXFileDict]

currentFile [RW] : fichier courant [String]

directory [RW] : répertoire courant[String]

matchMode [RW] : Wildcard matching mode, some combination of file matching flags [Integer]

pattern [RW] : Wildcard pattern [String]

 

Les méthodes :

itemDirectory?(anItem)  : retourne 'true' si l'item est un répertoire.

itemFile?(anItem)  : retourne 'true' si l'item est un fichier.

itemExecutable?(anItem) : retourne 'true' si l'item est un éxécutable.

itemFilename(anItem) : retourne le nom de l'item.

itemPathname(anItem) : retourne le chemin complet de l'item.

filesShown?() : retourne 'true' si les fichiers et les répertoires sont affichés.

filesShown=(state) : si 'state' vaut 'true' les fichiers et les répertoires sont affichés, sinon ne montre que les répertoires.

hiddenFilesShown?() : retourne 'true' si les fichiers et répertoires cachés sont affichés.

hiddenFilesShown=(state) : si 'state' est à 'true' les fichiers cachés sont affichés sinon non.

.

Les messages :

SEL_CLOSED : envoyé lorsqu'un item "repliable" est fermé. La donnée du message est une référence au FXDirItem fermé.

SEL_OPENED : envoyé lorsqu'un item "repliable" est ouvert. La donnée du message est une référence au FXDirItem ouvert.

 

 

FXDragCorner

Un composant "coin de dragage" peut être placé à l'angle inférieur droit d'une fenêtre pour lui permettre de se redimensionner plus facilement.

 

Ancêtre : FXWindow

 

Déclaration :

FXDragCorner.new(p) {|theDragCorner| ...}

 

Les attributs :

hiliteColor [RW] : couleur de surbrillance [FXColor]

shadowColor [RW] : couleur de l'ombre [FXColor]

 

 

FXDrawable

FXDrawable est une classe abstraite pour les surfaces sur lesquelles on peut tracer quelque chose, comme  FXWindow et FXImage.

 

Ancêtre : FXId.

 

Les attributs :

height [R] : hauteur en pixels [Integer]

visual [RW] : Visuel [FXVisual]

width [R] : largeur en pixels [Integer]

 

Les méthodes :

resize(width, height) : Redimensionne avec la largeur (‘width’)  et la hauteur (‘height’) spécifiés.

 

 

FXEvent

La classe des évènements FOX.

 

Ancêtre : Object.

 

Les attributs :

click_button [R] : bouton pressé de la souris [Integer]

click_count [R]  : nombre de clics [Integer]

click_time [R] : durée de la pression sur le bouton [Integer]

click_x [R] : coordonnée 'x' du curseur souris, relativement à la fenêtre [Integer]

click_y [R] : coordonnée 'y' du curseur souris, relativement à la fenêtre [Integer]

code [R] : Button, keysym or mode; DDE source [Integer]

last_x [R] : coordonnée 'x' précédente du curseur souris, relativement à la fenêtre [Integer]

last_y [R] : coordonnée 'y' précédente du curseur souris, relativement à la fenêtre [Integer]

root_x [R] : coordonnée 'x' initiale de la fenêtre [Integer]

root_y [R] : coordonnée 'y' initiale de la fenêtre [Integer]

rootclick_x [R] : Coordonnée 'x' de la souris dans la fenêtre racine [Integer]

rootclick_y [R] : Coordonnée 'y' de la souris dans la fenêtre racine [Integer]

state [R] : état du clavier [Integer]

target [R] : type de cible requis [Integer]

text [R] : texte de l'événement clavier [String]

time [R] : intervalle de temps depuis le dernier évènement [Integer]

type [R] :

win_x [R] : coordonnée 'x' relatif à la fenêtre [Integer]

win_y [R] : coordonnée 'y' relatif à la fenêtre [Integer]

 

 

Les méthodes :

moved?() : retourne 'true' si le curseur souris s'est déplacé depuis la dernière pression d'un

bouton.

rect() : rectangle englobant pour les évènements de "peinture".

synthetic?() : retourne 'true' si c'est un événement synthétique.

 

 

FXFileAssoc

Pour tout savoir sur les extensions et associations de fichier.

 

Ancêtre : object.

 

Déclaration :

FXFileAssoc.new

 

Les attributs :

bigicon [RW] : grande icône normale [FXIcon]

bigiconopen [RW] : grande icône d'ouverture[FXIcon]

command [RW] :

dragtype [RW] : type de dragage [FXDragType]

extension [RW] : nom de l'extension [String]

flags [RW] :  drapeaux [Integer]

mimetype [RW] : nom du type Mime [String]

miniicon [RW] : mini icône normale [FXIcon]

miniiconopen [RW] : mini icône d'ouverture [FXIcon]

 

 

FXFileDialog

Boîte de dialogue pour ouvrir ou sauvegarder des fichiers.

 

Ancêtre : FXDialogBox.

 

Déclaration :

FXFileDialog. new(owner, name, opts=0, x=0, y=0, w=500, h=300) {|theFileDialog| ...}

 

Les attributs :

currentPattern [RW] : nombre de formats de fichiers [Integer]

directory [RW] : répertoire [String]

fileBoxStyle [RW] : style de la liste des fichiers [Integer]

filename [RW]  : nom du fichier [String]

filenames [R]  : liste des fichiers sélectionnés [Array]

itemSpace [RW] : espace, en pixels, entre les items [Integer]

pattern [RW] : format des fichiers [String]

patternList [RW]  : liste des formats de fichiers affichée dans la fenêtre de dialogue [Array]

selectMode [RW] : mode de sélection des fichiers [Integer]

 

Les méthodes :

getOpenFilename(owner, caption, path, patterns="*", initial=0) : renvoie le nom du fichier ouvert.

getOpenFilenames(owner, caption, path, patterns = "*", initial=0) : renvoie les noms des fichiers ouverts.

getSaveFilename(owner, caption, path, patterns="*", initial=0) : renvoie le nom du fichier à sauvegarder.

getOpenDirectory(owner, caption, path) : renvoie le nom du répertoire d'ouverture.

 

Méthodes d'instance publique :

getPatternText(patno)  : renvoie le texte des formats de fichiers.

setPatternText(patno, text) : change le texte des formats de fichiers.

showReadOnly=(shown) : établit la visibilité du bouton 'en lecture seule', où 'shown' est soit 'true' soit 'false'.

readOnlyShown?() : retourne 'true' si le bouton 'lecture seule' est visible.

readOnly=(state) : établit l'état initial du bouton de 'lecture seule', où 'state' est soit 'true' soit 'false'.

readOnly?() : retourne 'true' si en lecture seule.

 

Note :

  Chaque format dans la liste des formats comprend un nom optionnel, suivit par un format entre parenthèses. Les formats sont séparés par des retour à la ligne (\n).

Par exemple :

 

 "*\n*.cpp,*.cc\n*.hpp,*.hh,*.h"

 

et

 

 "All Files (*)\nC++ Sources (*.cpp,*.cc)\nC++ Headers (*.hpp,*.hh,*.h)"

 

indiqueront les mêmes trois formats, mais le premier ne montre pas le nom des formats.

 

 

FXFileItem

Item de fichiers.

 

Ancêtre : FXIconItem

 

Déclaration :

FXFileItem.new(text, bi=nil, mi=nil, ptr=nil) {|theFileItem| ...}

Retourne une instance de FXFileItem initialisée.

 

Les attributs :

assoc [R] : l'objet associé à ce fichier [FXFileAssoc]

date [R] : la date pour cet item [Time]

size [R] : la taille du fichier [Integer]

 

Les méthodes :

file?()  : retourne 'true' si c'est un item de fichier.

directory?() : retourne 'true' si c'est un item de répertoire.

executable?()  : retourne 'true' si c'est un item de fichier exécutable.

symlink?()  : retourne 'true' si c'est un item de lien symbolique.

chardev?()  : Return true if this is a character device item

blockdev?()  : Return true if this is a block device item

fifo?()  : retourne 'true' si c'est un FIFO.

socket?() : retourne 'true' si c'est un socket.

 

 

 

FXFileList

Construit une liste de fichier.

 

Ancêtre : FXIconList.

 

Déclaration :

FXFileList.new(p, tgt=nil, sel=0, opts=0, x=0, y=0, w=0, h=0) {|theFileList| ...}

 

Les constantes prédéfinies :

Pour les options de fichiers :

 FILELIST_SHOWHIDDEN : montre les fichiers et répertoires cachés.

 FILELIST_SHOWDIRS : montre seulement les répertoires.

 FILELIST_NO_OWN_ASSOC : ne crée pas les associations de fichiers.

 

Pour les identifiants de message :

 

 ID_SORT_BY_NAME : x

 ID_SORT_BY_TYPE : x

 ID_SORT_BY_SIZE : x

 ID_SORT_BY_TIME : x

 ID_SORT_BY_USER : x

 ID_SORT_BY_GROUP : x

 ID_SORT_REVERSE : x

 ID_DIRECTORY_UP : x

 ID_SET_PATTERN : x

 ID_SET_DIRECTORY : x

 ID_SHOW_HIDDEN : x

 ID_HIDE_HIDDEN : x

 ID_TOGGLE_HIDDEN : x

 ID_REFRESHTIMER : x

 ID_OPENTIMER : x

 

 

Les attributs :

associations [RW] : associations de fichiers [FXFileDict]

currentFile [RW] : fichier courant [String]

directory [RW] : répertoire courant [String]

matchMode [RW] :

                  Wildcard matching mode [Integer]

pattern [RW] :

                  Wildcard matching pattern [String]

 

Les méthodes :

itemDirectory?(index) : retourne 'true' si l'item est un répertoire. Envoit une erreur

                                      (IndexError) si l'index  est hors indice.

itemFile?(index) : retourne 'true' si item est un fichier. Envoit une erreur

                                      (IndexError) si l'index  est hors indice.

itemExecutable?(index) : retourne 'true' si l'item est un exécutable. Envoit une erreur

                                      (IndexError) si l'index  est hors indice.

 

itemFilename(index) : retourne le nom de l'item à l'index 'index'. Envoit une erreur

                                      (IndexError) si l'index  est hors indice.

itemPathname(index) : retourne le chemin complet de l'item à l'index 'index'. Envoit une

                                      erreur (IndexError) si l'index  est hors indice.

itemAssoc(index) : retourne l'association de l'item à l'index 'index'. Envoit une erreur

                                      (IndexError) si l'index  est hors indice.

hiddenFilesShown?() : retourne 'true' si les fichiers cachés sont montrables.

hiddenFilesShown=(shown)  : montre ou cache les fichiers cachés.

onlyDirectoriesShown?() : retourne 'true' si on montre seulement les répertoires.

onlyDirectoriesShown=(shown) : montre ou cache seulement les répertoires.

 

 

FXFileStream

 

Ancêtre : FXSteam.

 

Déclaration :

FXFileStream.new(cont=nil) {|theFileStream| ...}

  Construit un flux.

FXFileStream.open(filename, save_or_load, container=nil) {|theFileStream| ...}

Construit un nouvel objet FXFileStream avec la direction du flux (sauver ou charger) et l'objet contenant. Si un bloc de code est donné, il sera passé à ce flux comme argument, et le flux sera automatiquement fermé à la fin du bloc. S'il n'y a pas de bloc de code, cette méthode retourne le nouveau flux dans un état ouvert.

 

Les méthodes :

open(filename, save_or_load) : ouvre le flux.

 

 

 

FXFont

Classe des fontes.

 

Ancêtre : FXId.

 

Déclaration :

FXFont. new(app, face, size, weight=FONTWEIGHT_NORMAL, slant=FONTSLANT_REGULAR, encoding=FONTENCODING_DEFAULT, setWidth=FONTSETWIDTH_DONTCARE, hints=0) {|theFont| ...}

  Construit une fonte avec le nom de la fonte ('face'), la taille ('size') en points (pixels), la graisse, la pente, character set encoding, setwidth, and hints

 

Les constantes prédéfinies :

Renseignements sur le style de fonte influançant la comparaison.

FONTPITCH_DEFAULT : chasse par défaut.

FONTPITCH_FIXED : chasse fixe, non proportionnelle.

FONTPITCH_VARIABLE : chasse variable, proportionnelle.

FONTHINT_DONTCARE : fonte indifférente.

FONTHINT_DECORATIVE : fonte décorative.

FONTHINT_MODERN : trait d'épaisseur constante.

FONTHINT_ROMAN : largeur variable comme "times", avec sérif.

FONTHINT_SCRIPT : écriture manuelle.

FONTHINT_SWISS : Helvetica/swiss , sans-serif .

FONTHINT_SYSTEM : fonte système.

+FONTHINT_X11+ : X11 Fonte X11 (Unix)

FONTHINT_SCALABLE : Scalable fonts.

FONTHINT_POLYMORPHIC : fonte polymorphique.

 

Inclinaison des fontes

FONTSLANT_DONTCARE : sans importance.

FONTSLANT_REGULAR : normale.

FONTSLANT_ITALIC : italique.

FONTSLANT_OBLIQUE :oblique.

FONTSLANT_REVERSE_ITALIC : italique inverse.

FONTSLANT_REVERSE_OBLIQUE :  oblique inverse.

 

Codage alphabet disponible :

 

 FONTENCODING_DEFAULT : par défaut

 +FONTENCODING_ISO_8859_1+:ISO-8859-1

 +FONTENCODING_ISO_8859_2+:ISO-8859-2

 +FONTENCODING_ISO_8859_3+: ISO-8859-3

 +FONTENCODING_ISO_8859_4+:ISO-8859-4

 +FONTENCODING_ISO_8859_5+:ISO-8859-5

 +FONTENCODING_ISO_8859_6+:ISO-8859-6

 +FONTENCODING_ISO_8859_7+: ISO-8859-7

 +FONTENCODING_ISO_8859_8+: ISO-8859-8

 +FONTENCODING_ISO_8859_9+: ISO-8859-9

 +FONTENCODING_ISO_8859_10+: ISO-8859-10

 +FONTENCODING_ISO_8859_11+: ISO-8859-11

 +FONTENCODING_ISO_8859_13+:ISO-8859-13

 +FONTENCODING_ISO_8859_14+: ISO-8859-14

 +FONTENCODING_ISO_8859_15+: ISO-8859-15

 +FONTENCODING_ISO_8859_16+:ISO-8859-16

 +FONTENCODING_KOI8+:KOI-8

 +FONTENCODING_KOI8_R+:Russian

 +FONTENCODING_KOI8_U+: Ukrainian

 +FONTENCODING_KOI8_UNIFIED+: x

 +FONTENCODING_LATIN1+ : identique à +FONTENCODING_ISO_8859_1+, Latin 1 (West European)

 +FONTENCODING_LATIN2+ : identique à+FONTENCODING_ISO_8859_2+, Latin 2 (East European)

 +FONTENCODING_LATIN3+ : identique à+FONTENCODING_ISO_8859_3+, Latin 3 (South European)

 +FONTENCODING_LATIN4+ : identique à+FONTENCODING_ISO_8859_4+, Latin 4 (North European)

 +FONTENCODING_LATIN5+ : identique à+FONTENCODING_ISO_8859_9+, Latin 5 (Turkish)

 +FONTENCODING_LATIN6+ : identique à+FONTENCODING_ISO_8859_10+, Latin 6 (Nordic)

 +FONTENCODING_LATIN7+ : identique à +FONTENCODING_ISO_8859_13+, Latin 7 (Baltic Rim)

 +FONTENCODING_LATIN8+ : identique à +FONTENCODING_ISO_8859_14+, Latin 8 (Celtic)

 +FONTENCODING_LATIN9+ : identique à +FONTENCODING_ISO_8859_15+, Latin 9 (a.k.a. Latin 0)

 +FONTENCODING_LATIN10+ : identique à +FONTENCODING_ISO_8859_16+, Latin 10

 FONTENCODING_USASCII :  identique à +FONTENCODING_ISO_8859_1+, Latin 1

 FONTENCODING_WESTEUROPE : identique à +FONTENCODING_ISO_8859_1+, Latin 1 (West European)

 FONTENCODING_EASTEUROPE : identique à +FONTENCODING_ISO_8859_2+, Latin 2 (East European)

 FONTENCODING_SOUTHEUROPE : identique à +FONTENCODING_ISO_8859_3+, Latin 3 (South European)

 FONTENCODING_NORTHEUROPE : identique à +FONTENCODING_ISO_8859_4+, Latin 4 (North European)

 FONTENCODING_CYRILLIC : identique à +FONTENCODING_ISO_8859_5+, Cyrillic

 FONTENCODING_RUSSIAN : identique à +FONTENCODING_KOI8+, Cyrillic

 FONTENCODING_ARABIC : identique à +FONTENCODING_ISO_8859_6+, Arabic

 FONTENCODING_GREEK : identique à +FONTENCODING_ISO_8859_7+, Greek

 FONTENCODING_HEBREW : identique à +FONTENCODING_ISO_8859_8+, Hebrew

 FONTENCODING_TURKISH : identique à +FONTENCODING_ISO_8859_9+, Latin 5 (Turkish)

 FONTENCODING_NORDIC : identique à +FONTENCODING_ISO_8859_10+, Latin 6 (Nordic)

 FONTENCODING_THAI : identique à +FONTENCODING_ISO_8859_11+, Thai

 FONTENCODING_BALTIC : identique à +FONTENCODING_ISO_8859_13+, Latin 7 (Baltic Rim)

 FONTENCODING_CELTIC : identique à +FONTENCODING_ISO_8859_14, Latin 8 (Celtic)

 

 

Graisse des fontes (poids d'encre)

FONTWEIGHT_DONTCARE : sans importance.

FONTWEIGHT_THIN : mince .

FONTWEIGHT_EXTRALIGHT : extra fine.

FONTWEIGHT_LIGHT : fine.

FONTWEIGHT_NORMAL : normale.

FONTWEIGHT_REGULAR : régulière.

FONTWEIGHT_MEDIUM : medium. 

FONTWEIGHT_DEMIBOLD : demi grasse.

FONTWEIGHT_BOLD : grasse.

FONTWEIGHT_EXTRABOLD : extra  grasse.

FONTWEIGHT_HEAVY : lourde.

FONTWEIGHT_BLACK : noire.

 

 

Largeur relative des fontes :

 

 FONTSETWIDTH_DONTCARE : sans importance.

 FONTSETWIDTH_ULTRACONDENSED : ultra condensée. 

 FONTSETWIDTH_EXTRACONDENSED : extra condensée.

 FONTSETWIDTH_CONDENSED : condensée.

 FONTSETWIDTH_NARROW : étroite.

 FONTSETWIDTH_COMPRESSED : compressée.

 FONTSETWIDTH_SEMICONDENSED : semi-condensée.

 FONTSETWIDTH_MEDIUM : médium.

 FONTSETWIDTH_NORMAL : normale.

 FONTSETWIDTH_REGULAR : régulière.

 FONTSETWIDTH_SEMIEXPANDED : semi étendue. 

 FONTSETWIDTH_EXPANDED : étendue.

 FONTSETWIDTH_WIDE : large.

 FONTSETWIDTH_EXTRAEXPANDED : extra large. 

 FONTSETWIDTH_ULTRAEXPANDED : ultra large.

 

Les attributs :

encoding [R] : codage [Integer]

fontAscent [R] : la plus grande hauteur au dessus de la ligne de base [Integer]

fontDesc [RW] : description  de la fonte[FXFontDesc]

fontDescent [R] : la plus grande hauteur au dessous de la ligne de base [Integer]

fontHeight [R] : hauteur du plus grand caractère de la fonte[Integer]

fontLeading [R] : Font leading [Integer]

fontSpacing [R] : interligne [Integer]

fontWidth [R] : largeur du caractère le plus large de la fonte[Integer]

hints [R] : renseignements [Integer]

maxChar [R] : glyphe du dernier caractère de la fonte [Integer]

minChar [R] : glyphe du premier caractère de la fonte [Integer]

name [R] : nom de la fonte [String]

setWidth [R] : largeur [Integer]

size [R] : dimension en  decipoints [Integer