© Jean-Pierre ANGHEL - 2004
FOX + RUBY
=
FXRuby
Par l’exemple
Quelles
connaissances faut-il avoir ?
Présentation
des composants visuels.
Présentation
des aménageurs d'espace.
Les
fenêtres et tout ce qui va avec.
Les
commentaires et substitutions
Exemple
général sur les boutons.
Les boutons
avec flèches incorporées
Les
expressions conditionnelles
Le composant
"Boîte de texte" et divers gadgets :
Les boîtes
à lister, les répertoires :
Les
variables globales prédéfinies
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
!)
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.
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?
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.
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 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.
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.
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…
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.
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.
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.
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.
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'.
Les boutons et autres composants
qui vont avec.
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.
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). |
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.
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 :
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)
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.
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.
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 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.
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
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.
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.
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.
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 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".
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
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.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 '.
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…'
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
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')
'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.
__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.
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.
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.
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"]
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"
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.
***************************************************************************
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 :
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 :
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.
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'
- 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 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.
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...
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 : '::'
CONST = 123
end
Par
contre les variables propres aux instances sont accessibles par ce que l'on
appelle des 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
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".
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
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.
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 !
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|
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 '
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
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
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.
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]+>/
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