Château Gombert – Programmation Orientée Objet

publicité
licence informatique – Château Gombert – Programmation Orientée Objet :
Travaux dirigés :
Séances dédiées à la programmation graphique sous JAVA
Suite et fin
1.7 : Dessiner et écrire
Seuls certains objets graphiques Swing ou AWT peuvent recevoir des contenus textuels et/ou dessinés.
Nous allons nous intéresser ici à quelques uns de ces objets.
Pour dessiner, le peintre n’utilise pas le cadre, mais la toile. En java, avec AWT c’est le même
processus : on ne dessine pas dans une Frame, mais dans un Canvas. le Canvas doit être étendu par une
classe définie par nous.
Avec Swing (notre choix), il n’est heureusement pas nécessaire de passer par cette étape : on utilise une
toile toute prête telle que je JPanel:
-créons une JFrame
-ajoutons lui un JPanel (ou un objet qui étend JPanel)
- dès qu’un évenement visuel se produit (la fenêtre est redimensionnée, salie par une fenêtre qui
passe devant, modifiée…), la méthode repaint du JPanel est appelée par le système. Cette méthode
déclenche un appel à deux méthodes qui nous intéressent particulièrement : update et paint. Si nous
voulons dessiner quelque chose dans notre JPanel, il nous faut redéfinir ces méthodes.
Voici un premier exemple avec la classe Circuit:
class Circuit extends JPanel implements MouseListener, MouseMotionListener
{
//… il nous faut redéfinir les méthodes ci-dessous …
// remarquez le paramètre ‘Graphics g’ : cette variable est l’espace où nous pouvons dessiner.
public synchronized void paint(Graphics g)
{
update(g);
// c’est tout pour paint (update fait le reste !!!)
}
public synchronized void update(Graphics g)
{
//ici, un exemple simpliste qui écrit ‘coucou’ en jaune à l’emplacement (10,10):
g.setColor(Color.yellow);
g.drawString(“Coucou !”, 10, 10);
}
}
1.7.a : des couleurs et des crayons
Dans le premier exemple ci-dessus, tout dessin ‘utilisateur’ est réalisé par la méthode update grâce à la
variable g de type Graphics qui est fournie par le système au moment nécessaire. Il nous est bien sûr
permis de déléguer à d’autres objets le droit de se peindre dans ce contexte graphique g. (nos
composant, par exemple devraient être dotés d’une méthode pour se peindre recevant en paramètre un
contexte graphique g). Le circuit n’aura alors plus qu’à parcourir le tableau de composants et leur
demander de se redessiner. Ne brûlons pas les étapes. Apprenos d’abord le b.a.-ba du dessin.
Dans la méthode update de notre circuit, il est maintenant permis d’appeler toutes les méthodes du
contexte graphique g.
Celui-ci permet d’accéder à une boite à outils (polices de caractères…) et à une palette de couleurs :
A tout moment, on peut changer d’outil et de couleur avec les commandes suivantes :
g.setFont( objet de type Font) ;
g.setColor( objet de type Color) ;
Jongler avec les Font et les Color et assez difficile si on souhaite aller loin, mais si on n’est pas exigeant,
JAVA fournit quelques polices par défaut assez simples et un jeu de constantes de couleur acceptables.
Ex :
g.setFont(new Font("Dialog", Font.PLAIN, 10)) ;
g.setColor( Color.black) ; //constante prédéfinie
Avec nos couleurs et nos polices de caractères, il nous est maintenant possible de dessiner avec une
variable de type Graphics:
1.7.b : des lignes, des points etc…
Des lignes de plus en plus complexes:
drawLine(int x1, int y1, int x2, int y2)
drawOval(int x1, int y1, int width, int height)
drawRect(int x1, int y1, int width, int height)
Vous pouvez aussi dessiner des formes pleines avec les méthodes du type
fillOval(int x1, int y1, int width, int height)
fillRect(int x1, int y1, int width, int height)
etc… (cf http://java.sun.com/j2se/1.3/docs/api/java/awt/Graphics.html)
1.7.c : du texte
Les chaines de caractères peuvent aussi bien se dessiner grâce aux polices choisies (on peut aussi ne pas
choisir de police et laisser faire JAVA).
g.drawString(String chaine, int x, int y) ;
1.8 : Dessiner une image
Nous avons besoin d’aller un peu plus loin avec le contexte graphique : par exemple précharger une
image et l’écrire au moment opportun.
1.8.a : Une image venant d’un fichier
JAVA connaît par défaut un nombre restreint de formats graphiques, mais cela nous suffira. Les formats
‘gif’ et ‘jpeg’ sont facilement utilisables. Les ressources du projet sont de type ‘gif’ avec une couleur de
transparence. Voyons quelques étapes du dessin de ce genre d’image.
L’objet Image en JAVA doit être étendu. Nous allons utiliser l’objet ImageIcon qui est très pratique:
ImageIcon i=new ImageIcon(System.getProperty("user.dir") + "/MicroLogic/ressources/AND.gif");
Observons au passage l’appel à la fonction System.getProperty(“user.dir”) qui renvoit le chemin depuis
lequel le programme a été lancé. Cette chaine de caractères nous permet de retrouver notre ressource
de type image :
System.getProperty("user.dir") + "/MicroLogic/ressources/AND.gif" est une chaine de
caractères menant à un fichier contenant l’image de la porte ET.
Une image de type ImageIcon est facilement duplicable :
ImageIcon j=new ImageIcon(i.getImage());
Dessiner une image de ce type est maintenant un jeu d’enfant: si par exemple nous disposons d’un objet
img de type ImageIcon, nous pouvons écrire dans la méthode update du circuit (de type JPanel):
//dessiner l’image img en (10,13) sur le contexte graphique g avec le JPanel ‘this’ comme conteneur
g.drawImage(img, 10, 13, this);
remarque 1 : la méhode drawImage a besoin d’un paramètre de type ImageObserver.
Nous lui fournissons à la place le composant de type Circuit qui est un JPanel qui étend
Jcomponent qui étend à son tour Component qui implément correctement
ImageObserver. On doit fournir un objet ImageObserver cohérent (un composant qui
contiendra l’image) à cette méthode, si on ne veut pas avoir de surprises au moment du
dessin.
Remarque 2 : si vous souhaitez déléguer le dessin des images aux objets de type
Composant, il sera nécessaire que tous les composants aient connaissance de l’objet
Circuit qui les contient afin de pouvoir appeler correctement cette méthode drawImage
qui a besoin d’un JPanel (cela nécessitera une petite gymnastique au moment de la
création des objets).
Remarque 3 à propos du projet de Circuits logiques. Les images que vous allez charger
vérifient certaines contraintes : elles font toutes 49 pixels * 49 pixels. Les extrémités
correspondant aux entrées et sorties sont positionnées très clairement à un endroit bien
précis. Vous devrez gérer des tableaux de points pour ces extrémités pour chaque
composant en fonction de l’image de ce composant. Grâce à ce tableau, vous pourrez
ensuite tracer les lignes de connection… Notez que vous aurez très vite des problèmes
de cables qui se croisent (ce qui est normal) car le tracé de graphes dont les arcs ne se
coupent pas ou dont les arcs contournent les nœuds est un problème excessivement
complexe qui sera abordé avec des algorithmes et des heuristiques difficiles, en Maîtrise
d’informatique ou plus tard encore. C’est un sujet de recherche à part entière.
1.8 : Technique du double-buffer
Un problème plus facile à résoudre va se poser très rapidement : dès qu’un composant est déplacé, tout
le circuit est repeint et l’ensemble clignote de manière peu agréable. En effet, la méthode repaint du
JPanel efface tout le contenu, puis redessine tout composant par composant.
Une technique consiste à ne pas dessiner directement dans le contexte graphique g dont nous avons parlé
jusqu’ici, mais à dessiner dans une image de la taille de ce contexte graphique. Cette image est ensuite
recopiée d’un seul coup dans le contexte graphique, ce qui évite le clignotement des objets. La technique
s’appelle celle du ‘double-buffer’. Elle est assez classique pour mériter quelques détails : voici son
déroulement.
//dans le circuit, prenons quelques variables d’instance :
Image offscreen= null;
Dimension offscreensize= getSize();
Graphics offgraphics= null;
//réécrivons la méthode update pour tenir compte de ces variables :
public synchronized void update(Graphics g)
{
//recopiez exactement ce code …
Dimension d= getSize();
//gérer le double buffer dans le cas où il doit être modifié ou recréé
if ((offscreen == null)
|| (d.width != offscreensize.width)
|| (d.height != offscreensize.height))
{
offscreen= createImage(d.width, d.height);
offscreensize= d;
offgraphics= offscreen.getGraphics();
}
//…jusqu’ici
//puis dessinez ce que vous voulez dans le double buffer
offgraphics.setColor(Color.yellow);
offgraphics.drawString(“coucou”, 15, 25);
//ici, vous pourriez dessiner un cadre vert, le nom du circuit
//puis effectuer une boucle parcourant le tableau de composants du circuit
// et appelant pour chacun d’eux la méthode paint avec offGraphics comme paramètre au
//lieu de g. Par exemple :
for (int i =0 ;i<nb_composants;i++)
Composants[i].paint(offgraphics);
//enfin, recopiez exactement le code ci-dessous :
paintComponents(offgraphics);
//Paint any components that have been added to this panel
g.drawImage(offscreen, 0, 0, null);
//…jusqu’ici.
}
Vous apprécierez la différence avec un contexte graphique directement modifié.
1.9 : Conclusion
Il reste beaucoup à découvrir avec Swing. Vous avez maintenant les outils nécessaires pour réaliser le
projet dans sa version finale.
N’hésitez pas à utiliser les tutoriels Swing fournis par Sun lorsque vous rencontrez des difficultés.
Téléchargement