Interfaces Homme Ordinateur

Download Report

Transcript Interfaces Homme Ordinateur

Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
A l'attention des étudiants de
l'ESIL-Info.
Auteur : Laurent Henocque
mis à jour en Février 2008
Licence Creative Commons
Cette création est mise à disposition selon le
Contrat Paternité-Partage des Conditions
Initiales à l'Identique 2.0 France disponible en
ligne
http://creativecommons.org/licenses/bysa/2.0/fr/
ou par courrier postal à Creative Commons, 559
Nathan Abbott Way, Stanford, California
94305, USA.
Situation
• Aucun langage dédié n'existe pour la spécification
•
•
homme machine
Les interfaces homme machine sont essentiellement
spécifiées par prototypage
Cette approche n'offre aucun secours dans le cas de
grands projets comportant des dizaines ou des
centaines d'écrans
• Il existe un réel besoin de support pour la
spécification des IHM
Cas du Prototypage
• Un prototype peut être présenté au client et validé.
• Le prototype montre que ce qui est développé est
•
compatible avec le besoin
Le prototype ne peut pas illustrer des OBJECTIFS de
développement - d'ergonomie notamment.
• Il manque alors un langage pour décrire CE QUE l'on
veut implanter, avant de le faire effectivement.
Rendre une spécification d'IHM exécutable
• Il est possible de développer une bibliothèque
logicielle permettant de rendre exécutables des
spécifications d'états
• Cela a été réalisé deux fois au moins : dans le
produit Ilog Views, après un premier projet appelé
Open Side
– Open Side (1991) : le langage des contextes, intégré au
langage WIZ.
– Ilog Views (1995) : intégration d'une API de gestion d'états
dans l'outil ILOG Views
– Aujourd’hui “JavaStates”
ISL
ISL = Interface State Language
• un langage pour décrire la dynamique des interfaces
•
•
homme machine.
ISL est fondé sur une extension du langage de
spécification d'états défini par la méthode UML
ISL peut être rendu exécutable - mais peut être
utilisé comme un langage
La dynamique "interne" des IHM
• ISL décrit la dynamique interne des interfaces:
• Les actions et changements qui n'appellent pas des
fonctions externes, mais sont limitées à l'interface elle
même
• Par exemple, une interface cache ou dévoile
fréquemment des fenêtres, change l'aspect de boutons,
positionne ou enlève des callbacks sur des objets
Transitions
• En ISL, une interface est décrite par ses états possibles
• La dynamique interne est définie par des changements
d'états, appelés transitions
• Ces transitions d'état sont causées par des callbacks
appliqués statiquement aux objets de l'interface
• La fraction de la dynamique de l'interface qui relève de
changements d'états est ainsi décrite de façon statique
Bénéfices attendus
• la spécification exacte de l'interface, en garantissant
l'absence de défauts d'aspects
• le support d'abstractions utiles lors de projets réalisés
en équipe
• accroître la dynamique et les qualités ergonomiques
d'une interface à moindre coût
• automatiser l'automate d'interfaces très complexes
Brève introduction aux diagrammes
d'état de UML
Interfaces et Etats
• Les interfaces graphiques, et plus généralement
toutes les sortes d'interfaces homme machine sont
bien décrites autour de la notion d'état
– On peut laisser son ordinateur allumé pendant des semaines
et espérer le retrouver dans le même état en revenant
– Par contre, la prise en compte d'une entrée sera en
apparence instantanée dans la plupart des cas
UML
• http://www.sparxsystems.com.au/resources/uml2_tutorial
/uml2_statediagram.html
• Object Management Group (OMG)
http://www.omg.org
• OMG UML Resource Page
http://www.omg.org/technology/uml
• Object Mentor Inc. - information on UML Design Principles
http://www.objectmentor.com
• UML Forum - Public forum for the UML modeling
community
http://www.uml-forum.com
Etats - Evénements - Transitions
• l'entrée est appelée un événement
• le changement d'état en réponse à un événement est une
•
transition
l'événement comme le changement d'état sont réputés
avoir une durée nulle ou négligeable, mais un état peut
durer indéfiniment
Etat composite à deux sous états
Etats élémentaires
Pseudo états
Concurrence
Points d'entrée et de sortie
Automate à Protocole
Pré et post conditions dans les
protocoles
Points d'entrée/sortie dans les
composites
Jonctions
Décisions
“Self” Transitions
Composite caché
Exemples de régions
Exemple de sous diagramme
Exemple
Variante
Exemple
Invariant d'état
Exemple du téléphone
Exemple
Exemple avec extension
Exercices
• Dessiner le diagramme d’états de l’automate d’un bouton
dans une IHM
• Dessiner le diagramme d’états de l’automate d’une
application de type « éditeur de texte »
Concepts fondateurs de ISL
Descripteur d'état
• ISL permet de nommer les états d'une interface, et
ainsi de définir explicitement l'automate d'états finis
que l'interface réalise
• A chaque état sont associés autant de descripteurs
que nécessaire
• Un descripteur est la spécification d'une
certaine ressource graphique (couleur,
position, visibilité...) où de comportement
(callback, réponse à un événement) pour un
objet donné
Détection VS Sélection d'état
• ISL ne requiert pas de détecter dans quel état se
trouve une interface, mais permet de définir un état
courant
– if / switch - case etc...
• Tous les descripteurs d'un état sont pris en compte
lorsque cet état est sélectionné, et donc les
changements correspondants effectués
– analogue dynamique des classes et des fonctions virtuelles
Implantation des transitions
• Une implantation d'ISL peut effectuer des calculs une
fois pour toutes lorsqu'un état donné est sélectionné.
Par la suite, aucun programme ne devra tester dans
quel état l'interface se trouve
• En reposant sur une implantation de ISL, une
interface ne teste jamais si elle se trouve dans un
état compatible avec la gestion d'un événement, et
ne teste pas non plus quelle est la transition
appropriée
• La transition appropriée a été installée sous forme
d'un callback adéquat lors du changement d'état
Descripteurs de transitions
• Parmi les descripteurs dynamiques figurent "setState"
et "leaveState", qui permettent des transitions
explicites
• ISL suppose que l'intégrité des états n'est jamais
violée : quelle que soit la manière d'y parvenir, un
état se manifeste et se comporte exactement comme
spécifié
• Lorsqu'on sort d'un état, les attributs (visuels et
callbacks) des objets qui étaient modifiés par cet état
sont remis à leur valeur d'origine, sauf si l'état cible
exprime une condition distincte
Exemple
• Trois objets, 1, 2 et 3
• Nous sommes dans un état A ayant les descripteurs
1 et 2 (3 non mentionné reste bleu).
• On passe dans un état B décrit par 2, 3.
• ISL suppose que 1 est régénéré, donc l'état atteint
est 1, 2, 3
• Si maintenant on quitte l'état B, on retrouve 1, 2, 3
Objets à leur création
• On crée trois objets colorés. Chaque instance reçoit
une couleur par défaut.
2
1
global
A
3
B
Etat global (racine)
• L'interface définit un état global, qui définit pour les
différents objets une valeur particulière de l'attribut
couleur
2
1
global
A
3
B
On passe dans l'état A
• Descripteurs 1 et 2 (3 non mentionné reste bleu)
2
1
global
A
3
B
On passe dans l'état B
• Décrit par 2, 3. 1 est remis dans son état d'origine
2
1
global
A
3
B
On quitte l'état B
• Les objets reprennent leurs attributs d'origine
2
1
global
A
3
B
Changement d'état: trois cas
Etat B
Etat A
p1:invisible
p1:visible
b1:actif
b1:rouge
Lors du passage de A vers B :
•la couleur de b1 est mémorisée avant d'être modifiée,
•l'état d'activité de b1 est remis à sa valeur anciennement
mémorisée,
•et la visibilité de p1 est seulement modifiée
Etat cible seul
• Une ressource n'est contrainte que dans l'état cible:
• Sa valeur avant le changement doit être
•
sauvegardée.
La nouvelle valeur est installée
Etat source et état cible
• Une ressource est contrainte dans les deux états:
source et cible
• La valeur mémorisée est conservée
• La valeur courante est modifiée conformément à
l'état cible
Etat source seul
• Une ressource n'est mentionnée que dans l'état
source
• Sa valeur doit être restaurée à la valeur mémorisée.
Optimisations des implantations
• Ce mécanisme peut être optimisé de diverses
manières :
• élimination de scintillements par l'utilisation du
double buffering
• gestion intelligente des attributs de visibilité
• réduction au minimum des interventions sur
l'interface, par des calculs ensemblistes reposant sur
des tables de hachage
Notions nécessaires
• Comme les diagrammes d'état de UML, ISL exploite
le concept d'états composites et de leurs sous états
• Egalement, le parallélisme d'ensembles d'états est
une notion nécessaire à la spécification des interfaces
homme machine
Visibilité
• En ISL, on ne décrit jamais un changement de
visibilité d'une fenêtre par un appel explicite aux
fonctions "show()" et "hide()"
– L'attribut de visibilité est positionné par des descripteurs
associés à des états
• Ainsi en changeant d'état on décrit implicitement les
modifications associées de visibilité s'il y a lieu
• Au lieu de décrire par un programme les effets d'un
changement d'état implicite, on décrit statiquement
et explicitement les états par des invariants
Les causes des défauts d'aspect
• Le bénéfice de cette approche apparaît clairement
quand un programmeur doit décider de ce qui doit
arriver quand un changement d'état se produit, et
qu'il y a beaucoup d'états, et beaucoup de ressources
à modifier en conséquence
– est ce que cette fenêtre doit rester visible?
• La difficulté de cette simple question est la cause
majeure de coûts de développement accrus, et de
manque d'ergonomie
Références
• ISL repose sur les state charts de David Harel (article
fondateur David Harel: On Visual Formalisms. CACM
31(5): 514-530 (1988),
• http://sunsite.informatik.rwthaachen.de/dblp/db/indices/atree/h/Harel:David.html),
• popularisé par UML (Unified Modeling Language,
http://www.rational.com/uml)
• un bon site en français : http://uml.free.fr
Extensions à UML
• la notion de descripteurs d'états (qui ne sont pas
•
exactement atteints par les variables d'états)
les sous ensembles d'états concurrents
nommés
• une gestion des espaces de noms permettant d'isoler
des symboles potentiellement en conflit (comme avec
les packages)
• la possibilité de rendre actif un état composite sans
activer aucun de ses sous états
– c'est utile pour la spécification de dialogues et n'est pas
incompatible avec le modèle de David Harel : chaque
Spécifications des IHM à l'aide
d'états
ISL
Descripteurs
• Un descripteur est un invariant portant sur la valeur
qu'une propriété doit avoir dans un état particulier
• "propriété" doit être pris au sens large : attribut
visuel (visibilité, couleur, ...), dynamique (callback ou
listener, réaction à événement, interacteur,
accélérateur clavier, etc...), mais aussi donnée
membre d'une classe, variable globale etc...
• Les descripteurs ne portent que sur des objets
nommés
Syntaxe
• La syntaxe pour les descripteurs est simple :
object "name" "resource" = "value" ;
• sauf ambiguïté, les guillemets, le signe égal et le
point virgule sont optionnels
Hiérarchies d'objets
• Les interfaces font toujours apparaître des objets dans d'autres
objets (par exemple dans les frames, ou dans les menus)
• On utilise soit une notation pointée :
object "name0.name1.name2" "resource" = "value" ;
• soit une notation imbriquée
object "name0" {
object "name1" {
object "name2" "resource" = "value" ;
}
}
Alias
• Dans de nombreux cas, la lisibilité des spécifications
peut être améliorée si l'on identifie les objets
graphiques par leur nom et leur type.
• Ainsi, "JFrame", "JPanel", "XmPuxhButton" sont des
alias possibles au mot cle "object", mais également
leurs versions indépendantes des bibliothèques
"frame", "panel", "button" etc...
• Egalement, les versions françaises de ces mots sont
utilisables : "fenêtre", "panneau", "bouton" etc...
Exemples de descripteurs
panel panel1 object button1 background = blue;
panel panel1 object button1 label = "a label with
spaces and a newline
in it";
panel panel2 visible = true;
panel panel3 object MenuBar.File.Open callback =
OpenFile(); // a menu item
Le contexte
• L'objet le plus récemment spécifié sert de
•
contexte pour tous les descripteurs suivants
Exemples :
panel panel1
visibility = true; // appliqué à panel1
background = blue; // également
object button1
foreground = red; //panel1.button1
callback = quit();// également
object button2
foreground= blue; //panel1.button2
Le contexte (2)
• Les mots clé identifiant des objets de haut niveau
(panel, frame, etc...) sont non enchâssés par défaut
• En cas d'ambiguïté, on utilise des accolades
panel panel1 {
visibility = true; // appliqué à panel1
background = blue; // également
panel panel2
foreground = red; //panel1. panel2
}
panel panel3 background = green;
Etats
• Un état est un conteneur pour des descripteurs et
d'autres états appelés ses sous états
• Quand un état devient actif, tous ses sous
descripteurs deviennent "vrais" : l'interface les
implémente
• Les valeurs des attributs avant le changement
doivent être mémorisées pour en permettre la
restauration ultérieure
Syntaxe
• La syntaxe des états est la suivante:
state "nom d'état" {
liste de descripteurs et de sous états
}
• Les accolades sont obligatoires, même si l'état ne
contient ni descripteur ni sous état (ce qui est un cas
valide)
Exemple
state A
{
panel MyMainWindow
visibility = true;
object ButtonQuit
visibility = true;
callback = pleaseLeaveSoftware();
}
Sous états
• Un sous état est seulement une déclaration d'état
imbriquée, avec ses propres descripteurs et sous
états
• Les sous états héritent des descripteurs de leurs
super états
• Un descripteur peut être surchargé par un sous état
Syntaxe imbriquée
state A {
... // descripteurs de A
state B_in_A {
... // descripteurs de B_in_A
state C_in_B_in_A {
... // descripteurs de C_in_B_in_A
}
}
state D_in_A
{
... // descripteurs de D_in_A
}
}
Syntaxe extraite
• Il peut être difficile de maintenir des spécifications
faisant apparaître un trop grand nombre
d'imbrications d'états.
• Egalement, il est parfois nécessaire de décrire des
fragments d'une spécification dans des fichiers
séparés, ce qui exclut la syntaxe imbriquée
state
state
state
state
A { ... }
B_in_A : A { ... }
C_in_B_in_A : B_in_A { ... }
D_in_A : A { ... }
Héritage
• Les descripteurs d'un état sont hérités par tous les
sous états, et peuvent être localement surchargés
state main {
panel "main panel"
background = black;
visibility = true;
}
state derived : main {
panel "main panel"
// background = black (hérité)
visibility = false; // surchargé
}
Exercices
• Décrire en ISL les états d'un bouton
• Décrire les états d'une application à deux fenêtres,
dont l'une est une fenêtre principale, et l'autre une
fenêtre d'impression par exemple
La concurrence
• Par défaut, les sous états sont exclusifs
• Les interfaces modernes font intervenir beaucoup de
parallélisme, allant jusqu'à la mise en ouvre de threads pour les
fonctions interfacées (compilation, édition de liens, exécution du
programme compilé etc...)
• Par exemple, l'affichage d'une aide, ou le choix d'une langue
pour les labels, sont indépendantes des autres fonctions d'une
interface, et correspondent à des états concurrents
Notation pour la concurrence
• Par défaut, tous les sous états d'un état sont
mutuellement exclusifs : au plus un est actif à la fois
• Pour décrire le parallélisme, il est nécessaire de
permettre l'existence simultanée de plusieurs groupes
de sous états, concurrents entre les groupes, mais
exclusifs en leur sein
• Ces groupes s'appellent des "sous régions" en UML
(subregions)
Syntaxe
• state child : parent [ subregion name ] { ... }
Cette déclaration définit "child" comme un sous état de
"parent", figurant dans la sous région "name"
• state child : parent { ... } est une abréviation de
• state child : parent [ subregion default ] { ... }
Déclaration préalable des régions
• Une spécification peut déclarer les sous régions (et
permettre ainsi certaines vérifications automatiques si
l'on dispose d'un outil)
state A {
...
subregion A_1
subregion A_2
subregion A_3
}
Sous régions : Exemple
• Chaque sous région est indépendante (concurrente)
•
des autres
Les états de chaque sous région sont mutuellement
exclusifs
state global {
subregion locale // definitions de language
subregion functionality // états fonctionnels
subregion help // aide
}
Conflits de concurrence
• C'est une erreur que deux états concurrents
formulent des descripteurs incompatibles
state root { ... }
state A : root [functionality] {
panel panel1 visibility = true;
}
state B : root [ help ] {
panel helpPanel visibility = true; // ok
panel panel1 visibility = false; // erreur
}
• Deux descripteurs sont incompatibles seulement s'ils
sélectionnent des valeurs différentes pour le même
attribut du même objet
Commentaires
• Comme les états A et B peuvent être actifs
•
•
•
simultanément, la spécification est inconséquente
(une implantation ferait varier l'état de l'interface
selon l'ordre de prise en compte des états)
Le parallélisme n'a de sens que pour des descripteurs
portant sur des données séparées
Toutefois, données séparées ne signifie pas objets
distincts. Il peut exister du parallélisme intra objet.
Par exemple, deux états concurrents peuvent agir
l'un sur la couleur de fond, et l'autre sur le texte du
label
Etat Initial
• Chaque sous groupe d'un état possède un état initial.
En UML cet état initial est décrit
par une pastille noire
• Par défaut, l'état initial ne possède ni descripteurs, ni
sous états (il est "vide"), et il s'appelle "initial"
• Lorsque une interface est placée dans un état donné,
tous les états initiaux de tous ses sous groupes sont
activés récursivement.
Syntaxe
• On peut spécifier un état particulier comme état
initial d'un sous groupe donné
state A {
subregion A_1 initial B;
...
}
• Il y a au plus un état initial par sous groupe. Si la
spécification définit un état initial, elle doit recourir à
la déclaration préalable du sous groupe
correspondant
Rôle des états initiaux
• Cette fonctionnalité est utile comme une abstraction.
Un état appelé "impression" peut être utilisé par
l'ensemble d'une équipe, ou identifié tôt dans le
processus de spécification, sans préjuger de son
implantation finale.
• On peut aussi admettre, à l'instar des fichiers de
défaults de XWindow, qu'un utilisateur avancé puisse
changer des états initiaux d'une interface qu'il utilise
souvent
Exemple
state abstract_global {
subregion "default" initial concreteB1
subregion "help" initial abstractB2
// default subregion
state concreteA1 {...}
state concreteB1 {...}
// help subregion
state concreteA2 [ help ] {...}
state abstractB2 [ help ] {
subregion "default" initial concrete_deep_B
state concrete_deep_A{...}
state concrete_deep_B{...}
}
}
• En activant abstract_global, on active en fait
concreteB1 et parallèlement concrete_deep_B
Commentaires
• Le concepteur d'un état peut contrôler l'établissement
automatique d'un sous état
• Les autres parties de la spécification peuvent
connaître l'abstraction en ignorant les détails de la
spécification
• Le concepteur de l'abstraction a une totale liberté
pour définir la structure réelle de l'abstraction
Espaces de noms
• Les états initiaux permettent de facilement séparer le
travail de spécification entre plusieurs ingénieurs.
• Le besoin d'éviter des conflits de noms apparaît
spontanément dans ce cadre.
• ISL permet une gestion explicite des espaces de
noms au moyen d'états "packages"
state package rootstate {
...
}
Règles de nommage
• L'état racine d'une spécification est par défaut un
package
• Les noms d'états sont uniques dans leur package
• Désigner un état ambigu se fait en utilisant son nom
de package en préfixe (package::state).
• Dans le cas où les noms de packages ne sont pas
uniques dans la spécification, on utilise la suite
complète des packages qui le contiennent
(package1::... packagen::state)
Désigner les états
• Quand un état est désigné dans un descripteur par
un nom simple, dans le contexte d'un package
donné, l'état portant ce nom est cherché dans ce
package exclusivement
• Dans tous les autres cas, il convient d'employer une
notation non ambiguë mentionnant les noms de
packages de façon explicite
Les callbacks setState et leaveState
• ISL prévoit deux callbacks prédéfinis pour les
changements d'états
• Les fonctions correspondantes sont utilisées comme
"valeurs" de descripteurs de callbacks
• exemple
... object buttonOK
callback = setState(astate);
... object buttonCancel
callback = leaveState(current);
Définitions
• setState positionne l'état courant
– ce n'est pas une erreur si cet état est déjà actif
– si c'est le cas, les sous états actifs sont abandonnés, et l'oin
procède comme pour le premier établissement de l'état (i.e.
relativement aux états initiaux)
• leaveState abandonne son état argument, à condition
qu'il soit actif
– leaveState abandonne également tous les sous états
– leaveState réactive l'état initial du groupe auquel l'état
abandonné appartient
Set Vs Leave
• Quand B est un sous état de A, quitter B n'est pas
équivalent à positionner A.
• Quand on fait "set" (A), on réactive tous les sous
états initiaux de A
• Quand on fait "leave" (B), on ne réactive que le seul
état initial du sous groupe de A auquel appartient B
• LeaveState est donc une facilité pour désactiver
sélectivement un sous groupe donné
Exemple
state A {
panel panel1 object button1
callback = setState(B);
...
state B
{
panel panel1 object buttonOk
callback = setState(A);
panel panel1 object buttonCancel
callback = leaveState(B);
...
}
}
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
Des détails
Etats composites : un support pour
l'abstraction
• Il est souvent utile de considérer des états comme
des abstractions pour d'autres
• Par exemple, le mode MoveResize d'une interface
• On peut définir un état "MoveResize", abstraction
pour plusieurs sous états comme : "NothingSelected",
"ButtonDownInsideObject", "
ButtonDownInsideResizingBox ".
• Certaines caractéristiques de l'interface son
communes à ces trois états (icône, transitions vers
d'autres états etc...)
Factoriser les descripteurs
• L'ensemble des descripteurs communs à des états
voisins peuvent être adéquatement groupés dans un
super état abstrait
• L'utilisation d'états initiaux permet de garantir qu'un
tel état abstrait ne soit jamais actif isolément
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
Exemples :
Définir une interface avec des états
Objectif : exemple 1
• On imagine l'interface d'un éditeur simplifié.
• Il a pour fonctions l'édition bien sûr, et aussi la
sauvegarde, l'ouverture de fichier, l'impression et la
recherche dans le texte appelées:
<edit>, <load>, <save>, <find> et <print>.
Analyse du module fonctionnel
• <edit> est une fonction interne de l'interface, ainsi
que <find>. Les fonctions <save>, <load>, et
<print> sont des appels au système, qui est perçu
comme le module fonctionnel interfacé
• Il n'y a pas de protocole particulier pour <save>,
<load>, et <print>.
• Ces trois opérations peuvent être effectuées dans
n'importe quel ordre
– (on ignore le fait que la sauvegarde puisse dans un
programme intelligent rester impossible tant que rien n'a été
édité)
Identifier les fenêtres principales
• L'application a un panneau principal appelé
"mainPanel".
• On y trouve la barre de menu et la zone de travail
• <save> et <load> partagent une fenêtre de sélection
de fichier appelée "fileSelector".
• <print> prend ses paramètres grâce à un panneau
"printParameters"
• <find> requiert un panneau "findPanel".
Concevoir l'automate
• <edit> est concurrente avec le reste des fonctions
• <find> n'a pas d'impact sur les données, et est
•
également indépendante des autres fonctions
on décide également que <save> et <print> sont
concurrentes entre elles
– leurs fenêtres caractéristiques peuvent être simultanément
visibles
• <load> est exclusive de <save> et <print>.
– quand l'utilisateur initié un dialogue <load>, les dialogues
<print> et <save> disparaissent s'ils étaient visibles.
– <load> et <save> étant exclusifs, ces états peuvent
partager la même fenêtre de sélection de fichiers.
Développer la structure générale des états
state package editor {
// the default subregion is kept for standard
editor functionalities
panel mainPanel visible = true
object menuBar.File.Open callback =
setState(load);
object menuBar.File.Save callback =
setState(save);
object menuBar.File.Print callback =
setState(print);
object menuBar.Edit.Find callback =
setState(find);
panel fileSelector
visible = false
panel printParameters visible = false
panel findPanel visible = false
}
<Find>, <Load>
state package editor { ...
subregion find_subregion;
//////////////////////////////////
state find [ find_subregion ] {
panel findPanel visible = true
object close callback =
leaveState(find);
object search callback
= doSearch();
}
subregion lps_subregion;
//////////////////////////////////
state load : editor [ lps_subregion ] {
panel fileSelector visible = true
object close callback =
leaveState(load);
object ok callback =
loadFromFile();
saveOrPrint (abstraction)
state saveOrPrint:editor [ lps_subregion ]
{
// default subregion not used,
abstract state
state save [ save_subregion ]
{
panel fileSelector
visible = true
object close
callback = leaveState(save);
object ok
callback = saveToFile();
}
state print [ print_subregion ]
{
panel printParameters visible
= true
object close callback =
leaveState(print);
object ok callback =
print();
}
}
Commentaires
• L'exemple à ce stade illustre de quelle manière des
états abstraits peuvent être utilisés pour grouper des
informations communes à plusieurs sous états
• En particulier, cela sert à définir une fois pour toutes
de valeurs pas défaut, chaque sous état étant défini
par différence avec ses super états
• Il est d'usage que l'état racine soit qualifié de
"package" pour signifier qu'il constitue un espace de
noms
Objectif : exemple 2
• Vous êtes le concepteur interface pour la commande
d'une locomotive à vapeur
• La spécification doit décrire la dynamique de cette
interface.
• Certains états sont concrets, et ont pour vocation
d'être activés
• D'autres sont abstraits et groupent des descripteurs
partagés sans avoir pour vocation à être activés
isolément
Hypothèses
•
•
•
•
•
•
•
La machine peut être garée (à l'arrêt moteur froid)
à l'arrêt,
chaudière en chauffe,
chaudière chaude,
en mouvement
chaudière en cours de refroidissement
chaudière froide
Global
state global {
// abstract
// spécifie la barre de menus
// les callbacks de menus sont positionnés
// mais insensitifs
initial Parked;
panel mainControlPanel visibility = true;
object MenuBar.Engine.Operate
callback = setState(InOperation);
sensitivity = false;
object MenuBar.Engine.Park
callback = setState(Parked);
sensitivity = false;
panel mainEnginePanel visibility = false;
}
Parked
C'est l'état initial
state Parked : global {
panel mainControlPanel
object MenuBar.Engine.Operate sensitivity =
true;
}
InOperation
state InOperation : global { // abstract
initial BoilerHeatingAndStopped;
panel mainEnginePanel
visibility = true;
object StartButton visibility = false;
object StopButton visibility = false;
object TemperatureHotEnough
visibility = false;
object TemperatureColdEnough
visibility = false;
object CoolBoiler visibility = false;
object Quit
visibility = false;
}
BoilerHeatingAndStopped
state BoilerHeatingAndStopped:InOperation
{
// le premier état atteint lors de
// la mise en opération
panel mainEnginePanel
object TemperatureHotEnough
visibility = true;
callback = setState(BoilerHot);
}
BoilerHot
state BoilerHot:InOperation { // abstract
initial Stopped;
state Stopped {
// on peut démarrer
// ou refroidir
panel mainEnginePanel
object CoolBoiler
visibility = true;
callback = setState(BoilerCooling);
object StartButton
visibility = true;
callback = setState(Running);
}
}
Running
state Running:BoilerHot {
// la seule transition possible
// est vers "stopped"
panel mainEnginePanel
object StartButton
visibility = false;
}
object StopButton
visibility = true;
callback = setState(Stopped);
}
}
BoilerCooling
state BoilerCooling:inOperation {
// on attend que la température soit assez
// basse pour garer la locomotive
// la décision est sous contrôle de
// l'utilisateur (click "ok, cold enough")
panel mainEnginePanel
object TemperatureColdEnough
visibility = true
callback = setState(BoilerCold);
}
BoilerCold:InOperation
state BoilerCold:InOperation {
// la machine peut être garée
panel mainControlPanel
object MenuBar.Engine.Park
sensitivity =
true;
panel mainEnginePanel
visibility = true
object Quit callback =
setState(Parked);
}
Combiner macro et micro automates
• ISL est utile pour décrire des états de haut niveua
•
•
dans une IHM, comme ceux liés aux visibilités de
fenêtres, aux callbacks,etc...
Les fonctionnalités d'une machine d'états finis
peuvent être également utilisées pour décrire des
automates plus élémentaires : sélection,
dimensionnement, déplacement d'objets 2D...
Tous les aspects de la dynamique interne d'une IHM
peuvent être traduits en terme d'automates d'états
finis (généralement, on utilise des composants
prédéfinis qui gèrent déjà en interne ces automates)
Efficacité
• Une implantation de ISL peut être efficace
• quand on quitte un état A pour un état B, on reste
dans un état commun le plus spécifique C
• l'algorithme pour gérer le changement d'état peut
être linéaire sur le nombre de spécifications d'état
applicables à B et A qui ne sont pas héritées de C
Applications
• Des changements subtils dans l'interface peuvent
être modélisés au moyen d'états sans perdre
d'efficacité
• L'algorithme ne changera pas les performances
générales du programme, car il ne fait que ce qui est
strictement nécessaire, sans surcoût
Améliorations de performances
• Il est même possible qu'une implantation de ISL
conduise à des programmes plus efficaces
• Cela est dû au fait que le programme de changement
d'état ne teste jamais l'état courant pour
déterminer le traitement à réaliser
• Les callbacks peuvent être installés dynamiquement,
et changer pour le même objet dans des états
distincts
Callbacks
• ISL gère les callbacks en installant et désinstallant les
pointeurs vers les fonctions dynamiquement.
• Contrairement à un programme standard, les
callbacks ne commencent jamais par des batteries de
tests
Classes vs Etats
• La différence entre classes et états est minime
• L'état d'un objet peut changer, mais pas sa classe
• Cette nuance est due à des raisons techniques,
l'implantation des objets reposant sur des concepts
comparables aux "struct" C
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
Eléments de méthodologie
Que doit on faire?
• utiliser des schémas d'interaction bien connus, et
•
singer les interfaces que l'on préfère
définir des standards de spécification/conception
d'interfaces que les programmeurs doivent respecter
– par exemple : le dialogue de confirmation
• Lier précisément les artéfacts visuels et la
sémantique :
– le même "signe" (la couleur "rouge" d'un bouton par
exemple) doit avoir une signification constante dans
l'interface
• préférer le Undo à la confirmation
• utiliser les états
Que doit on éviter?
• de court circuiter les états avec des callbacks ayant
des effets de bord
• d' utiliser plus qu'un nombre limité (trois) de signes
visuels percutants dans une fenêtre
• d'utiliser trop de couleurs
• de faire bouger les objets
• de faire une interface dont la moyenne des couleurs
soit trop éloignée du gris
Que peut on raisonnablement faire?
• réutiliser la même fenêtre dans plusieurs états
exclusifs
• par exemple : la fenêtre de sélection de fichiers
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
Processus
Processus de spécification
• ISL ne modifie pas les phases amont de la
spécification d'une interface
• Il permet de décrire le comportement des panneuax
de l'interface sans programmer
• La spécification des panneaux demeure une tâche
fondamentale
• Nous proposons un processus simplifié de mise en
œuvre de la méthode
Décrire le protocole applicatif
• Le module fonctionnel interfacé obéit à un protocole :
les appels de fonctions ne sont pas libres et
indépendants (comme pour une pile)
• L'interface aura pour rôle de donner accès à ce(s)
module(s) fonctionnel(s) en guidant l'utilisateur ou en
évitant ses erreurs
• La première chose à faire est de décrire l'automate
des modules fonctionnels (exemple winamp)
Style de programmation
• Un module fonctionnel ne corrige généralement pas les erreurs
(programmation dite de type "défensif") mieux que par le
lancement d'exceptions (qui normalement arrêtent l'exécution)
• Le style de programmation est dit "généreux" (generous) : une
erreur d'interaction (mauvais moment d'appel, mauvais
paramètre) provoque un arrêt brutal.
• Il est déraisonnable de dupliquer la logique de
filtrage/correction d'erreurs dans l'interface et dans le module
fonctionnel.
Machine d'états du module
fonctionnel
• On décrit un protocole avec des diagrammes d'états
UML
– évènements <-> appels de fonction
• Cet automate sert de guide pour la spécification de
l'automate de l'interface
– arguments de fonctions = données saisies dans l'interface
Sémantique de l'interface
• Certaines des fonctions d'un module fonctionnel sont
essentielles, d'autres plus auxiliaires
– les fonctions essentielles sont celles aui correspondent à la
raison d'être du programme
– ex : login sur db / requête sur db
• Identifier ces fonctions c'est isoler la "sémantique" de
l'interface
Définir les panneaux principaux
• C'est la première étape
• Leur contenu dépend du protocole fonctionnel, et des
données à fournir en paramètre
• Les choix faits pour les gadgets reposent sur la
charte graphique
Définir l'automate global
• Après la spécification des panneaux vient la
spécification des états principaux de l'interface.
• Les états sont identifiés, hiérarchisés, et la question
de leur concurrence est posée
Définir les packages
• Eventuellement, des états peuvent être sélectionnés
pour isoler un espace de noms de l'extérieur
• Cette fonctionnalité est essentielle pour développer
des automates sans risques de conflit de nommage
– ex : "confirmer" / "quitter"
Schémas d'états
• On peut aussi réutiliser un schéma d'états préexistant
(par exemple pour l'interaction de type
confirmation/annulation
• L'automate développé doit respecter le standard
syntaxique. Un moyen de le garantir est précisément
de réutiliser des schémas d'interaction.
• Le moyen le plus habituel de le faire est de réutiliser
des objets de niveau dialogue (par exemple la boite
de sélection de fichiers)
Compléter l'automate
• définir la hiérarchie
• définir les callbacks de transition
• décider des options de visibilité de panneaux
Raffiner l'automate
• panneaux auxiliaires (messages...)
• états auxiliaires, transitions associées, confirmations,
etc
• on continue à respecter la charte syntaxique
Décorer l'interface
• On améliore l'ergonomie :
• artéfacts visuels
• compléments d'information (messages, tooltips etc)
• l'interface peut informer l'utilisateur sur
– l'état courant
– l'objectif visé (imprimer, faxer, enregistrer, éditer, etc...
– les alternatives possibles
• Ces ajouts doivent suivre une charte sémantique, afin
de garantir l'aspect homogène de l'interface
Augmenter le nombre de transitions
• On ajoute des transitions qui fournissent des
raccourcis, afin d'améliorer la navigation
• boutons bascule
• fenêtres qui provoquent lors d'un click où passage au
premier plan (mapping) le changement d'état vers ce
à quoi elles servent
• On peut viser à couvrir le plus possible du graphe
d'états de façon à le rendre quasi complet
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
définir des standards
Niveau lexical
•
•
•
•
Style des polices de caractères
apparence des boutons ok, quit, cancel etc...
apparence des labels
dimensions et positions des différentes catégories de
panneaux
Niveau syntaxique
• Chaque objectif peut être atteint par différentes
séquences d'interaction
• Chaque séquence fait intervenir des éléments du
niveau lexical
• La réunion de ces séquences est le protocole de
l'interface
• Pour garantir l'utilisabilité de l'interface, les
séquences doivent être les mêmes pour des
opérations similaires
Exemple
• La séquence confirmation / annulation
– fenêtre de confirmation ?
– click supplémentaire sur le bouton après avoir changé le
label?
• sélection multi niveau
– cascade de popup menus ?
– affichage répété de listes intermédiaires?
– utilisation d'objet "tree"
Schémas Interaction Patterns
• Un produit utile de la charte syntaxique est la
définition de schémas d'automates pouvant êtyre
réutilisés
• C'est une manière simple d'améliorer les chances de
respects de cette charte
Le niveau sémantique
• L'utilisateur poursuit toujours un ou plusieurs
objectifs simultanés
• l'interface doit donner des signes visibles à tout
moment de ce qui est en cours
• Ces signes doivent être homogènes dans toutes les
situations
Le niveau sémantique
• On doit pour cela lister les fonctions essentielles, et
leur associer des artéfacts visuels, si possible
compatibles avec la concurrence, si les fonctions
peuvent être accédées simultanément
• L'interface devrait en permanence répondre
visuellement à la question "pourquoi suis-je là"
• C'est également un point requis par les exigences de
sécurité dans le cas des grandes interfaces
Séquences obligatoires / états
• On appelle séquence obligatoire une séquence
d'interaction minimale pour atteindre uin objectif
– par exemple pour imprimer
• Chaque étape correspond à au moins un état de
l'interface
• On peut se demander si ces états sont imbriqués ou
frères (ils sont bien sûr exclusifs)
Préférer l'héritage
• les états d'une séquence diffèrent généralement peu
de l'un à l'autre, si ce n'est pas quelques visibilités de
panneaux
• cela peut être une bonne idée de laisser les fenêtres
intermédiaires visibles, afin de permettre un retour
en arrière direct
Autres arguments
• L'utilisateur / le programmeur ont souvent
l'impression de s'enfoncer dans un processus en
suivant une séquence
• les états imbriqués en fournissent une bonne
métaphore
• les choix réalisés à chaque étape correspondent à
des sous états exclusifs
Modalité
• La modalité est positive quand l'utilisateur gagne des
possibilités suite à un changement d'état
• Elle est négative dans le cas inverse
• Les fenêtres modales (bloquantes) doivent être
évitées à tout prix car anti-ergonomiques
• Ces fenêtres implantent la forme la plus forte de
modalité négative, qui interdit généralement des
actions possibles sans danger
Cas possibles de fenêtres modales
• Dans les systèmes critiques (monitoring de centrale
nucléaire par ex) si l'application doit recevoir une
réponse à tout prix
• Dans une file de séquence quand les paramètres de
la phase précédent ne peuvent plus être changés
Modalité négative et états
state print {
panel parameters visible = true;
object print callback = setState(confirm);
panel confirmation visible = false;
}
state confirm : print {
panel confirmation
visible = true;
// positive modality
object close callback = setState(print);
object apply callback = doPrint();
panel parameters
visible = false; // negative modality
}
Langage de Spécification d'Interfaces
(Spécifications des IHM à l'aide
d'états)
Exemple :
Raffiner une interface avec des états
Objectif
• On reprend l'interface de l'éditeur simplifié vu
précédemment
• Il a pour fonctions l'édition bien sûr, et aussi la
sauvegarde, l'ouverture de fichier, l'impression et la
recherche dans le texte appelées:
<edit>, <load>, <save>, <find> et <print>.
• On illustre le processus décrit précédemment
Analyse du module fonctionnel
• <edit> est une fonction interne de l'interface, ainsi
que <find>. Les fonctions <save>, <load>, et
<print> sont des appels systèmes, perçu comme le
module fonctionnel
• Il n'y a pas de protocole pour <save>, <load>, et
<print>.
• Ces trois opérations peuvent être effectuées dans
n'importe quel ordre
– (on ignore le fait que la sauvegarde puisse dans un
programme intelligent rester impossible tant que rien n'a été
édité)
Identifier les fenêtres principales
• L'application a un panneau principal appelé
"mainPanel".
• On y trouve la barre de menu et la zone de travail
• <save> et <load> partagent une fenêtre de sélection
de fichier appelée "fileSelector".
• <print> prend ses paramètres grâce à un panneau
"printParameters"
• <find> requiert un panneau "findPanel".
Concevoir l'automate
• <edit> est concurrente avec le reste des fonction
• <find> n'a pas d'impact sur les données, et est
•
également indépendante des autres fonctions
on choisit aussi que <save> et <print> sont
concurrentes entre elles
– leurs fenêtres caractéristiques peuvent être simultanément
visibles
• <load> est exclusive de <save> et <print>.
– quand l'utilisateur initié un dialogue <load>, les dialogues
<print> et <save> disparaissent s'ils étaient visibles.
– <load> et <save> étant exclusifs, ces états peuvent
partager la même fenêtre de sélection de fichiers.
Développer la structure générale des états
state package editor {
// the default subregion is kept for standard
editor functionalities
panel mainPanel visible = true
object menuBar.File.Open callback =
setState(load);
object menuBar.File.Save callback =
setState(save);
object menuBar.File.Print callback =
setState(print);
object menuBar.Edit.Find callback =
setState(find);
panel fileSelector
visible = false
panel printParameters visible = false
panel findPanel visible = false
}
<Find>, <Load>
state package editor { ...
subregion find_subregion;
//////////////////////////////////
state find [ find_subregion ] {
panel findPanel visible = true
object close callback =
leaveState(find);
object search callback
= doSearch(find);
}
subregion lps_subregion;
//////////////////////////////////
state load : editor [ lps_subregion ] {
panel fileSelector visible = true
object close callback =
leaveState(load);
object ok callback =
loadFromFile();
saveOrPrint (abstraction)
state saveOrPrint:editor [ lps_subregion ]
{
// default subregion not used,
abstract state
state save [ save_subregion ]
{
panel fileSelector
visible = true
object close
callback = leaveState(save);
object ok
callback = saveToFile();
}
state print [ print_subregion ]
{
panel printParameters visible
= true
object close callback =
leaveState(print);
object ok callback =
print();
}
}
Commentaires
• L'exemple à ce stade illustre de quelle manière des
états abstraits peuvent être utilisés pour grouper des
informations communes à plusieurs sous états
• En particulier, cela sert à définir une fois pour toutes
de valeurs pas défaut, chaque sous état étant défini
par différence avec ses super états
• Il est d'usage que l'état racine soit qualifié de
"package" pour signifier qu'il constitue un espace de
noms
Raffiner la structure de l'automate
• On ajoute une fenêtre de confirmation pour
l'impression
• Cela requiert l'ajout d'une fenêtre, et d'un état qui
contrôle sa visibilité.
• C'est illustré comme suit :
Dialogue de confirmation
state print: saveOrPrint [ print_subregion ] {
panel printConfirm visible = false
panel printParameters visible = true
button close callback = leaveState(print);
button ok callback = setState(printConfirm);
state printConfirm {
panel printConfirm visible = true
object ok callback = print();
object cancel callback = leaveState(print);
}
}
Augmenter le nombre de Transitions
• L'exemple précédent introduit une fenêtre de
confirmation mais ne masque pas la fenêtre de
paramètres .
• Cela offre plusieurs avantages :
• 1- la fenêtre de paramètres reste ouverte de sorte
•
•
que l'on sache ce que l'on est en train de confirmer
2 - cela facilite le retour à cette fenêtre de
paramètres
3 - cela permet de donner au bouton "print" une
fonction de bascule, particulièrement ergonomique
Raffiner
• On adapte l'automate en modifiant le sous état
"printConfirm" de "print".
• Le bouton print, au lieu de lancer l'impression, passe
dans l'état "printConfirm".
• Dans l'état printConfirm, ce même bouton a pour
fonction de quitter l'état printConfirm (et donc dans
cet exemple simple de revenir à l'état print)
Ajout de Transitions
state print : saveOrPrint [ print_subregion ] {
panel printConfirm visible = false
panel printParameters visible =
true
object close cb = leaveState(print);
object ok cb = setState(printConfirm);
state printConfirm {
panel printConfirm visible = true
object ok cb = print();
object cancel cb = leaveState(print);
panel printParameters
object close cb = leaveState(printConfirm);
object ok cb = leaveState(printConfirm);
}
}
Conclusion
• ISL est un langage puissant pour la spécification
d'interfaces
• ISL étend les diagrammes d'états de UML
• ISL peut être rendu exécutable
Annexe, compléments de notation
Messages
Evenements déférrés
Extension (2)
Exemple
Exemple