deutsch     english    français     Imprimer

 

3.13 WIDGETS

 

 

INTRODUCTION

 

Les programmes que vous utilisez d’habitude sont dotés d’une interface utilisateur (GUI = Graphical User Interface). Parmi les éléments graphiques souvent présents, on compte les barres de menu, les champs de saisie ou les boutons. De tels composants graphiques sont appelés widgets et sont considérés comme des objets, tout comme les objets tortues que nous avions manipulés dans le chapitre Objets tortues. Si vous voulez développer un programme avec une interface utilisateur moderne, il est essentiel de connaître et bien comprendre les notions de base de la programmation orientée objets (POO = OOP en anglais, pour object-oriented programming) [plus...Les concepts de POO sont présentés de manière approfondie dans le chapitre Développement de jeux].

Les widgets sont répartis en différentes classes comme le montre la liste ci-dessous. 

Widget Classe
Boutons JButton
Étiquettes JLabel
Champs de texte JTextField
Barres de menu JMenuBar
Éléments de menu JMenuItem
Menu comportant des sous-menus JMenu

De même que l’on avait engendré une tortue en appelant le constructeur de la classe turtle, on doit créer un composant de l’interface utilisateur en appelant le constructeur de la classe idoine. Les constructeurs acceptent souvent des paramètres permettant d’initialiser certaines propriétés du widget. Par exemple, on peut créer un champ de saisie d’une longueur de 10 caractères avec  tf = JTextField(10).

Lors de l’appel du constructeur, il est également nécessaire de définir une variable qui servira ultérieurement à accéder à l’objet créé et retourné par l’appel au constructeur. Le code,  tf.getText() retourne par exemple le texte présent dans le champ de texte référencé par la variable tf.

Pour rendre un widget visible dans le GPanel, on utilise la fonction addComponent() à laquelle on passe la variable référençant le widget à placer. Les widgets sont automatiquement placés dans l’ordre des appels à addComponent() dans la partie supérieure de la fenêtre GPanel [plus...Pour obtenir une disposition plus soignée et plus flexible, il faut faire appel
à un gestionnaire de disposition des composants graphiques (layout manager)
].

CONCEPTS DE PROGRAMMATION: Interface utilisateur graphique, Composant GUI, fonction de rappel

 

 

ESTIMATION DE PI PAR SIMULATION MONTE CARLO

 

Nous avons appris comment déterminer l’aire de surfaces quelconques à l’aide d’une simulation Monte Carlo. Imaginons que l’on dessine un quart de disque de rayon 1 inscrit dans un carré de côté 1. Si l’on fait tomber de manière uniforme n gouttes de pluie sur le carré, on pourra facilement déterminer combien de gouttes tombent à l’intérieur du quart de disque en moyenne. Puisque l’aire du quart de disque est donnée par

  S =  (1)/ 4 * r2* π = (π)/ 4

et que l’aire du carré vaut 1, le nombre de gouttes devrait être égal à

  k = n  *  (π)/ 4  

De ce fait, si l’on fait tomber n gouttes dans une simulation et que k gouttes tombent à l’intérieur du quart de disque, on obtient une approximation de pi avec

  π = (4 * k)/   n  
 

L’interface utilisateur comporte deux étiquettes, deux champs de saisie textuels et un bouton. Une fois créés, on ajoute ces widgets au GPanel à l’aide de addComponent().

Il est clair qu’un clic sur le bouton OK devrait être considéré comme un événement. La fonction de rappel qui sert de gestionnaire pour cet événement de clic est enregistrée par le paramètre actionListener dans le constructeur de JButton. Vous vous rappelez sûrement qu’il ne faut pas placer du code qui s’exécute sur une longue durée à l’intérieur des fonctions de rappel. De ce fait, on ne fait rien d’autre, dans la fonction de rappel, que d’appeler wakeUp() pour réveiller le programme qui avait été endormi dans la boucle while par l’appel à putSleep() dans le but de lancer la simulation.

from gpanel import *
from random import random
from javax.swing import *

def actionCallback(e):
    wakeUp()
    
def createGUI():
    addComponent(lbl1)
    addComponent(tf1)
    addComponent(btn1)
    addComponent(lbl2)
    addComponent(tf2)
    validate()

def init():
    tf2.setText("")
    clear()
    move(0.5, 0.5)
    rectangle(1, 1)
    move(0, 0)
    arc(1, 0, 90)

def doIt(n):
    hits = 0
    for i in range(n):
        zx = random()
        zy = random()
        if zx * zx + zy * zy < 1:
            hits = hits + 1
            setColor("red")
        else:
            setColor("green")
        point(zx, zy)
    return hits

lbl1 = JLabel("Number of drops: ")
lbl2 = JLabel("             PI = ")
tf1 = JTextField(6)
tf2 = JTextField(10)
btn1 = JButton("OK", actionListener = actionCallback)

makeGPanel("Monte Carlo Simulation", -0.1, 1.1, -0.1, 1.1)
createGUI()
tf1.setText("10000")
init()

while True:
    putSleep()
    init()
    n = int(tf1.getText())
    k = doIt(n)
    pi =  4 * k / n
    tf2.setText(str(pi))
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller )

 

 

MEMENTO

 

Les widgets sont des objets de la bibliothèque de classes Swing faisant partie de l’écosystème Java. Ils sont créés par l’appel du constructeur qui porte le même nom que la classe. Lors de l’appel du constructeur, on veille à créer une variable qui permet de référencer l’objet nouvellement créé pour une utilisation future. Pour afficher le widget dans GPanel, il faut appeler la fonction addComponent() en lui fournissant cette variable en tant que paramètre.

Une fois tous les widgets ajoutés au GPanel, il faut appeler la fonction validate()

pour lancer une régénération complète de la fenêtre. Ceci permet de tenir compte des nouveaux widgets insérés en garantissant un résultat tout-à-fait prévisible.

On peut enregistrer des gestionnaires d’événements avec le paramètre nommé actionListener. Il ne faut jamais exécuter du code long à terminer à l’intérieur d’une fonction de rappel.

 

 

LES MENUS (Rien à voir avec la cantine !)

 

De nombreuses fenêtres sont pourvues d’une barre de menu comportant plusieurs éléments de menu (menu items). Lors d’un clic sur un élément de menu, il n’est pas rare qu’un autre sous-menu s’ouvre à son tour, pouvant lui-même comporter plusieurs nouvelles entrées de menu. Les menus et entrées de menu sont également considérés comme des objets qu’il faut créer. La sélection d’une entrée de menu va également générer un événement qui sera traité par un gestionnaire d’événements approprié enregistré au préalable.

On construit une barre de menu en recourant à JMenuBar()et en ajoutant également un sous-menu. Il suffit pour cela de créer un objet JMenu et de lui ajouter un objet JMenuItem. Cela donnera lieu à un menu hiérarchique

Dans le but de simplifier un peu le code, on peut utiliser le même gestionnaire d’événement actionCallback() pour toutes les options du menu en le passant au paramètre actionPerformed lors de chaque appel au constructeur de JMenuItem. Dans le gestionnaire d’événements, on peut déterminer laquelle des entrées du menu a déclenché l’événement en invoquant la méthode getSource() de l’objet événement e reçu par le gestionnaire d’événement..

 
from gpanel import *
from javax.swing import *

def actionCallback(e):
    if e.getSource() == goItem:
        wakeUp()
    if e.getSource() == exitItem:
        dispose()
    if e.getSource() == aboutItem:
        msgDlg("Pyramides Version 1.0")

def doIt():
    clear()
    for i in range(1, 30):
        setColor(getRandomX11Color())
        fillRectangle(i/2, i - 0.35, 30 - i/2, i + 0.35)

fileMenu = JMenu("File")
goItem = JMenuItem("Go", actionPerformed = actionCallback)
exitItem = JMenuItem("Exit", actionPerformed = actionCallback)
fileMenu.add(goItem)
fileMenu.add(exitItem)

aboutItem = JMenuItem("About", actionPerformed = actionCallback)

menuBar = JMenuBar()
menuBar.add(fileMenu)
menuBar.add(aboutItem)

makeGPanel(menuBar, 0, 30, 0, 30)

while not isDisposed():
    putSleep()
    if not isDisposed():
        doIt()
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Rappelez-vous toujours qu’une fonction de rappel telle qu’un gestionnaire d’événements ne devrait jamais exécuter du code qui prend long à se terminer. On effectue de ce fait le dessin dans le programme principal.

Pour s’assurer que le programme se termine avec une certitude absolue lorsque l’utilisateur clique sur le bouton fermer, on utilise isDisposed() pour tester si la fenêtre a été fermée [plus...Lorsque la fenêtre est fermée ou que la fonction dispose() est appelée, la fonction putSleep() se termine et retourne à l’appelant].

 

 

EXERCICES

 

1.


Éditer le programme Moiré du chapitre 3.2 et ajouter une étiquette textuelle, un champ de saisie pour le réglage du délai et un bouton OK. Lors d’un clic sur le bouton OK, l’image sera rafraîchie avec le délai spécifié dans le champ de saisie (en millisecondes).


2.

Éditer le programme de la section « algorithmes élégants d’art filaire » du chapitre 3.8 en y ajoutant le menu suivant : L’option de menu « Options » devrait contenir un sous-menu comportant les options de couleur « Rouge », « Vert », « Bleu ». L’option de menu « Go » devrait lancer le dessin du graphique filaire en utilisant la couleur sélectionnée dans le menu « Options ». Si aucune couleur n’est choisie dans le menu, le dessin sera fait en noir.


3*.

Choisir un de vos graphiques favoris de ce chapitre pour le personnaliser en y ajoutant quelques widgets pour améliorer l’expérience utilisateur (UX = User eXperience).