TP5 Java
Transcription
TP5 Java
-1- Programmation Objet et langage Java TP5 : AFFICHEUR DEFILANT PRESENTATION DU THEME Dans de nombreuses circonstances, à l'usine comme dans les lieux publics il est utile d'afficher une information visible de loin. Suivant l'importance et les enjeux, l'affichage va utiliser du matériel plus ou moins sophistiqué. Dans ce TP, notre objectif est de simuler en java un afficheur simple (sans LED) ainsi son l'IHM de commande mise à la disposition du responsable. Journal Défilant Choisir l’information à diffuser Notre diagramme des cas d'utilisation simplifié sera donc : Information à diffuser Démarrer/Arrêter le défilement Afficheur défilant Responsable Régler la vitesse de défilement PRINCIPE DE FONCTIONNEMENT DE L’AFFICHEUR L’afficheur défilant se compose d’une fenêtre de type boite de dialogue contenant l’affichage (classe Afficheur) et d’une interface de pilotage composé d’une fenêtre héritant de la classe Frame (classe PiloteAfficheur). La classe principale est JournalDefilant. Cette classe lance l’application. L’interface de pilotage permet de : spécifier le fichier contenant le texte à afficher, démarrer ou arrêter le défilement de l’affichage, régler la vitesse de défilement à l’aide d’une barre de défilement, fermer l’application à l’aide du bouton . Une fois le fichier contenant le texte à afficher spécifié, le texte affiché est rechargé depuis ce fichier à chaque nouveau départ de défilement pour permettre une mise à jour du texte de façon dynamique. TEXTE A AFFICHER Le texte à afficher est situé dans la première ligne d’un fichier d’extension .txt. Exemple : Supposons que la première ligne du fichier contient le célèbre vers de Verlaine : Les sanglots longs des violons...bercent mon cœur d’une langueur monotone. La taille de la zone d’affichage étant généralement inférieure à la taille du texte à afficher, l’afficheur fera défiler le texte à la façon d’un journal lumineux. Exemple l’afficheur affiche ici le texte en rouge : Les sanglots longs des violons....monotone 1. L’affichage débute en faisant entrer le texte par la droite: Les sanglots longs des violons de l’automne....monotone 2. La fin du défilement est obtenue lorsque la dernière lettre quitte l’affichage : Les sanglots longs des violons de l’automne....monotone Puis le processus redémarre en 1 Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -2- Programmation Objet et langage Java DIAGRAMME DES CLASSES DE NIVEAU CONCEPTION Pour faciliter la programmation objet, il est utile de concevoir la solution d'abord sous forme d'un diagramme des classes mises en jeu. Les liens sémantiques entre les classes sont résumés sous forme d'un simple verbe car l'objectif de ce diagramme est avant tout d'aider le programmeur à comprendre l'architecture du programme et de naviguer plus facilement dans le code java résultant : Le BufferedReader est local à la méthode readFile(). Frame Le Programme principal Journal Défilant PiloteAfficheur Timer modifie période est écouté par Afficheur commande Affiche min,max vitesse 3 Scrollbar Dialog Button Trouve fichier du message Label Lit la 1ère ligne du fichier du message FileDialog est écouté par est écouté par Affiche dans 2 BufferedReader Extrait les octets Anonyme1 Anonyme3 Anonyme2 FileReader Lit les blocs de caractères AdjustmentListener ActionListener File Les écouteurs anonymes peuvent accéder aux variables de PiloteAfficheur donc à tous les composants de celui-ci. Ces associations trop nombreuses ne sont pas représentées ici. DIAGRAMME D'OBJETS Pour préparer la phase de codage, il faut détailler le rôle des 3 boutons et des 2 étiquettes. Mais c'est surtout le rôle primordial des 5 écouteurs qu'il est nécessaire d'expliciter à travers leurs liens sémantiques vers les différents objets de l'application. Un objet est désigné sous la forme : "nom_objet : non_classe" ou " : nom_classe" si l'objet est anonyme par exemple en java new XXX() ou bien le nom de l'objet n'est pas important. On peut aussi écrire "nom_objet : " si le nom de sa classe n'a pas d'importance mais ce n'est pas le cas dans ce TP. Comme les écouteurs sont des "objets anonymes" appartenant à des "classes anonymes dérivées de XXXListener", nous utilisons abusivement une forme pratique pour l'être humain : anonymeNN : XxxListener afficheur:Afficheur Le BufferedReader est local à la méthode readFile(). Le Programme principal Affiche dans : JournalDéfilant : PiloteAfficheur labelTextDefilant:Label affiche min vitesse scrollbarVitesse : Scrollbar est écouté par labelMin:Label affiche max vitesse timer : Timer buttonStart: Button buttonStop: Button est écouté par est écouté par obtient valeur modifie période Trouve fichier du message buttonFichier: Button labelMax:Label lit la 1ère ligne du fichier du message est écouté par fileDialog:FileDialog démarre est écouté par anonyme1 : AdjustmentListener arrête anonyme21 : ActionListener active active anonyme22 : ActionListener bfr:BufferedReader lire le fichier obtient nom répertoire + nom fichier rend visible anonyme23 : ActionListener anonyme3: ActionListener affiche le nom du fichier comme titre Extrait les octets :FileReader Lit les blocs de caractères :File affiche le message décalé en fonction du temps Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -3- Programmation Objet et langage Java DIAGRAMMES DE SEQUENCE CORRESPONDANT AUX CAS D'UTILISATION NOMINAUX. Un cas d'utilisation est nominal quand aucune erreur habituelle ne survient (fichier inconnu, erreur lecteur fichier, erreur dans la transmission électronique de l'information, etc). Diagramme de séquence associé au CU « Choisir l’information à diffuser » buttonFichier : Button cliquer anonyme23 : ActionListener actionPerformed() fileDialog :FileDialog rendre visible obtient nom répertoire obtient nom fichier construit le nom du fichier Afficheur : Afficheur affiche le nom du fichier dans la barre de titre : PiloteAfficheur lire le fichier instancie un BufferReader bfr pour lire le fichier lit la 1ère ligne ferme le BufferReader bfr buttonStart : Button activer buttonStop : Button activer Diagramme de séquence associé au CU « Régler vitesse de défilement » scrollbarVitesse : Scrollbar change valeur anonyme1 : AdjustmentListener timer : Timer adjustmentValueChanged() donne la valeur courante change la période Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -4- Programmation Objet et langage Java Diagramme de séquence associé au CU « Démarrer le défilement » buttonStart : Button anonyme21 : ActionListener timer : Timer cliquer actionPerformed() démarrer ref Tic Horloge Diagramme de séquence associé à l’événement « Tic Horloge » timer : Timer anonyme3 : ActionListener tic actionPerformed() extraire la sous-chaine visible du message en fonction de la position courante afficheur:Afficheur Affiche la sous-chaine Incremente « circulairement » la position courante (modulo la « bonne taille ») opt [ position courante = 0 ] : PiloteAfficheur lire le fichier du message Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -5- Programmation Objet et langage Java Diagramme de séquence associé au CU « Arrêter le défilement » buttonStop : Button anonyme22 : ActionListener timer : Timer cliquer actionPerformed() arrêter NOTES ET CONTRAINTES SUR L'AFFICHAGE DU TEXTE Contrairement à un afficheur électronique dont les caractères peuvent être éteints, l’afficheur est ici constitué d’un label devant obligatoirement contenir des caractères. On utilisera les caractères « _ » invisibles dans un label si sa hauteur est ajustée de façon à cacher ce caractère. Le label sera initialisé avec les caractères « _ » ce qui correspond à un afficheur éteint. Nota : on ne peut pas utiliser le caractère space car il provoque un traitement particulier dans un label. Pour faire débuter l’affichage avec la première lettre du texte et faire terminer l’affichage par la dernière lettre du texte, on pré fixe et on post fixe le texte avec la chaine de caractères contenue dans le label de l’afficheur éteint. REALISATION DU DEFILEMENT DETERMINATION DE LA ZONE DE TEXTE A AFFICHER Pour réaliser le défilement du texte dans la fenêtre, il faut extraire du texte la portion correspondant à celle qui apparaitra sur le label. Supposons que le label affiche la portion de texte suivante : « ts longs des violo » lineLength Les sanglots ts longs des violo violons de l’automne....monotone pos displayTextLength pos+displayTextLength lineLength() est la taille totale du texte pré fixée et post fixée avec les caractères « _ » contenu dans label de l’afficheur éteint. Cette quantité est calculée après avoir lue la ligne à afficher. pos est la position de départ du premier caractère affiché. Cette quantité est calculée à chaque défilement. displayTextLength est la taille de la zone d’affichage. Cette quantité déterminée une fois pour toute dans le constructeur du pilote. pos + displayTextLength donne la position du dernier caractère à afficher. La portion de texte à afficher (donc à écrire sur le label) est extrait de la ligne à afficher par : Portion_de_Texte_à_afficher = line.substring(pos, pos + displayTextLength) ; En incrémentant pos, le texte défile sur l’afficheur. Attention : pos doit rester dans l’intervalle [0..(line.length() – displayTextLength)[ ce qui peut être réalisé à l’aide d’une opération modulo : pos = pos % (line.length() – displayTextLength). ANIMATION DU TEXTE La mise à jour de la position du texte dans l’afficheur est réalisée dans l’écouteur d’un timer. private Timer createTimer () { ActionListener action = new ActionListener () { // Position courante dans la chaine à afficher. Initialisé à 0 int pos = 0; // Ecouteur du timer. Appelé par défaut toutes les MIN_TIME ms public void actionPerformed (ActionEvent event) { // Mettre à jour l'afficheur avec la portion de texte à afficher // Incrémenter la position courante (pos) // S’assurer que pos est dans un intervalle valide // Si pos == 0 alors relire le fichier contenant le texte à afficher Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). Programmation Objet et langage Java -6- } }; return new Timer (MIN_TIME, action); } ATTRIBUTS DE LA CLASSE PILOTEAFFICHEUR // Valeur maximale et minimale par défaut de la barre de défilement et barre de défilement private final int MIN_TIME = 100; private final int MAX_TIME = 500; private Scrollbar scrollbarVitesse = null; // Mettre à jour l'attribut nom avec votre nom private String nom = "Corrigé"; // Boutons start et stop private Button buttonStart = null; private Button buttonStop = null; // Bouton permettant de spécifier un fichier private Button buttonFichier = null; // Labels placés aux extrémités supérieures de la barre de défilement private Label labelMin = null; private Label labelMax = null; // Ligne à affichée extraite du fichier private String line = null; // Contrôle FileDialog permettant de spécifier le fichier à lire private FileDialog fileDialog = null; // L'afficheur private Afficheur afficheur = null; // Le Timer private Timer timer = null; // Taille de l'afficheur private int displayTextLength = 0; // Texte présent dans l'afficheur éteint private String lineBlank = null; // Fichier à lire private String file = null; TRAVAIL DEMANDE Nota : si à l’exécution de l’application l’erreur « nul pointer » s’affiche dans la console, c’est que vous n’avez pas initialisé un attribut de type objet (généralement masqué cet attribut en le re-déclarant localement). Créer un projet TP5. Récupérer les fichiers java sur le site de cours (lien éléments fournis). Décompresser l’archive et ... Ajouter les fichiers suivants au projet dans le répertoire src et mettre à jour le projet : JournalDefilant.java Afficheur.java PiloteAfficheur.java 1. Compléter le constructeur 2. Compléter la méthode getScrollbarVitesse() 3. Compléter la méthode getButtonStart() 4. Compléter la méthode getButtonStop() 5. Compléter la méthode getButtonFichier() 6. Compléter la méthode readFile() 7. Compléter la méthode getScrollbarVitesse() 8. Compléter le « timer » 9. Compléter le fichier JournalDefilant.java Tester le bon fonctionnement de votre application. 10. On veut afficher dans l'IHM du responsable, à chaque "tic Horloge", la valeur de la position courante. Comme il n'y a pas d'objet disponible, nous afficherons la valeur décimale dans la zone titre de la fenêtre du PiloteAfficheur grâce à la méthode "setTitle". Dans l'écouteur de Timer, il est possible d'accéder aux méthodes de l'objet conteneur de 2 façons : - soit directement "nom_fonction(param…);" quand il n'y a pas de conflit de nom entre les méthodes de la classe "écouteur" (ici Timer) et celle de la classe "conteneur" (ici PiloteAfficheur). - soit indirectement "Nom_Classe_Conteneur.this.nom_fonction(param…);" quand il y a conflit de nom entre "nom_fonction " de la classe "écouteur" et celle de la classe "conteneur". Afficher la valeur de la position courante en testant les 2 façons expliquées précédemment. Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). Programmation Objet et langage Java -7- 11. Créer un fichier jar exécutable de l’application 12. Bonus : simplifier l’IHM en remplaçant les deux boutons start et stop par un seul bouton remplissant la même fonction que les boutons prétendants. Créer un fichier jar exécutable de l’application. NOTA : les écouteurs sont tous définis dans une classe anonyme. Leur squelette peut être généré par eclipse à l’aide de WindowBuilder (mode design). Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -8- Programmation Objet et langage Java ANNEXE : CODE FOURNI /** * Constructeur du pilote de l'afficheur */ public PiloteAfficheur() { super(); initialize(); // Instancier la classe Afficheur et mémoriser sa référence dans l'attribut afficheur // Rendre visible l'objet afficheur // Instancier l’attribut fileDialog de classe FileDialog : // type LOAD et titrer ce contrôle avec "Fichier ?" (voir cours) // Créér le Timer timer = createTimer(); // Lire le texte contenu dans l'afficheur (label labelTextDefilant) // et le mémoriser dans l'attribut lineBlank) // Mémoriser la taille de l'afficheur (longueur de lineBlank ) dans l'attribut displayTextLength } /** * This method initializes scrollbarVitesse * * @return java.awt.Scrollbar */ private Scrollbar getScrollbarVitesse() { if (scrollbarVitesse == null) { scrollbarVitesse = new Scrollbar(); scrollbarVitesse.setBounds(new Rectangle(14, 138, 238, 22)); // Initialiser la valeur minimale que peut retourner l’objet scrollbarVitesse avec MIN_TIME // Initialiser la valeur minimale que peut retourner l’objet scrollbarVitesse avec MAX_TIME // Définir l'orientation de la barre de défilement avec Scrollbar.HORIZONTAL // Abonner et définir l’écouteur de la barre de défilement (utiliser WindowBuilder) // Dans l’écouteur, règler le timer avec la valeur donnée par la position de la barre de défilement } return scrollbarVitesse; } /** * This method initializes buttonStart * * @return java.awt.Button */ private Button getButtonStart() { if (buttonStart == null) { buttonStart = new Button(); buttonStart.setBounds(new Rectangle(14, 79, 105, 32)); // Désactiver le bouton // Ecrire "Démarrer" sur la label du bouton // Abonner et définir l’écouteur du bouton (utiliser WindowBuilder) // Dans l’écouteur : démarrer le timer } return buttonStart; } /** * This method initializes buttonStop * * @return java.awt.Button */ private Button getButtonStop() { if (buttonStop == null) { buttonStop = new Button(); buttonStop.setBounds(new Rectangle(145, 78, 105, 30)); Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). Programmation Objet et langage Java // Désactiver le bouton // Ecrire "Stop" sur la label du bouton // Abonner et définir l’écouteur du bouton (utiliser WindowBuilder) // Dans l’écouteur : arrêter le timer } return buttonStop; } /** * This method initializes buttonFichier * * @return java.awt.Button */ private Button getButtonFichier() { if (buttonFichier == null) { buttonFichier = new Button(); buttonFichier.setBounds(new Rectangle(14, 37, 238, 30)); buttonFichier.setLabel("Fichier"); buttonFichier.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { // Rendre visible le contrôle fileDialog (il a été instancié dans le constructeur) // A l’aide des méthodes fournies par le contrôle fileDialog, // concaténer dans l’attribut file de type String : // le répertoire et le nom du fichier choisis. // Afficher le nom du fichier sur la barre de titre l'afficheur // Lire le fichier readFile(); // Activer les boutons buttonStart et buttonStop } }); } return buttonFichier; } /** * readFile lit la première ligne du fichier spécifié par l’attribut file * readFile doit capturer les exceptions FileNotFoundException et IOException */ private void readFile() { // Capturer les exceptions // Instancier l'objet bfr pour qu'il pointe sur le fichier spécifier par l’attribut file. // Lire uniquement la première ligne du fichier // Encadrer cette ligne par la chaine lineBlank // Fermer le fichier // Traiter les erreurs // FileNotFoundException // Mettre à jour la barre de titre avec le message "Fichier non trouvé !" // // IOException Mettre à jour la barre de titre avec le message ""Erreur de lecture" } Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -9- Programmation Objet et langage Java /** * This method initializes this * Ne pas modifier * @return void */ private void initialize() { labelMax = new Label(); labelMax.setBounds(new Rectangle(216, 116, 34, 21)); // Incrire dans le label labelMax avec la valeur spécifiée par MAX_TIME (en secondes) labelMin = new Label(); labelMin.setBounds(new Rectangle(16, 114, 37, 23)); // Incrire dans le label labelMin avec la valeur spécifiée par MAX_MIN (en secondes) this.setLayout(null); this.setSize(265, 179); this.setTitle(nom + ": Pilote afficheur défilant"); this.setLocation(new Point(100, 100)); this.add(getScrollbarVitesse(), null); this.add(getButtonStart(), null); this.add(getButtonStop(), null); this.add(getButtonFichier(), null); this.add(labelMin, null); this.add(labelMax, null); this.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { System.exit(0); } }); } // Fonction timer, abonnement et écouteur private Timer createTimer () { ActionListener action = new ActionListener () { // Position courante dans la chaine à afficher. Initialisé à 0 int pos = 0; // Ecouteur du timer. Appelé par défaut toutes les 100 ms public void actionPerformed (ActionEvent event) { // Cette séquence provoque le défilement du texte dans l’afficheur // Calculer la nouvelle position courante du début du texte dans l'afficheur // Ne pas oublier de maintenir pos dans un intervalle acceptable (voir sujet) // Si le pos == 0, relire le fichier } }; return new Timer (MIN_TIME, action); } } // @jve:decl-index=0:visual-constraint="7,25" Daniel Tschirhart & Pierre Sosinski. Edition du 12/12/2012, mise à jour 12/12/12 (TP5NewV5.12.docx). -10-