Etape 11 – Interface graphique complète

1 Introduction

Le but de cette dernière étape est de terminer l'interface graphique — et, du même coup, le projet — en ajoutant la possibilité de modifier les paramètres de la carte isochrone, à savoir l'arrêt, le jour et l'heure de départ. A la fin de cette étape, l'interface de votre programme devrait ressembler à la copie d'écran ci-dessous.

gui-e11.png

Figure 1 : Version finale de l'interface graphique

2 Mise en œuvre Java

La mise en œuvre de cette étape se fait en deux parties :

  1. les paramètres modifiables par l'utilisateur sont transformés en attributs de la classe IsochroneTL et le lien avec l'interface graphique est fait afin que les changements de paramètres provoquent une mise à jour de l'affichage,
  2. un panneau (JPanel) contenant les éléments graphiques permettant la sélection de l'arrêt, de la date et de l'heure de départ est créé puis ajouté comme fils du panneau de contenu de la fenêtre.

2.1 Attributs modifiables

La carte isochrone est paramétrée par trois valeurs : l'arrêt, la date et l'heure de départ. Chaque fois que l'un de ces paramètres change, la carte isochrone doit (éventuellement) être recalculée. Par exemple, lorsque la date change, l'ensemble des services actifs peut éventuellement changer, et si tel est le cas, le graphe, l'arbre des plus courts trajets et finalement la carte doivent être recalculés. Le graphe ci-dessous illustre les dépendances entre les différents éléments qui jouent un rôle dans le calcul de la carte isochrone.

ui-dependencies.png

Figure 2 : Graphe de dépendance

L'interface graphique permet à l'utilisateur de changer l'arrêt, la date et l'heure de départ. Lors d'un changement, les éléments qui dépendent de la (ou les) valeur(s) modifiée(s) doivent être mis à jour.

Pour gérer cela, une solution serait d'appliquer le patron Observer en faisant des trois paramètres sus-mentionnés des attributs observables, et en installant ensuite des observateurs pour mettre à jour les données dépendantes en cas de changement. Par exemple, la date pourrait être observée et, à chaque changement, l'ensemble des services mis à jour.

Nous vous suggérons toutefois d'utiliser ici une solution plus simple étant donné que la totalité du code se trouve dans une seule classe et qu'il n'est donc pas très important de découpler les observateurs des valeurs observées, comme le permet le patron Observer. L'idée de cette solution est de procéder ainsi : pour chaque valeur entrant dans le calcul de la carte isochrone, un champ est ajouté à la classe IsochroneTL. Ensuite, une méthode setter est définie pour chacune des trois valeurs directement modifiable par l'utilisateur (arrêt, date et heure de départ). De plus, une méthode de mise à jour est définie pour chaque valeur intermédiaire (services, graphe, arbre des trajets et carte isochrone). Finalement, chaque méthode modifiant une valeur se charge directement d'appeler la méthode de mise à jour de la ou les valeurs dépendant d'elle.

Par exemple, pour la date de départ, cela signifie qu'un champ la stockant doit être ajouté à la classe IsochroneTL. Pour modifier ce champ, une méthode setter, nommée p.ex. setDate, doit également être fournie. Cette méthode vérifie que la date reçue diffère effectivement de la date actuelle et, dans ce cas seulement, appelle la méthode de mise à jour des services, nommée p.ex. updateServices. Cette méthode updateServices calcule l'ensemble des services actifs pour la date actuelle et, si et seulement si cet ensemble est différent de l'ensemble actuel, appelle la méthode de mise à jour du graphe, nommée p.ex. updateGraph. Et ainsi de suite.

A noter qu'il n'est pas nécessaire de suivre ces instructions à la lettre, car dans certains cas il est possible de simplifier le code en combinant plusieurs méthodes. Par exemple, les méthodes de mise à jour de l'arbre des trajets et de la carte isochrone peuvent être combinées en une seule méthode. D'autres simplifications du même genre sont possibles, l'important étant que seules les valeurs qui doivent absolument être recalculées le soient effectivement. Par exemple, un changement de l'arrêt de départ ne doit en aucun cas provoquer une mise à jour (très coûteuse) du graphe.

2.2 Panneau supérieur

Les éléments graphiques permettant de sélectionner les paramètres de la carte isochrone — arrêt, date et heure de départ — sont à placer dans un nouveau panneau Swing, c-à-d une instance de JPanel. Ce panneau, créé par une méthode privée de la classe IsochroneTL similaire à createCenterPanel, doit avoir un gestionnaire de mise en page de type FlowLayout. Une fois créé et initialisé, le panneau doit être ajouté dans la zone PAGE_START du panneau de contenu de la fenêtre. Pour mémoire, ce dernier s'obtient au moyen de la méthode getContentPane de JFrame.

Le panneau supérieur contient un total de cinq fils qui sont, de gauche à droite :

  1. l'étiquette Départ,
  2. le menu des arrêts de départ possibles,
  3. un mince séparateur (invisible),
  4. l'étiquette Date et heure,
  5. le champ de sélection de la date et de l'heure de départ.

Les différentes classes Swing à utiliser pour chacun de ces fils sont décrites ci-après.

Etiquettes et séparateur

Les étiquettes sont des instances de JLabel initialisées avec le texte qu'elles affichent, ici Départ et Date et heure, respectivement.

Le séparateur est quant à lui une instance de JSeparator. Il est invisible mais occupe une place non nulle, ce qui permet d'espacer le menu des arrêts de départ et l'étiquette Date et heure.

Menu des arrêts

Le menu des arrêts est une instance de JComboBox, un composant générique dont le paramètre de type représente le type des éléments qu'il affiche, ici des arrêts (type Stop). Ce composant possède plusieurs constructeurs dont un prenant un vecteur (de type java.util.Vector) contenant les éléments à afficher dans le menu. C'est celui que nous vous conseillons d'utiliser.

La classe java.util.Vector est très similaire à la classe ArrayList mais plus ancienne. Elle n'est généralement plus utilisée, ArrayList étant mieux conçue, mais on la trouve parfois dans d'anciennes bibliothèques, comme ici.

L'élément sélectionné dans un composant de type JComboBox peut être défini au moyen de la méthode setSelectedItem et obtenu au moyen de la méthode getSelectedItem. Pour être informé d'un changement de cet élément sélectionné, il faut attacher un auditeur de type ActionListener au composant, via sa méthode addActionListener.

Sélection de la date et de l'heure

Le composant permettant de choisir la date et l'heure de départ est une instance de JSpinner. Le constructeur de ce composant prend en argument un modèle de type SpinnerModel décrivant le type de valeur qu'il doit contenir. Ici, étant donné que l'on désire afficher une date et une heure, il faut passer une instance de SpinnerDateModel.

Pour être averti d'un changement de la date et/ou de l'heure, il faut attacher un auditeur de type ChangeListener au modèle (et pas au composant directement), au moyen de sa méthode addChangeListener. La date et l'heure du modèle peuvent être obtenues au moyen de la méthode getDate, qui les retourne sous la forme d'une instance de java.util.Date.

2.3 Adaptation de la date et de l'heure

Comme cela a été expliqué dans l'énoncé de l'étape 2, les entreprises de transports en commun expriment généralement leurs horaires en termes de jour de circulation, qui ne commence pas à minuit comme un jour civil mais plutôt aux alentours de 3-4 h du matin. Etant donné qu'aucun transport ne circule à ces heures — ce qui n'est pas le cas aux alentours de minuit — cette solution permet de garantir que toutes les courses partent et arrivent le même jour.

Pour tenir compte de cela lors du dessin des cartes isochrones, il faut adapter les dates et les heures données par l'utilisateur dans la période allant de minuit à 4 heures du matin. Un tel couple date/heure doit être transformé pour que la date soit celle du jour d'avant, et l'heure comprise entre 24 et 28h.

Attention, cette adaptation ne doit pas être visible à l'utilisateur, dans le sens où il doit pouvoir choisir p.ex. de visualiser une carte isochrone pour un départ à 2h38 le 2 octobre 2013. Par contre, dans ce cas, la date utilisée pour le calcul de la carte doit être le 1er octobre, et l'heure 26h38.

Une solution pour mettre en œuvre cette adaptation est de faire une unique méthode setter pour la date et l'heure de départ, et de faire l'adaptation à ce moment.

3 Résumé

Pour cette étape, vous devez :

  1. Modifier la classe IsochroneTL pour y ajouter les méthodes de modification des paramètres et de mise à jour des valeurs qui en dépendent.
  2. Ecrire la méthode de création du panneau supérieur et faire en sorte que la modification des valeurs dans l'interface provoque la modification des paramètres de la carte isochrone.
  3. Tester votre programme.